| // 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. |
| |
| #include "cobalt/media/progressive/demuxer_extension_wrapper.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/task/post_task.h" |
| #include "base/task_runner_util.h" |
| #include "cobalt/extension/demuxer.h" |
| #include "starboard/system.h" |
| #include "third_party/chromium/media/base/audio_codecs.h" |
| #include "third_party/chromium/media/base/bind_to_current_loop.h" |
| #include "third_party/chromium/media/base/encryption_scheme.h" |
| #include "third_party/chromium/media/base/sample_format.h" |
| #include "third_party/chromium/media/base/starboard_utils.h" |
| #include "third_party/chromium/media/base/video_types.h" |
| #include "third_party/chromium/media/cobalt/ui/gfx/color_space.h" |
| #include "third_party/chromium/media/cobalt/ui/gfx/geometry/rect.h" |
| #include "third_party/chromium/media/cobalt/ui/gfx/geometry/size.h" |
| #include "third_party/chromium/media/filters/h264_to_annex_b_bitstream_converter.h" |
| #include "third_party/chromium/media/formats/mp4/box_definitions.h" |
| |
| namespace cobalt { |
| namespace media { |
| |
| using ::media::AudioCodec; |
| using ::media::AudioDecoderConfig; |
| using ::media::ChannelLayout; |
| using ::media::DecoderBuffer; |
| using ::media::DemuxerHost; |
| using ::media::DemuxerStream; |
| using ::media::EncryptionScheme; |
| using ::media::H264ToAnnexBBitstreamConverter; |
| using ::media::MediaTrack; |
| using ::media::PipelineStatus; |
| using ::media::PipelineStatusCallback; |
| using ::media::PipelineStatusCB; |
| using ::media::Ranges; |
| using ::media::SampleFormat; |
| using ::media::VideoCodec; |
| using ::media::VideoCodecProfile; |
| using ::media::VideoColorSpace; |
| using ::media::VideoDecoderConfig; |
| using ::media::VideoPixelFormat; |
| using ::media::VideoTransformation; |
| using ::media::mp4::AVCDecoderConfigurationRecord; |
| |
| // Used to convert a lambda to a pure C function. |
| // |user_data| is a callback of type T, which takes a U*. |
| template <typename T, typename U> |
| static void CallCB(U* u, void* user_data) { |
| (*static_cast<T*>(user_data))(u); |
| } |
| |
| // Converts AVCC h.264 frames to Annex B. This is necessary because the decoder |
| // expects packets in Annex B format. |
| class DemuxerExtensionWrapper::H264AnnexBConverter { |
| public: |
| // Creates an H264AnnexBConverter from the MP4 file's header data. |
| static std::unique_ptr<H264AnnexBConverter> Create(const uint8_t* extra_data, |
| size_t extra_data_size) { |
| if (!extra_data || extra_data_size == 0) { |
| LOG(ERROR) << "Invalid inputs to H264AnnexBConverter::Create."; |
| return nullptr; |
| } |
| AVCDecoderConfigurationRecord config; |
| std::unique_ptr<H264ToAnnexBBitstreamConverter> converter( |
| new H264ToAnnexBBitstreamConverter); |
| if (!converter->ParseConfiguration( |
| extra_data, static_cast<int>(extra_data_size), &config)) { |
| LOG(ERROR) << "Could not parse AVCC config."; |
| return nullptr; |
| } |
| return std::unique_ptr<H264AnnexBConverter>( |
| new H264AnnexBConverter(std::move(config), std::move(converter))); |
| } |
| |
| // Disallow copy and assign. |
| H264AnnexBConverter(const H264AnnexBConverter&) = delete; |
| H264AnnexBConverter& operator=(const H264AnnexBConverter&) = delete; |
| |
| ~H264AnnexBConverter() = default; |
| |
| // Attempts to convert the data in |data| from AVCC to AnnexB format, |
| // returning the data as a DecoderBuffer. Upon failure, the data will be |
| // returned unmodified in the DecoderBuffer. |
| scoped_refptr<DecoderBuffer> Convert(const uint8_t* data, size_t data_size) { |
| const auto* const config = config_.has_value() ? &*config_ : nullptr; |
| |
| std::vector<uint8_t> rewritten( |
| converter_->CalculateNeededOutputBufferSize(data, data_size, config)); |
| |
| uint32_t rewritten_size = rewritten.size(); |
| if (rewritten.empty() || |
| !converter_->ConvertNalUnitStreamToByteStream( |
| data, data_size, config, rewritten.data(), &rewritten_size)) { |
| // TODO(b/231994311): Add the buffer's side_data here, for HDR10+ support. |
| return DecoderBuffer::CopyFrom(data, data_size); |
| } else { |
| // The data was successfully rewritten. |
| |
| // The SPS and PPS NALUs -- generated from the config -- should only be |
| // sent with the first real NALU. |
| config_ = base::nullopt; |
| |
| // TODO(b/231994311): Add the buffer's side_data here, for HDR10+ support. |
| return DecoderBuffer::CopyFrom(rewritten.data(), rewritten.size()); |
| } |
| } |
| |
| private: |
| explicit H264AnnexBConverter( |
| AVCDecoderConfigurationRecord config, |
| std::unique_ptr<H264ToAnnexBBitstreamConverter> converter) |
| : config_(std::move(config)), converter_(std::move(converter)) {} |
| |
| // This config data is only sent with the first NALU (as SPS and PPS NALUs). |
| base::Optional<AVCDecoderConfigurationRecord> config_; |
| std::unique_ptr<H264ToAnnexBBitstreamConverter> converter_; |
| }; |
| |
| DemuxerExtensionStream::DemuxerExtensionStream( |
| CobaltExtensionDemuxer* demuxer, |
| scoped_refptr<base::SequencedTaskRunner> message_loop, |
| CobaltExtensionDemuxerVideoDecoderConfig config) |
| : demuxer_(demuxer), message_loop_(std::move(message_loop)) { |
| CHECK(demuxer_); |
| CHECK(message_loop_); |
| std::vector<uint8_t> extra_data; |
| if (config.extra_data_size > 0 && config.extra_data != nullptr) { |
| extra_data.assign(config.extra_data, |
| config.extra_data + config.extra_data_size); |
| } |
| |
| video_config_.emplace( |
| static_cast<VideoCodec>(config.codec), |
| static_cast<VideoCodecProfile>(config.profile), |
| static_cast<VideoDecoderConfig::AlphaMode>(config.alpha_mode), |
| VideoColorSpace( |
| config.color_space_primaries, config.color_space_transfer, |
| config.color_space_matrix, |
| static_cast<gfx::ColorSpace::RangeID>(config.color_space_range_id)), |
| VideoTransformation(), gfx::Size(config.coded_width, config.coded_height), |
| gfx::Rect(config.visible_rect_x, config.visible_rect_y, |
| config.visible_rect_width, config.visible_rect_height), |
| gfx::Size(config.natural_width, config.natural_height), extra_data, |
| static_cast<EncryptionScheme>(config.encryption_scheme)); |
| |
| LOG_IF(ERROR, !video_config_->IsValidConfig()) |
| << "Video config is not valid!"; |
| } |
| |
| DemuxerExtensionStream::DemuxerExtensionStream( |
| CobaltExtensionDemuxer* demuxer, |
| scoped_refptr<base::SequencedTaskRunner> message_loop, |
| CobaltExtensionDemuxerAudioDecoderConfig config) |
| : demuxer_(demuxer), message_loop_(std::move(message_loop)) { |
| CHECK(demuxer_); |
| CHECK(message_loop_); |
| std::vector<uint8_t> extra_data; |
| if (config.extra_data_size > 0 && config.extra_data != nullptr) { |
| extra_data.assign(config.extra_data, |
| config.extra_data + config.extra_data_size); |
| } |
| |
| audio_config_.emplace( |
| static_cast<AudioCodec>(config.codec), |
| static_cast<SampleFormat>(config.sample_format), |
| static_cast<ChannelLayout>(config.channel_layout), |
| config.samples_per_second, extra_data, |
| static_cast<EncryptionScheme>(config.encryption_scheme)); |
| |
| LOG_IF(ERROR, !audio_config_->IsValidConfig()) |
| << "Audio config is not valid!"; |
| } |
| |
| void DemuxerExtensionStream::Read(ReadCB read_cb) { |
| DCHECK(!read_cb.is_null()); |
| base::AutoLock auto_lock(lock_); |
| if (stopped_) { |
| LOG(INFO) << "Already stopped."; |
| std::move(read_cb).Run( |
| DemuxerStream::kOk, |
| scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer())); |
| return; |
| } |
| |
| // Buffers are only queued when there are no pending reads. |
| CHECK(buffer_queue_.empty() || read_queue_.empty()); |
| |
| if (buffer_queue_.empty()) { |
| read_queue_.push_back(std::move(read_cb)); |
| return; |
| } |
| |
| // We already have a buffer queued. Send the oldest buffer back. |
| scoped_refptr<DecoderBuffer> buffer = buffer_queue_.front(); |
| if (!buffer->end_of_stream()) { |
| // Do not pop EOS buffers, so that subsequent read requests also get EOS. |
| total_buffer_size_ -= buffer->data_size(); |
| buffer_queue_.pop_front(); |
| } |
| |
| std::move(read_cb).Run(DemuxerStream::kOk, buffer); |
| } |
| |
| AudioDecoderConfig DemuxerExtensionStream::audio_decoder_config() { |
| DCHECK(audio_config_.has_value()); |
| return *audio_config_; |
| } |
| |
| VideoDecoderConfig DemuxerExtensionStream::video_decoder_config() { |
| DCHECK(video_config_.has_value()); |
| return *video_config_; |
| } |
| |
| DemuxerStream::Type DemuxerExtensionStream::type() const { |
| const uint8_t is_audio = static_cast<int>(audio_config_.has_value()); |
| const uint8_t is_video = static_cast<int>(video_config_.has_value()); |
| DCHECK((is_audio ^ is_video) == 1); |
| return is_audio ? Type::AUDIO : Type::VIDEO; |
| } |
| |
| Ranges<base::TimeDelta> DemuxerExtensionStream::GetBufferedRanges() { |
| return buffered_ranges_; |
| } |
| |
| void DemuxerExtensionStream::EnqueueBuffer( |
| scoped_refptr<DecoderBuffer> buffer) { |
| base::AutoLock auto_lock(lock_); |
| |
| if (stopped_) { |
| // It is possible due to pipelining -- both downstream and within the |
| // demuxer -- that several pipelined reads will be enqueuing packets on a |
| // stopped stream. These will be dropped. |
| LOG(WARNING) << "attempted to enqueue packet on stopped stream"; |
| return; |
| } |
| |
| if (buffer->end_of_stream()) { |
| LOG(INFO) << "Received EOS"; |
| } else if (buffer->timestamp() != ::media::kNoTimestamp) { |
| if (last_buffer_timestamp_ != ::media::kNoTimestamp && |
| last_buffer_timestamp_ < buffer->timestamp()) { |
| buffered_ranges_.Add(last_buffer_timestamp_, buffer->timestamp()); |
| } |
| last_buffer_timestamp_ = buffer->timestamp(); |
| } else { |
| LOG(WARNING) << "Bad timestamp info on enqueued buffer."; |
| } |
| |
| if (read_queue_.empty()) { |
| buffer_queue_.push_back(buffer); |
| if (!buffer->end_of_stream()) { |
| total_buffer_size_ += buffer->data_size(); |
| } |
| return; |
| } |
| |
| // A pending read implies that the buffer queue was empty; otherwise it should |
| // never have been added to the read queue in the first place. |
| CHECK_EQ(buffer_queue_.size(), 0); |
| ReadCB read_cb(std::move(read_queue_.front())); |
| read_queue_.pop_front(); |
| std::move(read_cb).Run(DemuxerStream::kOk, std::move(buffer)); |
| } |
| |
| void DemuxerExtensionStream::FlushBuffers() { |
| base::AutoLock auto_lock(lock_); |
| buffer_queue_.clear(); |
| total_buffer_size_ = 0; |
| last_buffer_timestamp_ = ::media::kNoTimestamp; |
| } |
| |
| void DemuxerExtensionStream::Stop() { |
| DCHECK(message_loop_->RunsTasksInCurrentSequence()); |
| |
| base::AutoLock auto_lock(lock_); |
| buffer_queue_.clear(); |
| total_buffer_size_ = 0; |
| last_buffer_timestamp_ = ::media::kNoTimestamp; |
| // Fulfill any pending callbacks with EOS buffers set to end timestamp. |
| for (auto& read_cb : read_queue_) { |
| std::move(read_cb).Run( |
| DemuxerStream::kOk, |
| scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer())); |
| } |
| read_queue_.clear(); |
| stopped_ = true; |
| } |
| |
| base::TimeDelta DemuxerExtensionStream::GetLastBufferTimestamp() const { |
| base::AutoLock auto_lock(lock_); |
| return last_buffer_timestamp_; |
| } |
| |
| size_t DemuxerExtensionStream::GetTotalBufferSize() const { |
| base::AutoLock auto_lock(lock_); |
| return total_buffer_size_; |
| } |
| |
| PositionalDataSource::PositionalDataSource( |
| scoped_refptr<DataSourceReader> reader) |
| : reader_(std::move(reader)), position_(0) { |
| CHECK(reader_); |
| } |
| |
| PositionalDataSource::~PositionalDataSource() = default; |
| |
| void PositionalDataSource::Stop() { reader_->Stop(); } |
| |
| int PositionalDataSource::BlockingRead(uint8_t* data, int bytes_requested) { |
| const int bytes_read = |
| reader_->BlockingRead(position_, bytes_requested, data); |
| if (bytes_read != DataSourceReader::kReadError) { |
| position_ += bytes_read; |
| } |
| return bytes_read; |
| } |
| |
| void PositionalDataSource::SeekTo(int position) { position_ = position; } |
| |
| int64_t PositionalDataSource::GetPosition() const { return position_; } |
| |
| int64_t PositionalDataSource::GetSize() { return reader_->FileSize(); } |
| |
| // Functions for converting a PositionalDataSource to |
| // CobaltExtensionDemuxerDataSource. |
| static int CobaltExtensionDemuxerDataSource_BlockingReadRead( |
| uint8_t* data, int bytes_requested, void* user_data) { |
| return static_cast<PositionalDataSource*>(user_data)->BlockingRead( |
| data, bytes_requested); |
| } |
| |
| static void CobaltExtensionDemuxerDataSource_SeekTo(int position, |
| void* user_data) { |
| static_cast<PositionalDataSource*>(user_data)->SeekTo(position); |
| } |
| |
| static int64_t CobaltExtensionDemuxerDataSource_GetPosition(void* user_data) { |
| return static_cast<PositionalDataSource*>(user_data)->GetPosition(); |
| } |
| |
| static int64_t CobaltExtensionDemuxerDataSource_GetSize(void* user_data) { |
| return static_cast<PositionalDataSource*>(user_data)->GetSize(); |
| } |
| |
| std::unique_ptr<DemuxerExtensionWrapper> DemuxerExtensionWrapper::Create( |
| DataSource* data_source, |
| scoped_refptr<base::SequencedTaskRunner> message_loop, |
| const CobaltExtensionDemuxerApi* demuxer_api) { |
| if (demuxer_api == nullptr) { |
| // Attempt to use the Cobalt extension. |
| demuxer_api = static_cast<const CobaltExtensionDemuxerApi*>( |
| SbSystemGetExtension(kCobaltExtensionDemuxerApi)); |
| if (!demuxer_api || |
| strcmp(demuxer_api->name, kCobaltExtensionDemuxerApi) != 0) { |
| return nullptr; |
| } |
| } |
| |
| DCHECK(demuxer_api); |
| if (demuxer_api->version < 1) { |
| LOG(ERROR) << "Demuxer API version is too low: " << demuxer_api->version; |
| return nullptr; |
| } |
| |
| if (!data_source || !message_loop) { |
| LOG(ERROR) << "data_source and message_loop cannot be null."; |
| return nullptr; |
| } |
| |
| scoped_refptr<DataSourceReader> reader = new DataSourceReader; |
| reader->SetDataSource(data_source); |
| |
| std::unique_ptr<PositionalDataSource> positional_data_source( |
| new PositionalDataSource(std::move(reader))); |
| |
| std::unique_ptr<CobaltExtensionDemuxerDataSource> c_data_source( |
| new CobaltExtensionDemuxerDataSource{ |
| /*BlockingRead=*/&CobaltExtensionDemuxerDataSource_BlockingReadRead, |
| /*SeekTo=*/&CobaltExtensionDemuxerDataSource_SeekTo, |
| /*GetPosition=*/&CobaltExtensionDemuxerDataSource_GetPosition, |
| /*GetSize=*/&CobaltExtensionDemuxerDataSource_GetSize, |
| /*is_streaming=*/false, |
| /*user_data=*/positional_data_source.get()}); |
| |
| // TODO(b/231632632): Populate these vectors. |
| std::vector<CobaltExtensionDemuxerAudioCodec> supported_audio_codecs; |
| std::vector<CobaltExtensionDemuxerVideoCodec> supported_video_codecs; |
| |
| CobaltExtensionDemuxer* demuxer = demuxer_api->CreateDemuxer( |
| c_data_source.get(), supported_audio_codecs.data(), |
| supported_audio_codecs.size(), supported_video_codecs.data(), |
| supported_video_codecs.size()); |
| |
| if (!demuxer) { |
| LOG(ERROR) << "Failed to create a CobaltExtensionDemuxer."; |
| return nullptr; |
| } |
| |
| return std::unique_ptr<DemuxerExtensionWrapper>(new DemuxerExtensionWrapper( |
| demuxer_api, demuxer, std::move(positional_data_source), |
| std::move(c_data_source), std::move(message_loop))); |
| } |
| |
| DemuxerExtensionWrapper::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) |
| : demuxer_api_(demuxer_api), |
| impl_(demuxer), |
| data_source_(std::move(data_source)), |
| c_data_source_(std::move(c_data_source)), |
| blocking_thread_("DemuxerExtensionWrapperBlockingThread"), |
| message_loop_(std::move(message_loop)) { |
| CHECK(demuxer_api_); |
| CHECK(impl_); |
| CHECK(data_source_); |
| CHECK(c_data_source_); |
| CHECK(message_loop_); |
| } |
| |
| DemuxerExtensionWrapper::~DemuxerExtensionWrapper() { |
| if (impl_) { |
| demuxer_api_->DestroyDemuxer(impl_); |
| } |
| // Explicitly stop |blocking_thread_| to ensure that it stops before the |
| // destruction of any other members. |
| blocking_thread_.Stop(); |
| } |
| |
| std::vector<DemuxerStream*> DemuxerExtensionWrapper::GetAllStreams() { |
| std::vector<DemuxerStream*> streams; |
| if (audio_stream_.has_value()) { |
| streams.push_back(&*audio_stream_); |
| } |
| if (video_stream_.has_value()) { |
| streams.push_back(&*video_stream_); |
| } |
| return streams; |
| } |
| |
| std::string DemuxerExtensionWrapper::GetDisplayName() const { |
| return "DemuxerExtensionWrapper"; |
| } |
| void DemuxerExtensionWrapper::Initialize(DemuxerHost* host, |
| PipelineStatusCallback status_cb) { |
| DCHECK(message_loop_->RunsTasksInCurrentSequence()); |
| host_ = host; |
| |
| // Start the blocking thread and have it download and parse the media config. |
| if (!blocking_thread_.Start()) { |
| LOG(ERROR) << "Unable to start blocking thread"; |
| std::move(status_cb).Run(::media::DEMUXER_ERROR_COULD_NOT_PARSE); |
| return; |
| } |
| |
| // |status_cb| cannot be called until this function returns, so we post a task |
| // here. |
| base::PostTaskAndReplyWithResult( |
| blocking_thread_.message_loop()->task_runner().get(), FROM_HERE, |
| base::BindOnce(impl_->Initialize, impl_->user_data), |
| base::BindOnce(&DemuxerExtensionWrapper::OnInitializeDone, |
| base::Unretained(this), std::move(status_cb))); |
| } |
| |
| void DemuxerExtensionWrapper::OnInitializeDone( |
| PipelineStatusCallback status_cb, CobaltExtensionDemuxerStatus status) { |
| if (status == kCobaltExtensionDemuxerOk) { |
| // Set up the stream(s) on this end. |
| CobaltExtensionDemuxerAudioDecoderConfig audio_config = {}; |
| if (impl_->GetAudioConfig(&audio_config, impl_->user_data)) { |
| if (audio_config.encryption_scheme != |
| kCobaltExtensionDemuxerEncryptionSchemeUnencrypted) { |
| // TODO(b/232957482): Determine whether we need to handle this case. |
| LOG(ERROR) |
| << "Encrypted audio is not supported for progressive playback."; |
| std::move(status_cb).Run(::media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS); |
| return; |
| } |
| audio_stream_.emplace(impl_, message_loop_, std::move(audio_config)); |
| } |
| CobaltExtensionDemuxerVideoDecoderConfig video_config = {}; |
| if (impl_->GetVideoConfig(&video_config, impl_->user_data)) { |
| if (video_config.encryption_scheme != |
| kCobaltExtensionDemuxerEncryptionSchemeUnencrypted) { |
| // TODO(b/232957482): Determine whether we need to handle this case. |
| LOG(ERROR) |
| << "Encrypted video is not supported for progressive playback."; |
| std::move(status_cb).Run(::media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS); |
| return; |
| } |
| if (video_config.extra_data && video_config.extra_data_size > 0 && |
| video_config.codec == kCobaltExtensionDemuxerCodecH264) { |
| // This is probably an AVCC stream. We'll need to convert each packet |
| // from AVCC to AnnexB, so we create the converter based on the "extra |
| // data". This extra data will be passed in the form of SPS and PPS NALU |
| // packets in the AnnexB stream. |
| h264_converter_ = H264AnnexBConverter::Create( |
| video_config.extra_data, video_config.extra_data_size); |
| video_config.extra_data = nullptr; |
| video_config.extra_data_size = 0; |
| } |
| video_stream_.emplace(impl_, message_loop_, std::move(video_config)); |
| } |
| |
| if (!audio_stream_.has_value() && !video_stream_.has_value()) { |
| // Even though initialization seems to have succeeded, something is wrong |
| // if there are no streams. |
| LOG(ERROR) << "No streams are present"; |
| std::move(status_cb).Run(::media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS); |
| return; |
| } |
| |
| host_->SetDuration(base::TimeDelta::FromMicroseconds( |
| impl_->GetDuration(impl_->user_data))); |
| |
| // Begin downloading data. |
| Request(audio_stream_.has_value() ? DemuxerStream::AUDIO |
| : DemuxerStream::VIDEO); |
| } else { |
| LOG(ERROR) << "Initialization failed with status " << status; |
| } |
| std::move(status_cb).Run(static_cast<PipelineStatus>(status)); |
| } |
| |
| void DemuxerExtensionWrapper::AbortPendingReads() {} |
| |
| void DemuxerExtensionWrapper::StartWaitingForSeek(base::TimeDelta seek_time) {} |
| |
| void DemuxerExtensionWrapper::CancelPendingSeek(base::TimeDelta seek_time) {} |
| |
| void DemuxerExtensionWrapper::Seek(base::TimeDelta time, |
| PipelineStatusCallback status_cb) { |
| // It's safe to use base::Unretained here because blocking_thread_ will be |
| // stopped in this class's destructor. |
| blocking_thread_.message_loop()->task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&DemuxerExtensionWrapper::SeekTask, base::Unretained(this), |
| time, BindToCurrentLoop(std::move(status_cb)))); |
| } |
| |
| // TODO(b/232984963): Determine whether it's OK to have reads and seeks on the |
| // same thread. |
| void DemuxerExtensionWrapper::SeekTask(base::TimeDelta time, |
| PipelineStatusCallback status_cb) { |
| CHECK(blocking_thread_.message_loop() |
| ->task_runner() |
| ->RunsTasksInCurrentSequence()); |
| |
| // clear any enqueued buffers on demuxer streams |
| if (video_stream_.has_value()) video_stream_->FlushBuffers(); |
| if (audio_stream_.has_value()) audio_stream_->FlushBuffers(); |
| |
| const CobaltExtensionDemuxerStatus status = |
| impl_->Seek(time.InMicroseconds(), impl_->user_data); |
| |
| if (status != kCobaltExtensionDemuxerOk) { |
| LOG(ERROR) << "Seek failed with status " << status; |
| std::move(status_cb).Run(::media::PIPELINE_ERROR_READ); |
| return; |
| } |
| |
| // If all streams had finished downloading, we need to restart the request. |
| const bool issue_new_request = |
| (!video_stream_.has_value() || video_reached_eos_) && |
| (!audio_stream_.has_value() || audio_reached_eos_); |
| audio_reached_eos_ = false; |
| video_reached_eos_ = false; |
| flushing_ = true; |
| std::move(status_cb).Run(::media::PIPELINE_OK); |
| |
| if (issue_new_request) { |
| IssueNextRequest(); |
| } |
| } |
| |
| Ranges<base::TimeDelta> DemuxerExtensionWrapper::GetBufferedRanges() { |
| DCHECK(audio_stream_.has_value() || video_stream_.has_value()); |
| |
| if (!audio_stream_.has_value()) { |
| return video_stream_->GetBufferedRanges(); |
| } |
| if (!video_stream_.has_value()) { |
| return audio_stream_->GetBufferedRanges(); |
| } |
| return video_stream_->GetBufferedRanges().IntersectionWith( |
| audio_stream_->GetBufferedRanges()); |
| } |
| |
| void DemuxerExtensionWrapper::Stop() { |
| DCHECK(message_loop_->RunsTasksInCurrentSequence()); |
| { |
| base::AutoLock lock(lock_for_stopped_); |
| stopped_ = true; |
| } |
| data_source_->Stop(); |
| } |
| |
| base::TimeDelta DemuxerExtensionWrapper::GetStartTime() const { |
| return base::TimeDelta::FromMicroseconds( |
| impl_->GetStartTime(impl_->user_data)); |
| } |
| |
| base::Time DemuxerExtensionWrapper::GetTimelineOffset() const { |
| const SbTime reported_time = impl_->GetTimelineOffset(impl_->user_data); |
| return reported_time == 0 |
| ? base::Time() |
| : base::Time::FromDeltaSinceWindowsEpoch( |
| base::TimeDelta::FromMicroseconds(reported_time)); |
| } |
| |
| int64_t DemuxerExtensionWrapper::GetMemoryUsage() const { |
| NOTREACHED(); |
| return 0; |
| } |
| |
| void DemuxerExtensionWrapper::OnEnabledAudioTracksChanged( |
| const std::vector<MediaTrack::Id>& track_ids, base::TimeDelta curr_time, |
| TrackChangeCB change_completed_cb) { |
| NOTREACHED(); |
| } |
| |
| void DemuxerExtensionWrapper::OnSelectedVideoTrackChanged( |
| const std::vector<MediaTrack::Id>& track_ids, base::TimeDelta curr_time, |
| TrackChangeCB change_completed_cb) { |
| NOTREACHED(); |
| } |
| |
| void DemuxerExtensionWrapper::Request(DemuxerStream::Type type) { |
| static const auto kRequestDelay = base::TimeDelta::FromMilliseconds(100); |
| |
| if (type == DemuxerStream::AUDIO) { |
| DCHECK(audio_stream_.has_value()); |
| } else { |
| DCHECK(video_stream_.has_value()); |
| } |
| |
| if (!blocking_thread_.task_runner()->BelongsToCurrentThread()) { |
| blocking_thread_.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&DemuxerExtensionWrapper::Request, |
| base::Unretained(this), type)); |
| return; |
| } |
| |
| if (HasStopped()) { |
| return; |
| } |
| |
| const size_t total_buffer_size = |
| (audio_stream_.has_value() ? audio_stream_->GetTotalBufferSize() : 0) + |
| (video_stream_.has_value() ? video_stream_->GetTotalBufferSize() : 0); |
| |
| int progressive_budget = 0; |
| if (video_stream_.has_value()) { |
| const VideoDecoderConfig video_config = |
| video_stream_->video_decoder_config(); |
| // Only sdr video is supported in progressive mode. |
| // TODO(b/231994311): Figure out how to set this value properly. |
| constexpr int kBitDepth = 8; |
| progressive_budget = SbMediaGetProgressiveBufferBudget( |
| MediaVideoCodecToSbMediaVideoCodec(video_config.codec()), |
| video_config.visible_rect().size().width(), |
| video_config.visible_rect().size().height(), kBitDepth); |
| } else { |
| progressive_budget = SbMediaGetAudioBufferBudget(); |
| } |
| |
| if (total_buffer_size >= progressive_budget) { |
| // Retry after a delay. |
| blocking_thread_.message_loop()->task_runner()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&DemuxerExtensionWrapper::Request, base::Unretained(this), |
| type), |
| kRequestDelay); |
| return; |
| } |
| |
| scoped_refptr<DecoderBuffer> decoder_buffer; |
| bool called_cb = false; |
| auto read_cb = [this, type, &decoder_buffer, |
| &called_cb](CobaltExtensionDemuxerBuffer* buffer) { |
| called_cb = true; |
| if (!buffer) { |
| return; |
| } |
| |
| if (buffer->end_of_stream) { |
| decoder_buffer = DecoderBuffer::CreateEOSBuffer(); |
| return; |
| } |
| |
| if (h264_converter_ && type == DemuxerExtensionStream::VIDEO) { |
| // This converts from AVCC to AnnexB format for h.264 video. |
| decoder_buffer = |
| h264_converter_->Convert(buffer->data, buffer->data_size); |
| } else { |
| // TODO(b/231994311): Add the buffer's side_data here, for HDR10+ support. |
| decoder_buffer = DecoderBuffer::CopyFrom(buffer->data, buffer->data_size); |
| } |
| |
| decoder_buffer->set_timestamp( |
| base::TimeDelta::FromMicroseconds(buffer->pts)); |
| decoder_buffer->set_duration( |
| base::TimeDelta::FromMicroseconds(buffer->duration)); |
| decoder_buffer->set_is_key_frame(buffer->is_keyframe); |
| }; |
| impl_->Read(static_cast<CobaltExtensionDemuxerStreamType>(type), |
| &CallCB<decltype(read_cb), CobaltExtensionDemuxerBuffer>, |
| &read_cb, impl_->user_data); |
| |
| if (!called_cb) { |
| LOG(ERROR) |
| << "Demuxer extension implementation did not call the read callback."; |
| host_->OnDemuxerError(::media::PIPELINE_ERROR_READ); |
| return; |
| } |
| if (!decoder_buffer) { |
| LOG(ERROR) << "Received a null buffer from the demuxer."; |
| host_->OnDemuxerError(::media::PIPELINE_ERROR_READ); |
| return; |
| } |
| |
| auto& stream = |
| (type == DemuxerStream::AUDIO) ? *audio_stream_ : *video_stream_; |
| bool& eos_status = |
| (type == DemuxerStream::AUDIO) ? audio_reached_eos_ : video_reached_eos_; |
| |
| eos_status = decoder_buffer->end_of_stream(); |
| stream.EnqueueBuffer(std::move(decoder_buffer)); |
| if (!eos_status) { |
| host_->OnBufferedTimeRangesChanged(GetBufferedRanges()); |
| } |
| |
| // If we reach this point, enqueueing the buffer was successful. |
| IssueNextRequest(); |
| return; |
| } |
| |
| void DemuxerExtensionWrapper::IssueNextRequest() { |
| { |
| base::AutoLock lock(lock_for_stopped_); |
| if (stopped_) { |
| LOG(INFO) << "Already stopped; request loop is stopping."; |
| return; |
| } |
| } |
| |
| DemuxerStream::Type type = DemuxerStream::UNKNOWN; |
| if (audio_reached_eos_ || video_reached_eos_) { |
| // If we have eos in one or both buffers, the decision is easy. |
| if ((audio_reached_eos_ && video_reached_eos_) || |
| (audio_reached_eos_ && !video_stream_.has_value()) || |
| (video_reached_eos_ && !audio_stream_.has_value())) { |
| LOG(INFO) << "All streams at EOS, request loop is stopping."; |
| return; |
| } |
| // Only one of two streams is at eos; download data for the stream NOT at |
| // eos. |
| type = audio_reached_eos_ ? DemuxerStream::VIDEO : DemuxerStream::AUDIO; |
| } else if (!audio_stream_.has_value() || !video_stream_.has_value()) { |
| // If only one stream is present and not at eos, just download that data. |
| type = |
| audio_stream_.has_value() ? DemuxerStream::AUDIO : DemuxerStream::VIDEO; |
| } else { |
| // Both streams are present, and neither is at eos. Priority order for |
| // figuring out what to download next. |
| const base::TimeDelta audio_stamp = audio_stream_->GetLastBufferTimestamp(); |
| const base::TimeDelta video_stamp = video_stream_->GetLastBufferTimestamp(); |
| // If the audio demuxer stream is empty, always fill it first. |
| if (audio_stamp == ::media::kNoTimestamp) { |
| type = DemuxerStream::AUDIO; |
| } else if (video_stamp == ::media::kNoTimestamp) { |
| // The video demuxer stream is empty; we need data for it. |
| type = DemuxerStream::VIDEO; |
| } else if (video_stamp < audio_stamp) { |
| // Video is earlier; fill it first. |
| type = DemuxerStream::VIDEO; |
| } else { |
| type = DemuxerStream::AUDIO; |
| } |
| } |
| |
| DCHECK_NE(type, DemuxerStream::UNKNOWN); |
| // We cannot call Request() directly even if this function is also run on |
| // |blocking_thread_| as otherwise it is possible that this function is |
| // running in a tight loop and seek/stop requests would have no chance to kick |
| // in. |
| blocking_thread_.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&DemuxerExtensionWrapper::Request, |
| base::Unretained(this), type)); |
| } |
| |
| bool DemuxerExtensionWrapper::HasStopped() { |
| base::AutoLock lock(lock_for_stopped_); |
| return stopped_; |
| } |
| |
| namespace { |
| |
| // Ensure that the demuxer extension's enums match up with the internal enums. |
| // This doesn't affect any code, but prevents compilation if there's a mismatch |
| // somewhere. |
| #define DEMUXER_EXTENSION_ENUM_EQ(a, b) \ |
| COMPILE_ASSERT(static_cast<int>(a) == static_cast<int>(b), mismatching_enums) |
| |
| // Pipeline status. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerOk, ::media::PIPELINE_OK); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerErrorNetwork, |
| ::media::PIPELINE_ERROR_NETWORK); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerErrorAbort, |
| ::media::PIPELINE_ERROR_ABORT); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerErrorInitializationFailed, |
| ::media::PIPELINE_ERROR_INITIALIZATION_FAILED); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerErrorRead, |
| ::media::PIPELINE_ERROR_READ); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerErrorInvalidState, |
| ::media::PIPELINE_ERROR_INVALID_STATE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerErrorCouldNotOpen, |
| ::media::DEMUXER_ERROR_COULD_NOT_OPEN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerErrorCouldNotParse, |
| ::media::DEMUXER_ERROR_COULD_NOT_PARSE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerErrorNoSupportedStreams, |
| ::media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS); |
| |
| // Audio codecs. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecUnknownAudio, |
| ::media::AudioCodec::kUnknown); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecAAC, |
| ::media::AudioCodec::kAAC); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecMP3, |
| ::media::AudioCodec::kMP3); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecPCM, |
| ::media::AudioCodec::kPCM); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecVorbis, |
| ::media::AudioCodec::kVorbis); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecFLAC, |
| ::media::AudioCodec::kFLAC); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecAMR_NB, |
| ::media::AudioCodec::kAMR_NB); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecAMR_WB, |
| ::media::AudioCodec::kAMR_WB); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecPCM_MULAW, |
| ::media::AudioCodec::kPCM_MULAW); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecGSM_MS, |
| ::media::AudioCodec::kGSM_MS); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecPCM_S16BE, |
| ::media::AudioCodec::kPCM_S16BE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecPCM_S24BE, |
| ::media::AudioCodec::kPCM_S24BE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecOpus, |
| ::media::AudioCodec::kOpus); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecEAC3, |
| ::media::AudioCodec::kEAC3); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecPCM_ALAW, |
| ::media::AudioCodec::kPCM_ALAW); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecALAC, |
| ::media::AudioCodec::kALAC); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecAC3, |
| ::media::AudioCodec::kAC3); |
| |
| |
| // Video codecs. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecUnknownVideo, |
| ::media::VideoCodec::kUnknown); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecH264, |
| ::media::VideoCodec::kH264); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecVC1, |
| ::media::VideoCodec::kVC1); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecMPEG2, |
| ::media::VideoCodec::kMPEG2); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecMPEG4, |
| ::media::VideoCodec::kMPEG4); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecTheora, |
| ::media::VideoCodec::kTheora); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecVP8, |
| ::media::VideoCodec::kVP8); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecVP9, |
| ::media::VideoCodec::kVP9); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecHEVC, |
| ::media::VideoCodec::kHEVC); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecDolbyVision, |
| ::media::VideoCodec::kDolbyVision); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerCodecAV1, |
| ::media::VideoCodec::kAV1); |
| |
| // Sample formats. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerSampleFormatUnknown, |
| ::media::kUnknownSampleFormat); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerSampleFormatU8, |
| ::media::kSampleFormatU8); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerSampleFormatS16, |
| ::media::kSampleFormatS16); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerSampleFormatS32, |
| ::media::kSampleFormatS32); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerSampleFormatF32, |
| ::media::kSampleFormatF32); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerSampleFormatPlanarS16, |
| ::media::kSampleFormatPlanarS16); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerSampleFormatPlanarF32, |
| ::media::kSampleFormatPlanarF32); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerSampleFormatPlanarS32, |
| ::media::kSampleFormatPlanarS32); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerSampleFormatS24, |
| ::media::kSampleFormatS24); |
| |
| |
| // Channel layouts. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutNone, |
| ::media::CHANNEL_LAYOUT_NONE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutUnsupported, |
| ::media::CHANNEL_LAYOUT_UNSUPPORTED); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutMono, |
| ::media::CHANNEL_LAYOUT_MONO); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutStereo, |
| ::media::CHANNEL_LAYOUT_STEREO); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout2_1, |
| ::media::CHANNEL_LAYOUT_2_1); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutSurround, |
| ::media::CHANNEL_LAYOUT_SURROUND); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout4_0, |
| ::media::CHANNEL_LAYOUT_4_0); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout2_2, |
| ::media::CHANNEL_LAYOUT_2_2); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutQuad, |
| ::media::CHANNEL_LAYOUT_QUAD); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout5_0, |
| ::media::CHANNEL_LAYOUT_5_0); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout5_1, |
| ::media::CHANNEL_LAYOUT_5_1); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout5_0Back, |
| ::media::CHANNEL_LAYOUT_5_0_BACK); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout5_1Back, |
| ::media::CHANNEL_LAYOUT_5_1_BACK); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout7_0, |
| ::media::CHANNEL_LAYOUT_7_0); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout7_1, |
| ::media::CHANNEL_LAYOUT_7_1); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout7_1Wide, |
| ::media::CHANNEL_LAYOUT_7_1_WIDE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutStereoDownmix, |
| ::media::CHANNEL_LAYOUT_STEREO_DOWNMIX); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout2point1, |
| ::media::CHANNEL_LAYOUT_2POINT1); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout3_1, |
| ::media::CHANNEL_LAYOUT_3_1); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout4_1, |
| ::media::CHANNEL_LAYOUT_4_1); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout6_0, |
| ::media::CHANNEL_LAYOUT_6_0); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout6_0Front, |
| ::media::CHANNEL_LAYOUT_6_0_FRONT); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutHexagonal, |
| ::media::CHANNEL_LAYOUT_HEXAGONAL); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout6_1, |
| ::media::CHANNEL_LAYOUT_6_1); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout6_1Back, |
| ::media::CHANNEL_LAYOUT_6_1_BACK); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout6_1Front, |
| ::media::CHANNEL_LAYOUT_6_1_FRONT); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout7_0Front, |
| ::media::CHANNEL_LAYOUT_7_0_FRONT); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout7_1WideBack, |
| ::media::CHANNEL_LAYOUT_7_1_WIDE_BACK); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutOctagonal, |
| ::media::CHANNEL_LAYOUT_OCTAGONAL); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutDiscrete, |
| ::media::CHANNEL_LAYOUT_DISCRETE); |
| DEMUXER_EXTENSION_ENUM_EQ( |
| kCobaltExtensionDemuxerChannelLayoutStereoAndKeyboardMic, |
| ::media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayout4_1QuadSide, |
| ::media::CHANNEL_LAYOUT_4_1_QUAD_SIDE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerChannelLayoutBitstream, |
| ::media::CHANNEL_LAYOUT_BITSTREAM); |
| |
| // Video codec profiles. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVideoCodecProfileUnknown, |
| ::media::VIDEO_CODEC_PROFILE_UNKNOWN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileMin, |
| ::media::H264PROFILE_MIN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileBaseline, |
| ::media::H264PROFILE_BASELINE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileMain, |
| ::media::H264PROFILE_MAIN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileExtended, |
| ::media::H264PROFILE_EXTENDED); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileHigh, |
| ::media::H264PROFILE_HIGH); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileHigh10Profile, |
| ::media::H264PROFILE_HIGH10PROFILE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileHigh422Profile, |
| ::media::H264PROFILE_HIGH422PROFILE); |
| DEMUXER_EXTENSION_ENUM_EQ( |
| kCobaltExtensionDemuxerH264ProfileHigh444PredictiveProfile, |
| ::media::H264PROFILE_HIGH444PREDICTIVEPROFILE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileScalableBaseline, |
| ::media::H264PROFILE_SCALABLEBASELINE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileScalableHigh, |
| ::media::H264PROFILE_SCALABLEHIGH); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileStereoHigh, |
| ::media::H264PROFILE_STEREOHIGH); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileMultiviewHigh, |
| ::media::H264PROFILE_MULTIVIEWHIGH); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerH264ProfileMax, |
| ::media::H264PROFILE_MAX); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVp8ProfileMin, |
| ::media::VP8PROFILE_MIN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVp8ProfileAny, |
| ::media::VP8PROFILE_ANY); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVp8ProfileMax, |
| ::media::VP8PROFILE_MAX); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVp9ProfileMin, |
| ::media::VP9PROFILE_MIN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVp9ProfileProfile0, |
| ::media::VP9PROFILE_PROFILE0); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVp9ProfileProfile1, |
| ::media::VP9PROFILE_PROFILE1); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVp9ProfileProfile2, |
| ::media::VP9PROFILE_PROFILE2); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVp9ProfileProfile3, |
| ::media::VP9PROFILE_PROFILE3); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerVp9ProfileMax, |
| ::media::VP9PROFILE_MAX); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerHevcProfileMin, |
| ::media::HEVCPROFILE_MIN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerHevcProfileMain, |
| ::media::HEVCPROFILE_MAIN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerHevcProfileMain10, |
| ::media::HEVCPROFILE_MAIN10); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerHevcProfileMainStillPicture, |
| ::media::HEVCPROFILE_MAIN_STILL_PICTURE); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerHevcProfileMax, |
| ::media::HEVCPROFILE_MAX); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerDolbyVisionProfile0, |
| ::media::DOLBYVISION_PROFILE0); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerDolbyVisionProfile4, |
| ::media::DOLBYVISION_PROFILE4); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerDolbyVisionProfile5, |
| ::media::DOLBYVISION_PROFILE5); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerDolbyVisionProfile7, |
| ::media::DOLBYVISION_PROFILE7); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerTheoraProfileMin, |
| ::media::THEORAPROFILE_MIN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerTheoraProfileAny, |
| ::media::THEORAPROFILE_ANY); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerTheoraProfileMax, |
| ::media::THEORAPROFILE_MAX); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerAv1ProfileMin, |
| ::media::AV1PROFILE_MIN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerAv1ProfileProfileMain, |
| ::media::AV1PROFILE_PROFILE_MAIN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerAv1ProfileProfileHigh, |
| ::media::AV1PROFILE_PROFILE_HIGH); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerAv1ProfileProfilePro, |
| ::media::AV1PROFILE_PROFILE_PRO); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerAv1ProfileMax, |
| ::media::AV1PROFILE_MAX); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerDolbyVisionProfile8, |
| ::media::DOLBYVISION_PROFILE8); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerDolbyVisionProfile9, |
| ::media::DOLBYVISION_PROFILE9); |
| |
| // Color range IDs. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerColorSpaceRangeIdInvalid, |
| gfx::ColorSpace::RangeID::INVALID); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerColorSpaceRangeIdLimited, |
| gfx::ColorSpace::RangeID::LIMITED); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerColorSpaceRangeIdFull, |
| gfx::ColorSpace::RangeID::FULL); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerColorSpaceRangeIdDerived, |
| gfx::ColorSpace::RangeID::DERIVED); |
| |
| // Alpha modes. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerHasAlpha, |
| ::media::VideoDecoderConfig::AlphaMode::kHasAlpha); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerIsOpaque, |
| ::media::VideoDecoderConfig::AlphaMode::kIsOpaque); |
| |
| // Demuxer stream types. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerStreamTypeUnknown, |
| ::media::DemuxerStream::Type::UNKNOWN); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerStreamTypeAudio, |
| ::media::DemuxerStream::Type::AUDIO); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerStreamTypeVideo, |
| ::media::DemuxerStream::Type::VIDEO); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerStreamTypeText, |
| ::media::DemuxerStream::Type::TEXT); |
| |
| // Encryption schemes. |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerEncryptionSchemeUnencrypted, |
| ::media::EncryptionScheme::kUnencrypted); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerEncryptionSchemeCenc, |
| ::media::EncryptionScheme::kCenc); |
| DEMUXER_EXTENSION_ENUM_EQ(kCobaltExtensionDemuxerEncryptionSchemeCbcs, |
| ::media::EncryptionScheme::kCbcs); |
| |
| #undef DEMUXER_EXTENSION_ENUM_EQ |
| |
| } // namespace |
| |
| } // namespace media |
| } // namespace cobalt |