blob: c3f869a340809e5ae05c668d2e4f9ac0950ac031 [file] [log] [blame]
// Copyright 2022 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Contains classes that wrap the Demuxer Cobalt Extension, providing an
// implementation of a Cobalt demuxer. The main API is DemuxerExtensionWrapper.
#ifndef COBALT_MEDIA_PROGRESSIVE_DEMUXER_EXTENSION_WRAPPER_H_
#define COBALT_MEDIA_PROGRESSIVE_DEMUXER_EXTENSION_WRAPPER_H_
#include <deque>
#include <memory>
#include <string>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/threading/thread.h"
#include "cobalt/media/progressive/data_source_reader.h"
#include "starboard/extension/demuxer.h"
#include "third_party/chromium/media/base/audio_decoder_config.h"
#include "third_party/chromium/media/base/decoder_buffer.h"
#include "third_party/chromium/media/base/demuxer.h"
#include "third_party/chromium/media/base/pipeline_status.h"
#include "third_party/chromium/media/base/ranges.h"
#include "third_party/chromium/media/base/video_decoder_config.h"
namespace cobalt {
namespace media {
// Represents an audio or video stream. Reads data via the demuxer Cobalt
// Extension.
class DemuxerExtensionStream : public ::media::DemuxerStream {
public:
// Represents a video stream.
explicit DemuxerExtensionStream(
CobaltExtensionDemuxer* demuxer,
scoped_refptr<base::SequencedTaskRunner> message_loop,
CobaltExtensionDemuxerVideoDecoderConfig config);
// Represents an audio stream.
explicit DemuxerExtensionStream(
CobaltExtensionDemuxer* demuxer,
scoped_refptr<base::SequencedTaskRunner> message_loop,
CobaltExtensionDemuxerAudioDecoderConfig config);
// Disallow copy and assign.
DemuxerExtensionStream(const DemuxerExtensionStream&) = delete;
DemuxerExtensionStream& operator=(const DemuxerExtensionStream&) = delete;
~DemuxerExtensionStream() = default;
// Functions used by DemuxerExtensionWrapper.
::media::Ranges<base::TimeDelta> GetBufferedRanges();
void EnqueueBuffer(scoped_refptr<::media::DecoderBuffer> buffer);
void FlushBuffers();
void Stop();
base::TimeDelta GetLastBufferTimestamp() const;
size_t GetTotalBufferSize() const;
// DemuxerStream implementation:
void Read(int max_number_of_buffers_to_read, ReadCB read_cb) override;
::media::AudioDecoderConfig audio_decoder_config() override;
::media::VideoDecoderConfig video_decoder_config() override;
Type type() const override;
void EnableBitstreamConverter() override { NOTIMPLEMENTED(); }
bool SupportsConfigChanges() override { return false; }
private:
typedef std::deque<scoped_refptr<::media::DecoderBuffer>> BufferQueue;
typedef std::deque<ReadCB> ReadQueue;
CobaltExtensionDemuxer* demuxer_ = nullptr; // Not owned.
base::Optional<::media::VideoDecoderConfig> video_config_;
base::Optional<::media::AudioDecoderConfig> audio_config_;
// Protects everything below.
mutable base::Lock lock_;
// Keeps track of all time ranges this object has seen since creation.
// The demuxer uses these ranges to update the pipeline about what data
// it has demuxed.
::media::Ranges<base::TimeDelta> buffered_ranges_;
// The last timestamp of buffer enqueued. This is used in two places:
// 1. Used with the timestamp of the current frame to calculate the
// buffer range.
// 2. Used by the demuxer to deteminate what type of frame to get next.
base::TimeDelta last_buffer_timestamp_ = ::media::kNoTimestamp;
bool stopped_ = false;
BufferQueue buffer_queue_;
ReadQueue read_queue_;
scoped_refptr<base::SequencedTaskRunner> message_loop_;
size_t total_buffer_size_ = 0;
};
// Wraps a DataSourceReader in an even simpler API, where each read increments
// the read location. This better matches the C data source API.
class PositionalDataSource {
public:
explicit PositionalDataSource(scoped_refptr<DataSourceReader> reader);
// Disallow copy and assign.
PositionalDataSource(const PositionalDataSource&) = delete;
PositionalDataSource& operator=(const PositionalDataSource&) = delete;
~PositionalDataSource();
void Stop();
// Reads up to |bytes_requested|, writing the data into |data|.
int BlockingRead(uint8_t* data, int bytes_requested);
// Seeks to |position|.
void SeekTo(int position);
// Returns the current read position.
int64_t GetPosition() const;
// Returns the size of the file.
//
// TODO(b/231744342): investigate whether we need to fix
// DataSourceReader::FileSize(). In testing, it sometimes returned inaccurate
// results before a file was fully downloaded. That behavior affects what this
// function returns.
int64_t GetSize();
private:
scoped_refptr<DataSourceReader> reader_;
int64_t position_ = 0;
};
// Wraps the demuxer Cobalt Extension in the internal media::Demuxer API.
// Instances should be created via the Create method.
class DemuxerExtensionWrapper : public ::media::Demuxer {
public:
// Constructs a new DemuxerExtensionWrapper, returning null on failure. If
// |data_source| or |message_loop| is null, or if a demuxer cannot be created,
// this will return null. If |demuxer_api| is null, we will attempt to use the
// corresponding Cobalt extension.
static std::unique_ptr<DemuxerExtensionWrapper> Create(
DataSource* data_source,
scoped_refptr<base::SequencedTaskRunner> message_loop,
const CobaltExtensionDemuxerApi* demuxer_api = nullptr);
// Disallow copy and assign.
DemuxerExtensionWrapper(const DemuxerExtensionWrapper&) = delete;
DemuxerExtensionWrapper& operator=(const DemuxerExtensionWrapper&) = delete;
~DemuxerExtensionWrapper() override;
// Demuxer implementation:
std::vector<::media::DemuxerStream*> GetAllStreams() override;
std::string GetDisplayName() const override;
void Initialize(::media::DemuxerHost* host,
::media::PipelineStatusCallback status_cb) override;
void AbortPendingReads() override;
void StartWaitingForSeek(base::TimeDelta seek_time) override;
void CancelPendingSeek(base::TimeDelta seek_time) override;
void Seek(base::TimeDelta time,
::media::PipelineStatusCallback status_cb) override;
void Stop() override;
base::TimeDelta GetStartTime() const override;
base::Time GetTimelineOffset() const override;
int64_t GetMemoryUsage() const override;
void OnEnabledAudioTracksChanged(
const std::vector<::media::MediaTrack::Id>& track_ids,
base::TimeDelta curr_time, TrackChangeCB change_completed_cb) override;
void OnSelectedVideoTrackChanged(
const std::vector<::media::MediaTrack::Id>& track_ids,
base::TimeDelta curr_time, TrackChangeCB change_completed_cb) override;
absl::optional<::media::container_names::MediaContainerName>
GetContainerForMetrics() const override {
NOTREACHED();
return absl::nullopt;
}
private:
// Only a forward declaration here, since the specifics of this class are an
// implementation detail.
class H264AnnexBConverter;
// Arguments must not be null.
explicit DemuxerExtensionWrapper(
const CobaltExtensionDemuxerApi* demuxer_api,
CobaltExtensionDemuxer* demuxer,
std::unique_ptr<PositionalDataSource> data_source,
std::unique_ptr<CobaltExtensionDemuxerDataSource> c_data_source,
scoped_refptr<base::SequencedTaskRunner> message_loop);
void OnInitializeDone(::media::PipelineStatusCallback status_cb,
CobaltExtensionDemuxerStatus status);
void Request(::media::DemuxerStream::Type type);
bool HasStopped();
void IssueNextRequest();
void SeekTask(base::TimeDelta time,
::media::PipelineStatusCallback status_cb);
// Returns the range of buffered data. If both audio and video streams are
// present, this is the intersection of their buffered ranges; otherwise, it
// is whatever range of data is buffered.
::media::Ranges<base::TimeDelta> GetBufferedRanges();
const CobaltExtensionDemuxerApi* demuxer_api_ = nullptr; // Not owned.
// Owned by this class. Construction/destruction is done via demuxer_api_.
CobaltExtensionDemuxer* impl_ = nullptr;
std::unique_ptr<PositionalDataSource> data_source_;
std::unique_ptr<CobaltExtensionDemuxerDataSource> c_data_source_;
::media::DemuxerHost* host_ = nullptr;
mutable base::Lock lock_for_stopped_;
// Indicates whether Stop has been called.
bool stopped_ = false;
bool video_reached_eos_ = false;
bool audio_reached_eos_ = false;
bool flushing_ = false;
base::Optional<DemuxerExtensionStream> video_stream_;
base::Optional<DemuxerExtensionStream> audio_stream_;
std::unique_ptr<H264AnnexBConverter> h264_converter_;
// Thread for blocking I/O operations.
base::Thread blocking_thread_;
scoped_refptr<base::SequencedTaskRunner> message_loop_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<DemuxerExtensionWrapper> weak_factory_{this};
};
} // namespace media
} // namespace cobalt
#endif // COBALT_MEDIA_PROGRESSIVE_DEMUXER_EXTENSION_WRAPPER_H_