diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-05-20 09:47:09 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-06-07 11:15:42 +0000 |
commit | 189d4fd8fad9e3c776873be51938cd31a42b6177 (patch) | |
tree | 6497caeff5e383937996768766ab3bb2081a40b2 /chromium/media/filters | |
parent | 8bc75099d364490b22f43a7ce366b366c08f4164 (diff) | |
download | qtwebengine-chromium-189d4fd8fad9e3c776873be51938cd31a42b6177.tar.gz |
BASELINE: Update Chromium to 90.0.4430.221
Change-Id: Iff4d9d18d2fcf1a576f3b1f453010f744a232920
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/media/filters')
64 files changed, 883 insertions, 206 deletions
diff --git a/chromium/media/filters/BUILD.gn b/chromium/media/filters/BUILD.gn index 573ef0ee042..3cc37a4379f 100644 --- a/chromium/media/filters/BUILD.gn +++ b/chromium/media/filters/BUILD.gn @@ -4,7 +4,6 @@ import("//media/gpu/args.gni") import("//media/media_options.gni") -import("//third_party/libaom/options.gni") source_set("filters") { # Do not expand the visibility here without double-checking with OWNERS, this @@ -300,6 +299,7 @@ source_set("unit_tests") { "video_renderer_algorithm_unittest.cc", "vp9_parser_unittest.cc", "vp9_raw_bits_reader_unittest.cc", + "vp9_uncompressed_header_parser_unittest.cc", ] deps = [ diff --git a/chromium/media/filters/android/media_codec_audio_decoder.cc b/chromium/media/filters/android/media_codec_audio_decoder.cc index 587b78c1a30..5119af0a8bd 100644 --- a/chromium/media/filters/android/media_codec_audio_decoder.cc +++ b/chromium/media/filters/android/media_codec_audio_decoder.cc @@ -52,6 +52,10 @@ std::string MediaCodecAudioDecoder::GetDisplayName() const { return "MediaCodecAudioDecoder"; } +AudioDecoderType MediaCodecAudioDecoder::GetDecoderType() const { + return AudioDecoderType::kMediaCodec; +} + void MediaCodecAudioDecoder::Initialize(const AudioDecoderConfig& config, CdmContext* cdm_context, InitCB init_cb, diff --git a/chromium/media/filters/android/media_codec_audio_decoder.h b/chromium/media/filters/android/media_codec_audio_decoder.h index 54414590150..f5d7238913c 100644 --- a/chromium/media/filters/android/media_codec_audio_decoder.h +++ b/chromium/media/filters/android/media_codec_audio_decoder.h @@ -86,6 +86,7 @@ class MEDIA_EXPORT MediaCodecAudioDecoder : public AudioDecoder, // AudioDecoder implementation. std::string GetDisplayName() const override; + AudioDecoderType GetDecoderType() const override; void Initialize(const AudioDecoderConfig& config, CdmContext* cdm_context, InitCB init_cb, diff --git a/chromium/media/filters/audio_decoder_stream_unittest.cc b/chromium/media/filters/audio_decoder_stream_unittest.cc index 483b0fa0b6d..9e6475c2042 100644 --- a/chromium/media/filters/audio_decoder_stream_unittest.cc +++ b/chromium/media/filters/audio_decoder_stream_unittest.cc @@ -110,8 +110,7 @@ class AudioDecoderStreamTest : public testing::Test { } void OnAudioBufferReadDone(base::OnceClosure closure, - AudioDecoderStream::ReadStatus status, - scoped_refptr<AudioBuffer> audio_buffer) { + AudioDecoderStream::ReadResult result) { std::move(closure).Run(); } diff --git a/chromium/media/filters/audio_decoder_unittest.cc b/chromium/media/filters/audio_decoder_unittest.cc index c02f516e192..d54d50b2c85 100644 --- a/chromium/media/filters/audio_decoder_unittest.cc +++ b/chromium/media/filters/audio_decoder_unittest.cc @@ -62,7 +62,7 @@ namespace { // The number of packets to read and then decode from each file. const size_t kDecodeRuns = 3; -enum AudioDecoderType { +enum TestAudioDecoderType { FFMPEG, #if defined(OS_ANDROID) MEDIA_CODEC, @@ -123,7 +123,7 @@ void SetDiscardPadding(AVPacket* packet, } // namespace class AudioDecoderTest - : public TestWithParam<std::tuple<AudioDecoderType, TestParams>> { + : public TestWithParam<std::tuple<TestAudioDecoderType, TestParams>> { public: AudioDecoderTest() : decoder_type_(std::get<0>(GetParam())), @@ -301,12 +301,12 @@ class AudioDecoderTest decoded_audio_.push_back(std::move(buffer)); } - void DecodeFinished(const base::Closure& quit_closure, Status status) { + void DecodeFinished(base::OnceClosure quit_closure, Status status) { EXPECT_TRUE(pending_decode_); EXPECT_FALSE(pending_reset_); pending_decode_ = false; last_decode_status_ = std::move(status); - quit_closure.Run(); + std::move(quit_closure).Run(); } void ResetFinished() { @@ -392,7 +392,7 @@ class AudioDecoderTest const Status& last_decode_status() const { return last_decode_status_; } private: - const AudioDecoderType decoder_type_; + const TestAudioDecoderType decoder_type_; // Current TestParams used to initialize the test and decoder. The initial // valie is std::get<1>(GetParam()). Could be overridden by set_param() so diff --git a/chromium/media/filters/audio_file_reader.cc b/chromium/media/filters/audio_file_reader.cc index cb81d920def..df95bac9730 100644 --- a/chromium/media/filters/audio_file_reader.cc +++ b/chromium/media/filters/audio_file_reader.cc @@ -276,9 +276,25 @@ bool AudioFileReader::OnNewFrame( sizeof(float) * frames_read); } } else { - audio_bus->FromInterleaved( - frame->data[0], frames_read, - av_get_bytes_per_sample(codec_context_->sample_fmt)); + int bytes_per_sample = av_get_bytes_per_sample(codec_context_->sample_fmt); + switch (bytes_per_sample) { + case 1: + audio_bus->FromInterleaved<UnsignedInt8SampleTypeTraits>( + reinterpret_cast<const uint8_t*>(frame->data[0]), frames_read); + break; + case 2: + audio_bus->FromInterleaved<SignedInt16SampleTypeTraits>( + reinterpret_cast<const int16_t*>(frame->data[0]), frames_read); + break; + case 4: + audio_bus->FromInterleaved<SignedInt32SampleTypeTraits>( + reinterpret_cast<const int32_t*>(frame->data[0]), frames_read); + break; + default: + NOTREACHED() << "Unsupported bytes per sample encountered: " + << bytes_per_sample; + audio_bus->ZeroFrames(frames_read); + } } (*total_frames) += frames_read; diff --git a/chromium/media/filters/audio_video_metadata_extractor_unittest.cc b/chromium/media/filters/audio_video_metadata_extractor_unittest.cc index df1cb131eb3..b26dd2c3a4c 100644 --- a/chromium/media/filters/audio_video_metadata_extractor_unittest.cc +++ b/chromium/media/filters/audio_video_metadata_extractor_unittest.cc @@ -55,6 +55,19 @@ const std::string GetTagValue( return tag_data->second; } +const std::string TagsToString( + const media::AudioVideoMetadataExtractor::TagDictionary& tags) { + std::string result; + for (auto& kv : tags) { + if (!result.empty()) + result += " | "; + result += kv.first; + result += ": "; + result += kv.second; + } + return result; +} + TEST(AudioVideoMetadataExtractorTest, InvalidFile) { GetExtractor("ten_byte_file", true, false, 0, -1, -1); } @@ -172,16 +185,19 @@ TEST(AudioVideoMetadataExtractorTest, AndroidRotatedMP4Video) { GetTagValue(extractor->stream_infos()[0].tags, "minor_version")); EXPECT_EQ("h264", extractor->stream_infos()[1].type); - EXPECT_EQ(5u, extractor->stream_infos()[1].tags.size()); + EXPECT_EQ(6u, extractor->stream_infos()[1].tags.size()) + << "Tags: " << TagsToString(extractor->stream_infos()[1].tags); EXPECT_EQ("2014-02-11T00:39:25.000000Z", GetTagValue(extractor->stream_infos()[1].tags, "creation_time")); EXPECT_EQ("VideoHandle", GetTagValue(extractor->stream_infos()[1].tags, "handler_name")); + EXPECT_EQ("MOTO", GetTagValue(extractor->stream_infos()[1].tags, "encoder")); EXPECT_EQ("eng", GetTagValue(extractor->stream_infos()[1].tags, "language")); EXPECT_EQ("90", GetTagValue(extractor->stream_infos()[1].tags, "rotate")); EXPECT_EQ("aac", extractor->stream_infos()[2].type); - EXPECT_EQ(3u, extractor->stream_infos()[2].tags.size()); + EXPECT_EQ(4u, extractor->stream_infos()[2].tags.size()) + << "Tags: " << TagsToString(extractor->stream_infos()[2].tags); EXPECT_EQ("2014-02-11T00:39:25.000000Z", GetTagValue(extractor->stream_infos()[2].tags, "creation_time")); EXPECT_EQ("SoundHandle", @@ -258,7 +274,8 @@ TEST(AudioVideoMetadataExtractorTest, AudioFLACInMp4) { GetTagValue(extractor->stream_infos()[0].tags, "encoder")); EXPECT_EQ("flac", extractor->stream_infos()[1].type); - EXPECT_EQ(2u, extractor->stream_infos()[1].tags.size()); + EXPECT_EQ(3u, extractor->stream_infos()[1].tags.size()) + << "Tags: " << TagsToString(extractor->stream_infos()[1].tags); EXPECT_EQ("SoundHandler", GetTagValue(extractor->stream_infos()[1].tags, "handler_name")); EXPECT_EQ("und", GetTagValue(extractor->stream_infos()[1].tags, "language")); diff --git a/chromium/media/filters/chunk_demuxer.cc b/chromium/media/filters/chunk_demuxer.cc index 9f0f2cc5999..2165cd2f121 100644 --- a/chromium/media/filters/chunk_demuxer.cc +++ b/chromium/media/filters/chunk_demuxer.cc @@ -624,6 +624,58 @@ void ChunkDemuxer::CancelPendingSeek(TimeDelta seek_time) { RunSeekCB_Locked(PIPELINE_OK); } +ChunkDemuxer::Status ChunkDemuxer::AddId( + const std::string& id, + std::unique_ptr<AudioDecoderConfig> audio_config) { + DCHECK(audio_config); + DVLOG(1) << __func__ << " id=" + << " audio_config=" << audio_config->AsHumanReadableString(); + base::AutoLock auto_lock(lock_); + + // Any valid audio config provided by WC is bufferable here, though decode + // error may occur later. + if (!audio_config->IsValidConfig()) + return ChunkDemuxer::kNotSupported; + + if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id)) + return kReachedIdLimit; + + DCHECK(init_cb_); + + std::string expected_codec = GetCodecName(audio_config->codec()); + std::unique_ptr<media::StreamParser> stream_parser( + media::StreamParserFactory::Create(std::move(audio_config))); + DCHECK(stream_parser); + + return AddIdInternal(id, std::move(stream_parser), expected_codec); +} + +ChunkDemuxer::Status ChunkDemuxer::AddId( + const std::string& id, + std::unique_ptr<VideoDecoderConfig> video_config) { + DCHECK(video_config); + DVLOG(1) << __func__ << " id=" + << " video_config=" << video_config->AsHumanReadableString(); + base::AutoLock auto_lock(lock_); + + // Any valid video config provided by WC is bufferable here, though decode + // error may occur later. + if (!video_config->IsValidConfig()) + return ChunkDemuxer::kNotSupported; + + if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id)) + return kReachedIdLimit; + + DCHECK(init_cb_); + + std::string expected_codec = GetCodecName(video_config->codec()); + std::unique_ptr<media::StreamParser> stream_parser( + media::StreamParserFactory::Create(std::move(video_config))); + DCHECK(stream_parser); + + return AddIdInternal(id, std::move(stream_parser), expected_codec); +} + ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, const std::string& content_type, const std::string& codecs) { @@ -646,6 +698,18 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, return ChunkDemuxer::kNotSupported; } + return AddIdInternal(id, std::move(stream_parser), + ExpectedCodecs(content_type, codecs)); +} + +ChunkDemuxer::Status ChunkDemuxer::AddIdInternal( + const std::string& id, + std::unique_ptr<media::StreamParser> stream_parser, + std::string expected_codecs) { + DVLOG(2) << __func__ << " id=" << id + << " expected_codecs=" << expected_codecs; + lock_.AssertAcquired(); + std::unique_ptr<FrameProcessor> frame_processor = std::make_unique<FrameProcessor>( base::BindRepeating(&ChunkDemuxer::IncreaseDurationIfNecessary, @@ -670,8 +734,8 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, source_state->Init(base::BindOnce(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this), id), - ExpectedCodecs(content_type, codecs), - encrypted_media_init_data_cb_, base::NullCallback()); + expected_codecs, encrypted_media_init_data_cb_, + base::NullCallback()); // TODO(wolenetz): Change to DCHECKs once less verification in release build // is needed. See https://crbug.com/786975. @@ -900,6 +964,66 @@ bool ChunkDemuxer::AppendData(const std::string& id, return true; } +bool ChunkDemuxer::AppendChunks( + const std::string& id, + std::unique_ptr<StreamParser::BufferQueue> buffer_queue, + base::TimeDelta append_window_start, + base::TimeDelta append_window_end, + base::TimeDelta* timestamp_offset) { + DCHECK(buffer_queue); + DVLOG(1) << __func__ << ": " << id + << ", buffer_queue size()=" << buffer_queue->size(); + + DCHECK(!id.empty()); + DCHECK(timestamp_offset); + + Ranges<TimeDelta> ranges; + + { + base::AutoLock auto_lock(lock_); + DCHECK_NE(state_, ENDED); + + // Capture if any of the SourceBuffers are waiting for data before we start + // buffering new chunks. + bool old_waiting_for_data = IsSeekWaitingForData_Locked(); + + if (buffer_queue->size() == 0u) + return true; + + switch (state_) { + case INITIALIZING: + case INITIALIZED: + DCHECK(IsValidId(id)); + if (!source_state_map_[id]->AppendChunks( + std::move(buffer_queue), append_window_start, append_window_end, + timestamp_offset)) { + ReportError_Locked(CHUNK_DEMUXER_ERROR_APPEND_FAILED); + return false; + } + break; + + case PARSE_ERROR: + case WAITING_FOR_INIT: + case ENDED: + case SHUTDOWN: + DVLOG(1) << "AppendChunks(): called in unexpected state " << state_; + return false; + } + + // Check to see if data was appended at the pending seek point. This + // indicates we have parsed enough data to complete the seek. Work is still + // in progress at this point, but it's okay since |seek_cb_| will post. + if (old_waiting_for_data && !IsSeekWaitingForData_Locked() && seek_cb_) + RunSeekCB_Locked(PIPELINE_OK); + + ranges = GetBufferedRanges_Locked(); + } + + host_->OnBufferedTimeRangesChanged(ranges); + progress_cb_.Run(); + return true; +} + void ChunkDemuxer::ResetParserState(const std::string& id, TimeDelta append_window_start, TimeDelta append_window_end, @@ -1294,7 +1418,7 @@ ChunkDemuxerStream* ChunkDemuxer::CreateDemuxerStream( DemuxerStream::Type type) { // New ChunkDemuxerStreams can be created only during initialization segment // processing, which happens when a new chunk of data is appended and the - // lock_ must be held by ChunkDemuxer::AppendData. + // lock_ must be held by ChunkDemuxer::AppendData/Chunks. lock_.AssertAcquired(); MediaTrack::Id media_track_id = GenerateMediaTrackId(); diff --git a/chromium/media/filters/chunk_demuxer.h b/chromium/media/filters/chunk_demuxer.h index f86d931e9a0..68a2488a13a 100644 --- a/chromium/media/filters/chunk_demuxer.h +++ b/chromium/media/filters/chunk_demuxer.h @@ -33,6 +33,9 @@ class MEDIA_EXPORT SourceBufferStream; namespace media { +class AudioDecoderConfig; +class VideoDecoderConfig; + class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream { public: using BufferQueue = base::circular_deque<scoped_refptr<StreamParserBuffer>>; @@ -199,7 +202,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { }; // |open_cb| Run when Initialize() is called to signal that the demuxer - // is ready to receive media data via AppendData(). + // is ready to receive media data via AppendData/Chunks(). // |progress_cb| Run each time data is appended. // |encrypted_media_init_data_cb| Run when the demuxer determines that an // encryption key is needed to decrypt the content. @@ -233,17 +236,25 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { void StartWaitingForSeek(base::TimeDelta seek_time) override; void CancelPendingSeek(base::TimeDelta seek_time) override; - // Registers a new |id| to use for AppendData() calls. |content_type| + // Registers a new |id| to use for AppendData/Chunks() calls. |content_type| // indicates the MIME type's ContentType and |codecs| indicates the MIME // type's "codecs" parameter string (if any) for the data that we intend to // append for this ID. kOk is returned if the demuxer has enough resources to // support another ID and supports the format indicated by |content_type| and - // |codecs|. kReachedIdLimit is returned if the demuxer cannot handle another - // ID right now. kNotSupported is returned if |content_type| and |codecs| is + // |codecs|. kReachedIdLimit is returned if the demuxer cannot handle another + // ID right now. kNotSupported is returned if |content_type| and |codecs| is // not a supported format. + // The |audio_config| and |video_config| overloads behave similarly, except + // the caller must provide valid, supported decoder configs; those overloads' + // usage indicates that we intend to append WebCodecs encoded audio or video + // chunks for this ID. Status AddId(const std::string& id, const std::string& content_type, const std::string& codecs); + Status AddId(const std::string& id, + std::unique_ptr<AudioDecoderConfig> audio_config); + Status AddId(const std::string& id, + std::unique_ptr<VideoDecoderConfig> video_config); // Notifies a caller via |tracks_updated_cb| that the set of media tracks // for a given |id| has changed. @@ -286,6 +297,16 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { base::TimeDelta append_window_end, base::TimeDelta* timestamp_offset); + // Appends webcodecs encoded chunks (already converted by caller into a + // BufferQueue of StreamParserBuffers) to the source buffer associated with + // |id|, with same semantic for other parameters and return value as + // AppendData(). + bool AppendChunks(const std::string& id, + std::unique_ptr<StreamParser::BufferQueue> buffer_queue, + base::TimeDelta append_window_start, + base::TimeDelta append_window_end, + base::TimeDelta* timestamp_offset); + // Aborts parsing the current segment and reset the parser to a state where // it can accept a new segment. // Some pending frames can be emitted during that process. These frames are @@ -393,6 +414,14 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { SHUTDOWN, }; + // Helper for AddId's creation of FrameProcessor, and + // SourceBufferState creation, initialization and tracking in + // source_state_map_. + ChunkDemuxer::Status AddIdInternal( + const std::string& id, + std::unique_ptr<media::StreamParser> stream_parser, + std::string expected_codecs); + // Helper for vide and audio track changing. void FindAndEnableProperTracks(const std::vector<MediaTrack::Id>& track_ids, base::TimeDelta curr_time, @@ -499,7 +528,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { base::TimeDelta duration_; // The duration passed to the last SetDuration(). If - // SetDuration() is never called or an AppendData() call or + // SetDuration() is never called or an AppendData/Chunks() call or // a EndOfStream() call changes |duration_|, then this // variable is set to < 0 to indicate that the |duration_| represents // the actual duration instead of a user specified value. diff --git a/chromium/media/filters/chunk_demuxer_unittest.cc b/chromium/media/filters/chunk_demuxer_unittest.cc index 2ed13c82723..a3de1346858 100644 --- a/chromium/media/filters/chunk_demuxer_unittest.cc +++ b/chromium/media/filters/chunk_demuxer_unittest.cc @@ -4388,10 +4388,10 @@ TEST_F(ChunkDemuxerTest, } namespace { -void QuitLoop(base::Closure quit_closure, +void QuitLoop(base::OnceClosure quit_closure, DemuxerStream::Type type, const std::vector<DemuxerStream*>& streams) { - quit_closure.Run(); + std::move(quit_closure).Run(); } void DisableAndEnableDemuxerTracks( diff --git a/chromium/media/filters/dav1d_video_decoder.cc b/chromium/media/filters/dav1d_video_decoder.cc index bb31801e439..7e5eaf5b245 100644 --- a/chromium/media/filters/dav1d_video_decoder.cc +++ b/chromium/media/filters/dav1d_video_decoder.cc @@ -129,6 +129,16 @@ struct ScopedDav1dPictureFree { } }; +// static +SupportedVideoDecoderConfigs Dav1dVideoDecoder::SupportedConfigs() { + return {{/*profile_min=*/AV1PROFILE_PROFILE_MAIN, + /*profile_max=*/AV1PROFILE_PROFILE_HIGH, + /*coded_size_min=*/kDefaultSwDecodeSizeMin, + /*coded_size_max=*/kDefaultSwDecodeSizeMax, + /*allow_encrypted=*/false, + /*require_encrypted=*/false}}; +} + Dav1dVideoDecoder::Dav1dVideoDecoder(MediaLog* media_log, OffloadState offload_state) : media_log_(media_log), @@ -145,6 +155,10 @@ std::string Dav1dVideoDecoder::GetDisplayName() const { return "Dav1dVideoDecoder"; } +VideoDecoderType Dav1dVideoDecoder::GetDecoderType() const { + return VideoDecoderType::kDav1d; +} + void Dav1dVideoDecoder::Initialize(const VideoDecoderConfig& config, bool low_delay, CdmContext* /* cdm_context */, @@ -368,7 +382,7 @@ bool Dav1dVideoDecoder::DecodeBuffer(scoped_refptr<DecoderBuffer> buffer) { color_space = config_.color_space_info(); frame->set_color_space(color_space.ToGfxColorSpace()); - frame->metadata()->power_efficient = false; + frame->metadata().power_efficient = false; frame->set_hdr_metadata(config_.hdr_metadata()); // When we use bind mode, our image data is dependent on the Dav1dPicture, diff --git a/chromium/media/filters/dav1d_video_decoder.h b/chromium/media/filters/dav1d_video_decoder.h index 22c4e1fd381..689628ae07e 100644 --- a/chromium/media/filters/dav1d_video_decoder.h +++ b/chromium/media/filters/dav1d_video_decoder.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/memory/ref_counted_memory.h" #include "base/sequence_checker.h" +#include "media/base/supported_video_decoder_config.h" #include "media/base/video_decoder.h" #include "media/base/video_decoder_config.h" #include "media/base/video_frame.h" @@ -24,11 +25,14 @@ class MediaLog; class MEDIA_EXPORT Dav1dVideoDecoder : public OffloadableVideoDecoder { public: + static SupportedVideoDecoderConfigs SupportedConfigs(); + Dav1dVideoDecoder(MediaLog* media_log, OffloadState offload_state = OffloadState::kNormal); ~Dav1dVideoDecoder() override; // VideoDecoder implementation. + VideoDecoderType GetDecoderType() const override; std::string GetDisplayName() const override; void Initialize(const VideoDecoderConfig& config, bool low_delay, diff --git a/chromium/media/filters/dav1d_video_decoder_unittest.cc b/chromium/media/filters/dav1d_video_decoder_unittest.cc index 19572a06873..0711b2f9e7e 100644 --- a/chromium/media/filters/dav1d_video_decoder_unittest.cc +++ b/chromium/media/filters/dav1d_video_decoder_unittest.cc @@ -169,7 +169,7 @@ class Dav1dVideoDecoderTest : public testing::Test { } void FrameReady(scoped_refptr<VideoFrame> frame) { - DCHECK(!frame->metadata()->end_of_stream); + DCHECK(!frame->metadata().end_of_stream); output_frames_.push_back(std::move(frame)); } diff --git a/chromium/media/filters/decoder_selector.cc b/chromium/media/filters/decoder_selector.cc index adba332ab89..ac99eae91ca 100644 --- a/chromium/media/filters/decoder_selector.cc +++ b/chromium/media/filters/decoder_selector.cc @@ -33,9 +33,25 @@ namespace { const char kSelectDecoderTrace[] = "DecoderSelector::SelectDecoder"; +bool SkipDecoderForRTC(const AudioDecoderConfig& /*config*/, + const AudioDecoder& /*decoder*/) { + return false; +} + +bool SkipDecoderForRTC(const VideoDecoderConfig& config, + const VideoDecoder& decoder) { + // For now, we assume that RTC decoders are able to decode non-RTC streams, + // presumably by configuring themselves based on the config's rtc bit. Since + // no decoders take any action at all based on it, this is as good as any. + return config.is_rtc() && !decoder.IsOptimizedForRTC(); +} + template <typename ConfigT, typename DecoderT> -DecoderPriority NormalDecoderPriority(const ConfigT& /*config*/, - const DecoderT& /*decoder*/) { +DecoderPriority NormalDecoderPriority(const ConfigT& config, + const DecoderT& decoder) { + if (SkipDecoderForRTC(config, decoder)) + return DecoderPriority::kSkipped; + return DecoderPriority::kNormal; } @@ -43,12 +59,15 @@ DecoderPriority ResolutionBasedDecoderPriority(const VideoDecoderConfig& config, const VideoDecoder& decoder) { #if defined(OS_ANDROID) constexpr auto kSoftwareDecoderHeightCutoff = 360; -#elif BUILDFLAG(IS_ASH) +#elif BUILDFLAG(IS_CHROMEOS_ASH) constexpr auto kSoftwareDecoderHeightCutoff = 360; #else constexpr auto kSoftwareDecoderHeightCutoff = 720; #endif + if (SkipDecoderForRTC(config, decoder)) + return DecoderPriority::kSkipped; + // We only do a height check to err on the side of prioritizing platform // decoders. const auto at_or_above_software_cutoff = @@ -62,8 +81,11 @@ DecoderPriority ResolutionBasedDecoderPriority(const VideoDecoderConfig& config, } template <typename ConfigT, typename DecoderT> -DecoderPriority SkipNonPlatformDecoders(const ConfigT& /*config*/, +DecoderPriority SkipNonPlatformDecoders(const ConfigT& config, const DecoderT& decoder) { + if (SkipDecoderForRTC(config, decoder)) + return DecoderPriority::kSkipped; + return decoder.IsPlatformDecoder() ? DecoderPriority::kNormal : DecoderPriority::kSkipped; } diff --git a/chromium/media/filters/decoder_selector_unittest.cc b/chromium/media/filters/decoder_selector_unittest.cc index db84317a22c..e32c8d0e32c 100644 --- a/chromium/media/filters/decoder_selector_unittest.cc +++ b/chromium/media/filters/decoder_selector_unittest.cc @@ -164,6 +164,8 @@ class AudioDecoderSelectorTestParam { static void ExpectNotInitialize(MockDecoder* decoder) { EXPECT_CALL(*decoder, Initialize_(_, _, _, _, _)).Times(0); } + + static void SetRTCDecoderness(MockDecoder* decoder, bool is_rtc_decoder) {} }; // Allocate storage for the member variables. @@ -246,6 +248,11 @@ class VideoDecoderSelectorTestParam { static void ExpectNotInitialize(MockDecoder* decoder) { EXPECT_CALL(*decoder, Initialize_(_, _, _, _, _, _)).Times(0); } + + static void SetRTCDecoderness(MockDecoder* decoder, bool is_optimized) { + EXPECT_CALL(*decoder, IsOptimizedForRTC()) + .WillRepeatedly(Return(is_optimized)); + } }; // Allocate storate for the member variables. @@ -289,6 +296,7 @@ class DecoderSelectorTest : public ::testing::Test { bool supports_decryption; bool is_platform_decoder; bool expect_not_initialized; + bool is_rtc_decoder = false; }; DecoderSelectorTest() @@ -330,6 +338,14 @@ class DecoderSelectorTest : public ::testing::Test { AddMockDecoder(std::move(args)); } + void AddMockRTCPlatformDecoder(const std::string& decoder_name, + DecoderCapability capability) { + auto args = MockDecoderArgs::Create(std::move(decoder_name), capability); + args.is_rtc_decoder = true; + args.is_platform_decoder = true; + AddMockDecoder(std::move(args)); + } + void AddMockDecoder(MockDecoderArgs args) { // Actual decoders are created in CreateDecoders(), which may be called // multiple times by the DecoderSelector. @@ -357,6 +373,7 @@ class DecoderSelectorTest : public ::testing::Test { } else { TypeParam::ExpectInitialize(decoder.get(), args.capability); } + TypeParam::SetRTCDecoderness(decoder.get(), args.is_rtc_decoder); decoders.push_back(std::move(decoder)); } @@ -1049,4 +1066,54 @@ TEST_F(VideoDecoderSelectorTest, EncryptedStream_PrioritizePlatformDecoders) { this->SelectDecoder(); } +// Tests that the normal decoder selector rule skips non-RTC decoders for RTC. +TEST_F(VideoDecoderSelectorTest, RTC_NormalPriority) { + base::test::ScopedFeatureList features; + + this->AddMockDecoder(kDecoder1, kAlwaysSucceed); + this->AddMockRTCPlatformDecoder(kDecoder2, kAlwaysSucceed); + + auto config = TestVideoConfig::Custom(gfx::Size(4096, 4096)); + config.set_is_rtc(true); + this->demuxer_stream_.set_video_decoder_config(config); + this->CreateDecoderSelector(); + + EXPECT_CALL(*this, OnDecoderSelected(kDecoder2, IsNull())); + this->SelectDecoder(); +} + +// Tests that the resolution-based rule skips non-RTC decoders for RTC. +TEST_F(VideoDecoderSelectorTest, RTC_DecoderBasedPriority) { + base::test::ScopedFeatureList features; + features.InitAndEnableFeature(kResolutionBasedDecoderPriority); + + this->AddMockDecoder(kDecoder1, kAlwaysSucceed); + this->AddMockRTCPlatformDecoder(kDecoder2, kAlwaysSucceed); + + auto config = TestVideoConfig::Custom(gfx::Size(4096, 4096)); + config.set_is_rtc(true); + this->demuxer_stream_.set_video_decoder_config(config); + this->CreateDecoderSelector(); + + EXPECT_CALL(*this, OnDecoderSelected(kDecoder2, IsNull())); + this->SelectDecoder(); +} + +// Tests that the hardware-based rule skips non-RTC decoders for RTC. +TEST_F(VideoDecoderSelectorTest, RTC_ForceHardwareDecoders) { + base::test::ScopedFeatureList features; + features.InitAndEnableFeature(kForceHardwareVideoDecoders); + + this->AddMockPlatformDecoder(kDecoder1, kAlwaysSucceed); + this->AddMockRTCPlatformDecoder(kDecoder2, kAlwaysSucceed); + + auto config = TestVideoConfig::Custom(gfx::Size(4096, 4096)); + config.set_is_rtc(true); + this->demuxer_stream_.set_video_decoder_config(config); + this->CreateDecoderSelector(); + + EXPECT_CALL(*this, OnDecoderSelected(kDecoder2, IsNull())); + this->SelectDecoder(); +} + } // namespace media diff --git a/chromium/media/filters/decoder_stream.cc b/chromium/media/filters/decoder_stream.cc index c4b4c806d6c..bf80bb13684 100644 --- a/chromium/media/filters/decoder_stream.cc +++ b/chromium/media/filters/decoder_stream.cc @@ -78,17 +78,14 @@ const char* GetPrepareTraceString<DemuxerStream::AUDIO>() { return "AudioDecoderStream::PrepareOutput"; } -template <DemuxerStream::Type StreamType> -const char* GetStatusString( - typename DecoderStream<StreamType>::ReadStatus status) { - switch (status) { - case DecoderStream<StreamType>::OK: +const char* GetStatusString(const Status& status) { + // TODO(crbug.com/1129662): Replace this with generic Status-to-string. + switch (status.code()) { + case StatusCode::kOk: return "okay"; - case DecoderStream<StreamType>::ABORTED: + case StatusCode::kAborted: return "aborted"; - case DecoderStream<StreamType>::DEMUXER_READ_ABORTED: - return "demuxer_read_aborted"; - case DecoderStream<StreamType>::DECODE_ERROR: + default: return "decode_error"; } @@ -130,7 +127,7 @@ DecoderStream<StreamType>::~DecoderStream() { } if (read_cb_) { read_cb_ = BindToCurrentLoop(std::move(read_cb_)); - SatisfyRead(ABORTED, nullptr); + SatisfyRead(StatusCode::kAborted); } if (reset_cb_) task_runner_->PostTask(FROM_HERE, std::move(reset_cb_)); @@ -189,20 +186,22 @@ void DecoderStream<StreamType>::Read(ReadCB read_cb) { TRACE_EVENT_ASYNC_BEGIN0("media", GetReadTraceString<StreamType>(), this); if (state_ == STATE_ERROR) { read_cb_ = BindToCurrentLoop(std::move(read_cb)); - SatisfyRead(DECODE_ERROR, nullptr); + // TODO(crbug.com/1129662): Consider attaching a caused-by of the original + // error as well. + SatisfyRead(StatusCode::kDecoderStreamInErrorState); return; } if (state_ == STATE_END_OF_STREAM && ready_outputs_.empty() && unprepared_outputs_.empty()) { read_cb_ = BindToCurrentLoop(std::move(read_cb)); - SatisfyRead(OK, StreamTraits::CreateEOSOutput()); + SatisfyRead(StreamTraits::CreateEOSOutput()); return; } if (!ready_outputs_.empty()) { read_cb_ = BindToCurrentLoop(std::move(read_cb)); - SatisfyRead(OK, ready_outputs_.front()); + SatisfyRead(ready_outputs_.front()); ready_outputs_.pop_front(); MaybePrepareAnotherOutput(); } else { @@ -224,7 +223,7 @@ void DecoderStream<StreamType>::Reset(base::OnceClosure closure) { if (read_cb_) { read_cb_ = BindToCurrentLoop(std::move(read_cb_)); - SatisfyRead(ABORTED, nullptr); + SatisfyRead(StatusCode::kAborted); } ClearOutputs(); @@ -411,7 +410,7 @@ void DecoderStream<StreamType>::OnDecoderSelected( media_log_->SetProperty<StreamTraits::kIsDecryptingDemuxerStream>( !!decrypting_demuxer_stream_); media_log_->SetProperty<StreamTraits::kDecoderName>( - decoder_->GetDisplayName()); + decoder_->GetDecoderType()); media_log_->SetProperty<StreamTraits::kIsPlatformDecoder>( decoder_->IsPlatformDecoder()); @@ -440,12 +439,11 @@ void DecoderStream<StreamType>::OnDecoderSelected( } template <DemuxerStream::Type StreamType> -void DecoderStream<StreamType>::SatisfyRead(ReadStatus status, - scoped_refptr<Output> output) { +void DecoderStream<StreamType>::SatisfyRead(ReadResult result) { DCHECK(read_cb_); TRACE_EVENT_ASYNC_END1("media", GetReadTraceString<StreamType>(), this, - "status", GetStatusString<StreamType>(status)); - std::move(read_cb_).Run(status, std::move(output)); + "status", GetStatusString(result.code())); + std::move(read_cb_).Run(std::move(result)); } template <DemuxerStream::Type StreamType> @@ -505,7 +503,7 @@ void DecoderStream<StreamType>::DecodeInternal( std::move(buffer), base::BindOnce(&DecoderStream<StreamType>::OnDecodeDone, fallback_weak_factory_.GetWeakPtr(), buffer_size, - decoding_eos_, base::Passed(&trace_event))); + decoding_eos_, std::move(trace_event))); } template <DemuxerStream::Type StreamType> @@ -567,7 +565,7 @@ void DecoderStream<StreamType>::OnDecodeDone( if (end_of_stream) { state_ = STATE_END_OF_STREAM; if (ready_outputs_.empty() && unprepared_outputs_.empty() && read_cb_) - SatisfyRead(OK, StreamTraits::CreateEOSOutput()); + SatisfyRead(StreamTraits::CreateEOSOutput()); return; } @@ -594,14 +592,14 @@ void DecoderStream<StreamType>::OnDecodeDone( state_ = STATE_REINITIALIZING_DECODER; SelectDecoder(); } else { - media_log_->NotifyError(std::move(status)); + media_log_->NotifyError(status); MEDIA_LOG(ERROR, media_log_) << GetStreamTypeString() << " decode error!"; state_ = STATE_ERROR; ClearOutputs(); if (read_cb_) - SatisfyRead(DECODE_ERROR, nullptr); + SatisfyRead(std::move(status)); } return; } @@ -653,7 +651,7 @@ void DecoderStream<StreamType>::OnDecodeOutputReady( // If |ready_outputs_| was non-empty, the read would have already been // satisifed by Read(). DCHECK(ready_outputs_.empty()); - SatisfyRead(OK, std::move(output)); + SatisfyRead(std::move(output)); return; } @@ -743,7 +741,7 @@ void DecoderStream<StreamType>::OnBufferReady( pending_buffers_.clear(); ClearOutputs(); if (read_cb_) - SatisfyRead(DECODE_ERROR, nullptr); + SatisfyRead(StatusCode::kDecoderStreamDemuxerError); } // Decoding has been stopped. @@ -819,7 +817,7 @@ void DecoderStream<StreamType>::OnBufferReady( if (status == DemuxerStream::kAborted) { if (read_cb_) - SatisfyRead(DEMUXER_READ_ABORTED, nullptr); + SatisfyRead(StatusCode::kAborted); return; } @@ -862,7 +860,7 @@ void DecoderStream<StreamType>::CompleteDecoderReinitialization(bool success) { if (state_ == STATE_ERROR) { MEDIA_LOG(ERROR, media_log_) << GetStreamTypeString() << " decoder reinitialization failed"; - SatisfyRead(DECODE_ERROR, nullptr); + SatisfyRead(StatusCode::kDecoderStreamReinitFailed); return; } @@ -981,7 +979,7 @@ void DecoderStream<StreamType>::OnPreparedOutputReady( if (!read_cb_) ready_outputs_.emplace_back(std::move(output)); else - SatisfyRead(OK, std::move(output)); + SatisfyRead(std::move(output)); MaybePrepareAnotherOutput(); diff --git a/chromium/media/filters/decoder_stream.h b/chromium/media/filters/decoder_stream.h index 0d881689024..6fe5739c8d3 100644 --- a/chromium/media/filters/decoder_stream.h +++ b/chromium/media/filters/decoder_stream.h @@ -14,7 +14,7 @@ #include "base/containers/circular_deque.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/util/type_safety/pass_key.h" +#include "base/types/pass_key.h" #include "media/base/audio_decoder.h" #include "media/base/audio_timestamp_helper.h" #include "media/base/demuxer_stream.h" @@ -46,13 +46,6 @@ class MEDIA_EXPORT DecoderStream { using Output = typename StreamTraits::OutputType; using DecoderConfig = typename StreamTraits::DecoderConfigType; - enum ReadStatus { - OK, // Everything went as planned. - ABORTED, // Read aborted due to Reset() during pending read. - DEMUXER_READ_ABORTED, // Demuxer returned aborted read. - DECODE_ERROR, // Decoder returned decode error. - }; - // Callback to create a list of decoders. using CreateDecodersCB = base::RepeatingCallback<std::vector<std::unique_ptr<Decoder>>()>; @@ -61,7 +54,8 @@ class MEDIA_EXPORT DecoderStream { using InitCB = base::OnceCallback<void(bool success)>; // Indicates completion of a DecoderStream read. - using ReadCB = base::OnceCallback<void(ReadStatus, scoped_refptr<Output>)>; + using ReadResult = StatusOr<scoped_refptr<Output>>; + using ReadCB = base::OnceCallback<void(ReadResult)>; DecoderStream(std::unique_ptr<DecoderStreamTraits<StreamType>> traits, scoped_refptr<base::SequencedTaskRunner> task_runner, @@ -131,9 +125,10 @@ class MEDIA_EXPORT DecoderStream { config_change_observer_cb_ = config_change_observer; } - // Allows tests to keep track the currently selected decoder. + // Allow interested folks to keep track the currently selected decoder. The + // provided decoder is valid only during the scope of the callback. using DecoderChangeObserverCB = base::RepeatingCallback<void(Decoder*)>; - void set_decoder_change_observer_for_testing( + void set_decoder_change_observer( DecoderChangeObserverCB decoder_change_observer_cb) { decoder_change_observer_cb_ = std::move(decoder_change_observer_cb); } @@ -149,7 +144,7 @@ class MEDIA_EXPORT DecoderStream { bool is_demuxer_read_pending() const { return pending_demuxer_read_; } DecoderSelector<StreamType>& GetDecoderSelectorForTesting( - util::PassKey<class VideoDecoderStreamTest>) { + base::PassKey<class VideoDecoderStreamTest>) { return decoder_selector_; } @@ -185,8 +180,8 @@ class MEDIA_EXPORT DecoderStream { std::unique_ptr<Decoder> selected_decoder, std::unique_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream); - // Satisfy pending |read_cb_| with |status| and |output|. - void SatisfyRead(ReadStatus status, scoped_refptr<Output> output); + // Satisfy pending |read_cb_| with |result|. + void SatisfyRead(ReadResult result); // Decodes |buffer| and returns the result via OnDecodeOutputReady(). // Saves |buffer| into |pending_buffers_| if appropriate. diff --git a/chromium/media/filters/decoder_stream_traits.cc b/chromium/media/filters/decoder_stream_traits.cc index c9e20bfd654..1222c802007 100644 --- a/chromium/media/filters/decoder_stream_traits.cc +++ b/chromium/media/filters/decoder_stream_traits.cc @@ -48,7 +48,9 @@ void DecoderStreamTraits<DemuxerStream::AUDIO>::SetIsDecryptingDemuxerStream( DecoderStreamTraits<DemuxerStream::AUDIO>::DecoderStreamTraits( MediaLog* media_log, ChannelLayout initial_hw_layout) - : media_log_(media_log), initial_hw_layout_(initial_hw_layout) {} + : media_log_(media_log), initial_hw_layout_(initial_hw_layout) { + weak_this_ = weak_factory_.GetWeakPtr(); +} DecoderStreamTraits<DemuxerStream::AUDIO>::DecoderConfigType DecoderStreamTraits<DemuxerStream::AUDIO>::GetDecoderConfig( @@ -80,9 +82,24 @@ void DecoderStreamTraits<DemuxerStream::AUDIO>::InitializeDecoder( OnConfigChanged(config); config_ = config; - stats_.audio_decoder_info.decoder_name = decoder->GetDisplayName(); - decoder->Initialize(config, cdm_context, std::move(init_cb), output_cb, - waiting_cb); + stats_.audio_decoder_info.decoder_type = AudioDecoderType::kUnknown; + // Both |this| and |decoder| are owned by a DecoderSelector and will stay + // alive at least until |init_cb| is finished executing. + decoder->Initialize( + config, cdm_context, + base::BindOnce( + &DecoderStreamTraits<DemuxerStream::AUDIO>::OnDecoderInitialized, + weak_this_, base::Unretained(decoder), std::move(init_cb)), + output_cb, waiting_cb); +} + +void DecoderStreamTraits<DemuxerStream::AUDIO>::OnDecoderInitialized( + DecoderType* decoder, + InitCB cb, + Status result) { + if (result.is_ok()) + stats_.audio_decoder_info.decoder_type = decoder->GetDecoderType(); + std::move(cb).Run(result); } void DecoderStreamTraits<DemuxerStream::AUDIO>::OnStreamReset( @@ -147,7 +164,9 @@ void DecoderStreamTraits<DemuxerStream::VIDEO>::SetIsDecryptingDemuxerStream( DecoderStreamTraits<DemuxerStream::VIDEO>::DecoderStreamTraits( MediaLog* media_log) // Randomly selected number of samples to keep. - : keyframe_distance_average_(16) {} + : keyframe_distance_average_(16) { + weak_this_ = weak_factory_.GetWeakPtr(); +} DecoderStreamTraits<DemuxerStream::VIDEO>::DecoderConfigType DecoderStreamTraits<DemuxerStream::VIDEO>::GetDecoderConfig( @@ -181,10 +200,25 @@ void DecoderStreamTraits<DemuxerStream::VIDEO>::InitializeDecoder( const OutputCB& output_cb, const WaitingCB& waiting_cb) { DCHECK(config.IsValidConfig()); - stats_.video_decoder_info.decoder_name = decoder->GetDisplayName(); - DVLOG(2) << stats_.video_decoder_info.decoder_name; - decoder->Initialize(config, low_delay, cdm_context, std::move(init_cb), - output_cb, waiting_cb); + stats_.video_decoder_info.decoder_type = VideoDecoderType::kUnknown; + DVLOG(2) << decoder->GetDisplayName(); + // |decoder| is owned by a DecoderSelector and will stay + // alive at least until |init_cb| is finished executing. + decoder->Initialize( + config, low_delay, cdm_context, + base::BindOnce( + &DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecoderInitialized, + weak_this_, base::Unretained(decoder), std::move(init_cb)), + output_cb, waiting_cb); +} + +void DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecoderInitialized( + DecoderType* decoder, + InitCB cb, + Status result) { + if (result.is_ok()) + stats_.video_decoder_info.decoder_type = decoder->GetDecoderType(); + std::move(cb).Run(result); } void DecoderStreamTraits<DemuxerStream::VIDEO>::OnStreamReset( @@ -234,8 +268,8 @@ PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone( return PostDecodeAction::DELIVER; // Add a timestamp here to enable buffering delay measurements down the line. - buffer->metadata()->decode_begin_time = it->second.decode_begin_time; - buffer->metadata()->decode_end_time = base::TimeTicks::Now(); + buffer->metadata().decode_begin_time = it->second.decode_begin_time; + buffer->metadata().decode_end_time = base::TimeTicks::Now(); auto action = it->second.should_drop ? PostDecodeAction::DROP : PostDecodeAction::DELIVER; @@ -243,7 +277,7 @@ PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone( // Provide duration information to help the rendering algorithm on the very // first and very last frames. if (it->second.duration != kNoTimestamp) - buffer->metadata()->frame_duration = it->second.duration; + buffer->metadata().frame_duration = it->second.duration; // We erase from the beginning onward to our target frame since frames should // be returned in presentation order. It's possible to accumulate entries in @@ -255,12 +289,12 @@ PostDecodeAction DecoderStreamTraits<DemuxerStream::VIDEO>::OnDecodeDone( void DecoderStreamTraits<DemuxerStream::VIDEO>::OnOutputReady( OutputType* buffer) { - if (!buffer->metadata()->decode_begin_time.has_value()) + if (!buffer->metadata().decode_begin_time.has_value()) return; // Tag buffer with elapsed time since creation. - buffer->metadata()->processing_time = - base::TimeTicks::Now() - *buffer->metadata()->decode_begin_time; + buffer->metadata().processing_time = + base::TimeTicks::Now() - *buffer->metadata().decode_begin_time; } } // namespace media diff --git a/chromium/media/filters/decoder_stream_traits.h b/chromium/media/filters/decoder_stream_traits.h index 66fe7cd85e9..08aa2112881 100644 --- a/chromium/media/filters/decoder_stream_traits.h +++ b/chromium/media/filters/decoder_stream_traits.h @@ -65,6 +65,7 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::AUDIO> { InitCB init_cb, const OutputCB& output_cb, const WaitingCB& waiting_cb); + void OnDecoderInitialized(DecoderType* decoder, InitCB cb, Status status); DecoderConfigType GetDecoderConfig(DemuxerStream* stream); void OnDecode(const DecoderBuffer& buffer); PostDecodeAction OnDecodeDone(OutputType* buffer); @@ -84,6 +85,10 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::AUDIO> { ChannelLayout initial_hw_layout_; PipelineStatistics stats_; AudioDecoderConfig config_; + + base::WeakPtr<DecoderStreamTraits<DemuxerStream::AUDIO>> weak_this_; + base::WeakPtrFactory<DecoderStreamTraits<DemuxerStream::AUDIO>> weak_factory_{ + this}; }; template <> @@ -118,6 +123,7 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::VIDEO> { InitCB init_cb, const OutputCB& output_cb, const WaitingCB& waiting_cb); + void OnDecoderInitialized(DecoderType* decoder, InitCB cb, Status status); void OnDecode(const DecoderBuffer& buffer); PostDecodeAction OnDecodeDone(OutputType* buffer); void OnStreamReset(DemuxerStream* stream); @@ -136,6 +142,10 @@ class MEDIA_EXPORT DecoderStreamTraits<DemuxerStream::VIDEO> { base::flat_map<base::TimeDelta, FrameMetadata> frame_metadata_; PipelineStatistics stats_; + + base::WeakPtr<DecoderStreamTraits<DemuxerStream::VIDEO>> weak_this_; + base::WeakPtrFactory<DecoderStreamTraits<DemuxerStream::VIDEO>> weak_factory_{ + this}; }; } // namespace media diff --git a/chromium/media/filters/decrypting_audio_decoder.cc b/chromium/media/filters/decrypting_audio_decoder.cc index e42c00d8c6a..9415f532058 100644 --- a/chromium/media/filters/decrypting_audio_decoder.cc +++ b/chromium/media/filters/decrypting_audio_decoder.cc @@ -46,6 +46,10 @@ std::string DecryptingAudioDecoder::GetDisplayName() const { return "DecryptingAudioDecoder"; } +AudioDecoderType DecryptingAudioDecoder::GetDecoderType() const { + return AudioDecoderType::kDecrypting; +} + void DecryptingAudioDecoder::Initialize(const AudioDecoderConfig& config, CdmContext* cdm_context, InitCB init_cb, diff --git a/chromium/media/filters/decrypting_audio_decoder.h b/chromium/media/filters/decrypting_audio_decoder.h index aff41e2c9b7..9c77c521e1f 100644 --- a/chromium/media/filters/decrypting_audio_decoder.h +++ b/chromium/media/filters/decrypting_audio_decoder.h @@ -42,6 +42,7 @@ class MEDIA_EXPORT DecryptingAudioDecoder : public AudioDecoder { // Decoder implementation bool SupportsDecryption() const override; + AudioDecoderType GetDecoderType() const override; std::string GetDisplayName() const override; // AudioDecoder implementation. diff --git a/chromium/media/filters/decrypting_audio_decoder_unittest.cc b/chromium/media/filters/decrypting_audio_decoder_unittest.cc index b88ec863f1f..c796c8f302d 100644 --- a/chromium/media/filters/decrypting_audio_decoder_unittest.cc +++ b/chromium/media/filters/decrypting_audio_decoder_unittest.cc @@ -12,6 +12,7 @@ #include "base/run_loop.h" #include "base/stl_util.h" #include "base/test/gmock_callback_support.h" +#include "base/test/gmock_move_support.h" #include "base/test/task_environment.h" #include "media/base/audio_buffer.h" #include "media/base/decoder_buffer.h" @@ -23,12 +24,10 @@ #include "media/filters/decrypting_audio_decoder.h" #include "testing/gmock/include/gmock/gmock.h" -using ::base::test::RunCallback; using ::base::test::RunOnceCallback; using ::testing::_; using ::testing::AtMost; using ::testing::Return; -using ::testing::SaveArg; using ::testing::StrictMock; namespace media { @@ -148,20 +147,21 @@ class DecryptingAudioDecoderTest : public testing::Test { // Helper function to simulate the decrypting and decoding process in the // |decryptor_| with a decoding delay of kDecodingDelay buffers. void DecryptAndDecodeAudio(scoped_refptr<DecoderBuffer> encrypted, - const Decryptor::AudioDecodeCB& audio_decode_cb) { + Decryptor::AudioDecodeCB audio_decode_cb) { num_decrypt_and_decode_calls_++; if (!encrypted->end_of_stream()) num_frames_in_decryptor_++; if (num_decrypt_and_decode_calls_ <= kDecodingDelay || num_frames_in_decryptor_ == 0) { - audio_decode_cb.Run(Decryptor::kNeedMoreData, Decryptor::AudioFrames()); + std::move(audio_decode_cb) + .Run(Decryptor::kNeedMoreData, Decryptor::AudioFrames()); return; } num_frames_in_decryptor_--; - audio_decode_cb.Run(Decryptor::kSuccess, - Decryptor::AudioFrames(1, decoded_frame_)); + std::move(audio_decode_cb) + .Run(Decryptor::kSuccess, Decryptor::AudioFrames(1, decoded_frame_)); } // Sets up expectations and actions to put DecryptingAudioDecoder in an @@ -189,7 +189,7 @@ class DecryptingAudioDecoderTest : public testing::Test { void EnterPendingDecodeState() { EXPECT_TRUE(!pending_audio_decode_cb_); EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(encrypted_buffer_, _)) - .WillOnce(SaveArg<1>(&pending_audio_decode_cb_)); + .WillOnce(MoveArg<1>(&pending_audio_decode_cb_)); decoder_->Decode(encrypted_buffer_, base::BindOnce(&DecryptingAudioDecoderTest::DecodeDone, @@ -203,7 +203,7 @@ class DecryptingAudioDecoderTest : public testing::Test { void EnterWaitingForKeyState() { EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(encrypted_buffer_, _)) .WillRepeatedly( - RunCallback<1>(Decryptor::kNoKey, Decryptor::AudioFrames())); + RunOnceCallback<1>(Decryptor::kNoKey, Decryptor::AudioFrames())); EXPECT_CALL(*this, OnWaiting(WaitingReason::kNoDecryptionKey)); decoder_->Decode(encrypted_buffer_, base::BindOnce(&DecryptingAudioDecoderTest::DecodeDone, @@ -326,7 +326,7 @@ TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_DecodeError) { EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) .WillRepeatedly( - RunCallback<1>(Decryptor::kError, Decryptor::AudioFrames())); + RunOnceCallback<1>(Decryptor::kError, Decryptor::AudioFrames())); DecodeAndExpect(encrypted_buffer_, DecodeStatus::DECODE_ERROR); } @@ -347,7 +347,7 @@ TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_MultipleFrames) { decoded_frame_list_.push_back(frame_b); EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) - .WillOnce(RunCallback<1>(Decryptor::kSuccess, decoded_frame_list_)); + .WillOnce(RunOnceCallback<1>(Decryptor::kSuccess, decoded_frame_list_)); EXPECT_CALL(*this, FrameReady(decoded_frame_)); EXPECT_CALL(*this, FrameReady(frame_a)); @@ -413,7 +413,8 @@ TEST_F(DecryptingAudioDecoderTest, KeyAdded_DuringWaitingForKey) { EnterWaitingForKeyState(); EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kSuccess, decoded_frame_list_)); + .WillRepeatedly( + RunOnceCallback<1>(Decryptor::kSuccess, decoded_frame_list_)); EXPECT_CALL(*this, FrameReady(decoded_frame_)); EXPECT_CALL(*this, DecodeDone(IsOkStatus())); event_cb_.Run(CdmContext::Event::kHasAdditionalUsableKey); @@ -427,7 +428,8 @@ TEST_F(DecryptingAudioDecoderTest, KeyAdded_DruingPendingDecode) { EnterPendingDecodeState(); EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kSuccess, decoded_frame_list_)); + .WillRepeatedly( + RunOnceCallback<1>(Decryptor::kSuccess, decoded_frame_list_)); EXPECT_CALL(*this, FrameReady(decoded_frame_)); EXPECT_CALL(*this, DecodeDone(IsOkStatus())); // The audio decode callback is returned after the correct decryption key is diff --git a/chromium/media/filters/decrypting_video_decoder.cc b/chromium/media/filters/decrypting_video_decoder.cc index 43601b56c5d..548d9ba61b4 100644 --- a/chromium/media/filters/decrypting_video_decoder.cc +++ b/chromium/media/filters/decrypting_video_decoder.cc @@ -28,6 +28,10 @@ DecryptingVideoDecoder::DecryptingVideoDecoder( DETACH_FROM_SEQUENCE(sequence_checker_); } +VideoDecoderType DecryptingVideoDecoder::GetDecoderType() const { + return VideoDecoderType::kDecrypting; +} + std::string DecryptingVideoDecoder::GetDisplayName() const { return kDecoderName; } @@ -298,7 +302,7 @@ void DecryptingVideoDecoder::DeliverFrame(Decryptor::Status status, CHECK(frame); // Frame returned with kSuccess should not be an end-of-stream frame. - DCHECK(!frame->metadata()->end_of_stream); + DCHECK(!frame->metadata().end_of_stream); // If color space is not set, use the color space in the |config_|. if (!frame->ColorSpace().IsValid()) { diff --git a/chromium/media/filters/decrypting_video_decoder.h b/chromium/media/filters/decrypting_video_decoder.h index 6f7c92402ec..e8715db88be 100644 --- a/chromium/media/filters/decrypting_video_decoder.h +++ b/chromium/media/filters/decrypting_video_decoder.h @@ -41,6 +41,7 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder { bool SupportsDecryption() const override; // VideoDecoder implementation. + VideoDecoderType GetDecoderType() const override; std::string GetDisplayName() const override; void Initialize(const VideoDecoderConfig& config, bool low_delay, diff --git a/chromium/media/filters/decrypting_video_decoder_unittest.cc b/chromium/media/filters/decrypting_video_decoder_unittest.cc index 525fdb247f5..82cc59ed390 100644 --- a/chromium/media/filters/decrypting_video_decoder_unittest.cc +++ b/chromium/media/filters/decrypting_video_decoder_unittest.cc @@ -12,6 +12,7 @@ #include "base/run_loop.h" #include "base/stl_util.h" #include "base/test/gmock_callback_support.h" +#include "base/test/gmock_move_support.h" #include "base/test/task_environment.h" #include "media/base/decoder_buffer.h" #include "media/base/decrypt_config.h" @@ -22,12 +23,10 @@ #include "media/filters/decrypting_video_decoder.h" #include "testing/gmock/include/gmock/gmock.h" -using ::base::test::RunCallback; using ::base::test::RunOnceCallback; using ::testing::_; using ::testing::Invoke; using ::testing::Return; -using ::testing::SaveArg; using ::testing::StrictMock; using ::testing::WithArg; @@ -135,20 +134,20 @@ class DecryptingVideoDecoderTest : public testing::Test { // Helper function to simulate the decrypting and decoding process in the // |decryptor_| with a decoding delay of kDecodingDelay buffers. void DecryptAndDecodeVideo(scoped_refptr<DecoderBuffer> encrypted, - const Decryptor::VideoDecodeCB& video_decode_cb) { + Decryptor::VideoDecodeCB video_decode_cb) { num_decrypt_and_decode_calls_++; if (!encrypted->end_of_stream()) num_frames_in_decryptor_++; if (num_decrypt_and_decode_calls_ <= kDecodingDelay || num_frames_in_decryptor_ == 0) { - video_decode_cb.Run(Decryptor::kNeedMoreData, - scoped_refptr<VideoFrame>()); + std::move(video_decode_cb) + .Run(Decryptor::kNeedMoreData, scoped_refptr<VideoFrame>()); return; } num_frames_in_decryptor_--; - video_decode_cb.Run(Decryptor::kSuccess, decoded_video_frame_); + std::move(video_decode_cb).Run(Decryptor::kSuccess, decoded_video_frame_); } // Sets up expectations and actions to put DecryptingVideoDecoder in an @@ -176,7 +175,7 @@ class DecryptingVideoDecoderTest : public testing::Test { void EnterPendingDecodeState() { EXPECT_TRUE(!pending_video_decode_cb_); EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(encrypted_buffer_, _)) - .WillOnce(SaveArg<1>(&pending_video_decode_cb_)); + .WillOnce(MoveArg<1>(&pending_video_decode_cb_)); decoder_->Decode(encrypted_buffer_, base::BindOnce(&DecryptingVideoDecoderTest::DecodeDone, @@ -189,7 +188,8 @@ class DecryptingVideoDecoderTest : public testing::Test { void EnterWaitingForKeyState() { EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kNoKey, null_video_frame_)); + .WillRepeatedly( + RunOnceCallback<1>(Decryptor::kNoKey, null_video_frame_)); EXPECT_CALL(*this, OnWaiting(WaitingReason::kNoDecryptionKey)); decoder_->Decode(encrypted_buffer_, base::BindOnce(&DecryptingVideoDecoderTest::DecodeDone, @@ -319,8 +319,8 @@ TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_DecodeError) { Initialize(); EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kError, - scoped_refptr<VideoFrame>(nullptr))); + .WillRepeatedly(RunOnceCallback<1>(Decryptor::kError, + scoped_refptr<VideoFrame>(nullptr))); DecodeAndExpectError(encrypted_buffer_); @@ -343,7 +343,7 @@ TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringWaitingForKey) { EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) .WillRepeatedly( - RunCallback<1>(Decryptor::kSuccess, decoded_video_frame_)); + RunOnceCallback<1>(Decryptor::kSuccess, decoded_video_frame_)); EXPECT_CALL(*this, FrameReady(decoded_video_frame_)); EXPECT_CALL(*this, DecodeDone(IsOkStatus())); event_cb_.Run(CdmContext::Event::kHasAdditionalUsableKey); @@ -358,7 +358,7 @@ TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringPendingDecode) { EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) .WillRepeatedly( - RunCallback<1>(Decryptor::kSuccess, decoded_video_frame_)); + RunOnceCallback<1>(Decryptor::kSuccess, decoded_video_frame_)); EXPECT_CALL(*this, FrameReady(decoded_video_frame_)); EXPECT_CALL(*this, DecodeDone(IsOkStatus())); // The video decode callback is returned after the correct decryption key is diff --git a/chromium/media/filters/demuxer_perftest.cc b/chromium/media/filters/demuxer_perftest.cc index d444f4d06f9..3bb2619c3e8 100644 --- a/chromium/media/filters/demuxer_perftest.cc +++ b/chromium/media/filters/demuxer_perftest.cc @@ -45,10 +45,10 @@ class DemuxerHostImpl : public media::DemuxerHost { DISALLOW_COPY_AND_ASSIGN(DemuxerHostImpl); }; -static void QuitLoopWithStatus(base::Closure quit_cb, +static void QuitLoopWithStatus(base::OnceClosure quit_cb, media::PipelineStatus status) { CHECK_EQ(status, media::PIPELINE_OK); - quit_cb.Run(); + std::move(quit_cb).Run(); } static void OnEncryptedMediaInitData(EmeInitDataType init_data_type, @@ -81,7 +81,7 @@ class StreamReader { private: void OnReadDone(scoped_refptr<base::SingleThreadTaskRunner> task_runner, - const base::Closure& quit_when_idle_closure, + base::OnceClosure quit_when_idle_closure, bool* end_of_stream, base::TimeDelta* timestamp, media::DemuxerStream::Status status, @@ -139,7 +139,7 @@ bool StreamReader::IsDone() { void StreamReader::OnReadDone( scoped_refptr<base::SingleThreadTaskRunner> task_runner, - const base::Closure& quit_when_idle_closure, + base::OnceClosure quit_when_idle_closure, bool* end_of_stream, base::TimeDelta* timestamp, media::DemuxerStream::Status status, @@ -148,7 +148,7 @@ void StreamReader::OnReadDone( CHECK(buffer); *end_of_stream = buffer->end_of_stream(); *timestamp = *end_of_stream ? media::kNoTimestamp : buffer->timestamp(); - task_runner->PostTask(FROM_HERE, quit_when_idle_closure); + task_runner->PostTask(FROM_HERE, std::move(quit_when_idle_closure)); } int StreamReader::GetNextStreamIndexToRead() { diff --git a/chromium/media/filters/fake_video_decoder.cc b/chromium/media/filters/fake_video_decoder.cc index e2f4726f28f..4e8593fd530 100644 --- a/chromium/media/filters/fake_video_decoder.cc +++ b/chromium/media/filters/fake_video_decoder.cc @@ -68,6 +68,10 @@ std::string FakeVideoDecoder::GetDisplayName() const { return decoder_name_; } +VideoDecoderType FakeVideoDecoder::GetDecoderType() const { + return VideoDecoderType::kUnknown; +} + void FakeVideoDecoder::Initialize(const VideoDecoderConfig& config, bool low_delay, CdmContext* cdm_context, diff --git a/chromium/media/filters/fake_video_decoder.h b/chromium/media/filters/fake_video_decoder.h index 898f4dab778..45e920a9496 100644 --- a/chromium/media/filters/fake_video_decoder.h +++ b/chromium/media/filters/fake_video_decoder.h @@ -49,6 +49,7 @@ class FakeVideoDecoder : public VideoDecoder { bool SupportsDecryption() const override; bool IsPlatformDecoder() const override; std::string GetDisplayName() const override; + VideoDecoderType GetDecoderType() const override; // VideoDecoder implementation void Initialize(const VideoDecoderConfig& config, diff --git a/chromium/media/filters/fake_video_decoder_unittest.cc b/chromium/media/filters/fake_video_decoder_unittest.cc index 49130158ce1..58baa499a53 100644 --- a/chromium/media/filters/fake_video_decoder_unittest.cc +++ b/chromium/media/filters/fake_video_decoder_unittest.cc @@ -91,7 +91,7 @@ class FakeVideoDecoderTest } void FrameReady(scoped_refptr<VideoFrame> frame) { - DCHECK(!frame->metadata()->end_of_stream); + DCHECK(!frame->metadata().end_of_stream); last_decoded_frame_ = std::move(frame); num_decoded_frames_++; } diff --git a/chromium/media/filters/ffmpeg_audio_decoder.cc b/chromium/media/filters/ffmpeg_audio_decoder.cc index e5daea40328..7ff037ac847 100644 --- a/chromium/media/filters/ffmpeg_audio_decoder.cc +++ b/chromium/media/filters/ffmpeg_audio_decoder.cc @@ -69,6 +69,10 @@ std::string FFmpegAudioDecoder::GetDisplayName() const { return "FFmpegAudioDecoder"; } +AudioDecoderType FFmpegAudioDecoder::GetDecoderType() const { + return AudioDecoderType::kFFmpeg; +} + void FFmpegAudioDecoder::Initialize(const AudioDecoderConfig& config, CdmContext* /* cdm_context */, InitCB init_cb, diff --git a/chromium/media/filters/ffmpeg_audio_decoder.h b/chromium/media/filters/ffmpeg_audio_decoder.h index 2111b89a797..5b3d20380e7 100644 --- a/chromium/media/filters/ffmpeg_audio_decoder.h +++ b/chromium/media/filters/ffmpeg_audio_decoder.h @@ -40,6 +40,7 @@ class MEDIA_EXPORT FFmpegAudioDecoder : public AudioDecoder { ~FFmpegAudioDecoder() override; // AudioDecoder implementation. + AudioDecoderType GetDecoderType() const override; std::string GetDisplayName() const override; void Initialize(const AudioDecoderConfig& config, CdmContext* cdm_context, diff --git a/chromium/media/filters/ffmpeg_demuxer.cc b/chromium/media/filters/ffmpeg_demuxer.cc index fa39c5698ed..5d8134ceaa6 100644 --- a/chromium/media/filters/ffmpeg_demuxer.cc +++ b/chromium/media/filters/ffmpeg_demuxer.cc @@ -12,6 +12,7 @@ #include "base/base64.h" #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/feature_list.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_functions.h" @@ -32,6 +33,7 @@ #include "media/base/decrypt_config.h" #include "media/base/demuxer_memory_limit.h" #include "media/base/limits.h" +#include "media/base/media_switches.h" #include "media/base/media_tracks.h" #include "media/base/media_types.h" #include "media/base/sample_rates.h" @@ -214,6 +216,17 @@ std::unique_ptr<FFmpegDemuxerStream> FFmpegDemuxerStream::Create( std::unique_ptr<AudioDecoderConfig> audio_config; std::unique_ptr<VideoDecoderConfig> video_config; +#if BUILDFLAG(IS_CHROMEOS_ASH) + if (base::FeatureList::IsEnabled(kDeprecateLowUsageCodecs)) { + const auto codec_id = stream->codecpar->codec_id; + if (codec_id == AV_CODEC_ID_AMR_NB || codec_id == AV_CODEC_ID_AMR_WB || + codec_id == AV_CODEC_ID_GSM_MS) { + MEDIA_LOG(ERROR, media_log) << "AMR and GSM are deprecated on ChromeOS."; + return nullptr; + } + } +#endif + if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audio_config = std::make_unique<AudioDecoderConfig>(); diff --git a/chromium/media/filters/ffmpeg_demuxer_unittest.cc b/chromium/media/filters/ffmpeg_demuxer_unittest.cc index 5bdd717becc..698990ed696 100644 --- a/chromium/media/filters/ffmpeg_demuxer_unittest.cc +++ b/chromium/media/filters/ffmpeg_demuxer_unittest.cc @@ -779,7 +779,7 @@ TEST_F(FFmpegDemuxerTest, Read_AudioNegativeStartTimeAndOpusDiscard_Sync) { #if BUILDFLAG(USE_PROPRIETARY_CODECS) -#if BUILDFLAG(IS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) TEST_F(FFmpegDemuxerTest, TestAudioNegativeTimestamps) { // Note: This test will _crash_ the browser if negative timestamp // values are skipped, since this file is heavily truncated to avoid @@ -795,7 +795,7 @@ TEST_F(FFmpegDemuxerTest, TestAudioNegativeTimestamps) { Read(audio, FROM_HERE, 104, 77619, true); Read(audio, FROM_HERE, 104, 103492, true); } -#endif // BUILDFLAG(IS_ASH) +#endif // BUILDFLAG(IS_CHROMEOS_ASH) // Similar to the test above, but using an opus clip plus h264 b-frames to // ensure we don't apply chained ogg workarounds to other content. @@ -1653,10 +1653,10 @@ TEST_F(FFmpegDemuxerTest, Seek_FallbackToDisabledAudioStream) { } namespace { -void QuitLoop(base::Closure quit_closure, +void QuitLoop(base::OnceClosure quit_closure, DemuxerStream::Type type, const std::vector<DemuxerStream*>& streams) { - quit_closure.Run(); + std::move(quit_closure).Run(); } void DisableAndEnableDemuxerTracks( diff --git a/chromium/media/filters/ffmpeg_glue_unittest.cc b/chromium/media/filters/ffmpeg_glue_unittest.cc index 53db86c0dc8..e37437d683b 100644 --- a/chromium/media/filters/ffmpeg_glue_unittest.cc +++ b/chromium/media/filters/ffmpeg_glue_unittest.cc @@ -316,7 +316,7 @@ TEST_F(FFmpegGlueContainerTest, AAC) { ExpectContainer(container_names::CONTAINER_AAC); } -#if BUILDFLAG(IS_ASH) +#if BUILDFLAG(IS_CHROMEOS_ASH) TEST_F(FFmpegGlueContainerTest, AVI) { InitializeAndOpen("bear.avi"); ExpectContainer(container_names::CONTAINER_AVI); @@ -326,7 +326,7 @@ TEST_F(FFmpegGlueContainerTest, AMR) { InitializeAndOpen("bear.amr"); ExpectContainer(container_names::CONTAINER_AMR); } -#endif // BUILDFLAG(IS_ASH) +#endif // BUILDFLAG(IS_CHROMEOS_ASH) #endif // BUILDFLAG(USE_PROPRIETARY_CODECS) // Probe something unsupported to ensure we fall back to the our internal guess. diff --git a/chromium/media/filters/ffmpeg_video_decoder.cc b/chromium/media/filters/ffmpeg_video_decoder.cc index d703189571a..6e4abe2ae76 100644 --- a/chromium/media/filters/ffmpeg_video_decoder.cc +++ b/chromium/media/filters/ffmpeg_video_decoder.cc @@ -83,6 +83,30 @@ bool FFmpegVideoDecoder::IsCodecSupported(VideoCodec codec) { return avcodec_find_decoder(VideoCodecToCodecID(codec)) != nullptr; } +// static +SupportedVideoDecoderConfigs FFmpegVideoDecoder::SupportedConfigsForWebRTC() { + SupportedVideoDecoderConfigs supported_configs; + + if (IsCodecSupported(kCodecH264)) { + supported_configs.emplace_back(/*profile_min=*/H264PROFILE_BASELINE, + /*profile_max=*/H264PROFILE_HIGH, + /*coded_size_min=*/kDefaultSwDecodeSizeMin, + /*coded_size_max=*/kDefaultSwDecodeSizeMax, + /*allow_encrypted=*/false, + /*require_encrypted=*/false); + } + if (IsCodecSupported(kCodecVP8)) { + supported_configs.emplace_back(/*profile_min=*/VP8PROFILE_ANY, + /*profile_max=*/VP8PROFILE_ANY, + /*coded_size_min=*/kDefaultSwDecodeSizeMin, + /*coded_size_max=*/kDefaultSwDecodeSizeMax, + /*allow_encrypted=*/false, + /*require_encrypted=*/false); + } + + return supported_configs; +} + FFmpegVideoDecoder::FFmpegVideoDecoder(MediaLog* media_log) : media_log_(media_log), state_(kUninitialized), decode_nalus_(false) { DVLOG(1) << __func__; @@ -161,6 +185,13 @@ int FFmpegVideoDecoder::GetVideoBuffer(struct AVCodecContext* codec_context, if (codec_context->color_range == AVCOL_RANGE_JPEG) { video_frame->set_color_space(gfx::ColorSpace::CreateJpeg()); } + } else if (codec_context->codec_id == AV_CODEC_ID_H264 && + codec_context->colorspace == AVCOL_SPC_RGB && + format == PIXEL_FORMAT_I420) { + // Some H.264 videos contain a VUI that specifies a color matrix of GBR, + // when they are actually ordinary YUV. Only 4:2:0 formats are checked, + // because GBR is reasonable for 4:4:4 content. See crbug.com/1067377. + video_frame->set_color_space(gfx::ColorSpace::CreateREC709()); } else if (codec_context->color_primaries != AVCOL_PRI_UNSPECIFIED || codec_context->color_trc != AVCOL_TRC_UNSPECIFIED || codec_context->colorspace != AVCOL_SPC_UNSPECIFIED) { @@ -196,6 +227,10 @@ int FFmpegVideoDecoder::GetVideoBuffer(struct AVCodecContext* codec_context, return 0; } +VideoDecoderType FFmpegVideoDecoder::GetDecoderType() const { + return VideoDecoderType::kFFmpeg; +} + std::string FFmpegVideoDecoder::GetDisplayName() const { return "FFmpegVideoDecoder"; } @@ -359,7 +394,7 @@ bool FFmpegVideoDecoder::OnNewFrame(AVFrame* frame) { reinterpret_cast<VideoFrame*>(av_buffer_get_opaque(frame->buf[0])); video_frame->set_timestamp( base::TimeDelta::FromMicroseconds(frame->reordered_opaque)); - video_frame->metadata()->power_efficient = false; + video_frame->metadata().power_efficient = false; output_cb_.Run(video_frame); return true; } diff --git a/chromium/media/filters/ffmpeg_video_decoder.h b/chromium/media/filters/ffmpeg_video_decoder.h index 4ea30459b48..de1ff5390d0 100644 --- a/chromium/media/filters/ffmpeg_video_decoder.h +++ b/chromium/media/filters/ffmpeg_video_decoder.h @@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/sequence_checker.h" +#include "media/base/supported_video_decoder_config.h" #include "media/base/video_decoder.h" #include "media/base/video_decoder_config.h" #include "media/base/video_frame_pool.h" @@ -29,6 +30,7 @@ class MediaLog; class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder { public: static bool IsCodecSupported(VideoCodec codec); + static SupportedVideoDecoderConfigs SupportedConfigsForWebRTC(); explicit FFmpegVideoDecoder(MediaLog* media_log); ~FFmpegVideoDecoder() override; @@ -38,6 +40,7 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder { void set_decode_nalus(bool decode_nalus) { decode_nalus_ = decode_nalus; } // VideoDecoder implementation. + VideoDecoderType GetDecoderType() const override; std::string GetDisplayName() const override; void Initialize(const VideoDecoderConfig& config, bool low_delay, diff --git a/chromium/media/filters/ffmpeg_video_decoder_unittest.cc b/chromium/media/filters/ffmpeg_video_decoder_unittest.cc index 599fb12c7fe..3a07a387ad1 100644 --- a/chromium/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/chromium/media/filters/ffmpeg_video_decoder_unittest.cc @@ -197,7 +197,7 @@ class FFmpegVideoDecoderTest : public testing::Test { } void FrameReady(scoped_refptr<VideoFrame> frame) { - DCHECK(!frame->metadata()->end_of_stream); + DCHECK(!frame->metadata().end_of_stream); output_frames_.push_back(std::move(frame)); } diff --git a/chromium/media/filters/fuchsia/fuchsia_video_decoder.cc b/chromium/media/filters/fuchsia/fuchsia_video_decoder.cc index d17b79cb657..76149fddb7b 100644 --- a/chromium/media/filters/fuchsia/fuchsia_video_decoder.cc +++ b/chromium/media/filters/fuchsia/fuchsia_video_decoder.cc @@ -123,7 +123,7 @@ class OutputMailbox { coded_size, visible_rect, natural_size, timestamp); // Request a fence we'll wait on before reusing the buffer. - frame->metadata()->read_lock_fences_enabled = true; + frame->metadata().read_lock_fences_enabled = true; return frame; } @@ -134,7 +134,7 @@ class OutputMailbox { // The mailbox is referenced by a VideoFrame. It will be deleted as soon // as the frame is destroyed. DCHECK(reuse_callback_); - reuse_callback_ = base::Closure(); + reuse_callback_ = base::OnceClosure(); } else { delete this; } @@ -197,6 +197,7 @@ class FuchsiaVideoDecoder : public VideoDecoder, bool IsPlatformDecoder() const override; bool SupportsDecryption() const override; std::string GetDisplayName() const override; + VideoDecoderType GetDecoderType() const override; // VideoDecoder implementation. void Initialize(const VideoDecoderConfig& config, @@ -334,6 +335,7 @@ FuchsiaVideoDecoder::FuchsiaVideoDecoder( enable_sw_decoding_(enable_sw_decoding), use_overlays_for_video_(base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseOverlaysForVideo)), + sysmem_allocator_("CrFuchsiaVideoDecoder"), client_native_pixmap_factory_(ui::CreateClientNativePixmapFactoryOzone()), weak_factory_(this) { DCHECK(raster_context_provider_); @@ -361,6 +363,10 @@ std::string FuchsiaVideoDecoder::GetDisplayName() const { return "FuchsiaVideoDecoder"; } +VideoDecoderType FuchsiaVideoDecoder::GetDecoderType() const { + return VideoDecoderType::kFuchsia; +} + void FuchsiaVideoDecoder::Initialize(const VideoDecoderConfig& config, bool low_delay, CdmContext* cdm_context, @@ -684,8 +690,9 @@ void FuchsiaVideoDecoder::SendInputPacket( DCHECK(in_flight_input_packets_.find(packet.buffer_index()) == in_flight_input_packets_.end()); + const size_t buffer_index = packet.buffer_index(); in_flight_input_packets_.insert_or_assign( - packet.buffer_index(), InputDecoderPacket{std::move(packet)}); + buffer_index, InputDecoderPacket{std::move(packet)}); } void FuchsiaVideoDecoder::ProcessEndOfStream() { @@ -765,14 +772,18 @@ void FuchsiaVideoDecoder::OnOutputConstraints( fuchsia::sysmem::BufferCollectionTokenPtr collection_token; sysmem_allocator_.raw()->AllocateSharedCollection( collection_token.NewRequest()); + collection_token->SetName(100u, "ChromiumVideoDecoderOutput"); + collection_token->SetDebugClientInfo("chromium_video_decoder", 0u); // Create sysmem tokens for the gpu process and the codec. fuchsia::sysmem::BufferCollectionTokenPtr collection_token_for_codec; collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, collection_token_for_codec.NewRequest()); + collection_token_for_codec->SetDebugClientInfo("codec", 0u); fuchsia::sysmem::BufferCollectionTokenPtr collection_token_for_gpu; collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, collection_token_for_gpu.NewRequest()); + collection_token_for_gpu->SetDebugClientInfo("chromium_gpu", 0u); // Convert the token to a BufferCollection connection. sysmem_allocator_.raw()->BindSharedCollection( @@ -944,11 +955,11 @@ void FuchsiaVideoDecoder::OnOutputPacket(fuchsia::media::Packet output_packet, // codec may still decode on hardware even when |enable_sw_decoding_| is set // (i.e. power_efficient flag would not be set correctly in that case). It // doesn't matter because software decoders can be enabled only for tests. - frame->metadata()->power_efficient = !enable_sw_decoding_; + frame->metadata().power_efficient = !enable_sw_decoding_; // Allow this video frame to be promoted as an overlay, because it was // registered with an ImagePipe. - frame->metadata()->allow_overlay = use_overlays_for_video_; + frame->metadata().allow_overlay = use_overlays_for_video_; output_cb_.Run(std::move(frame)); } diff --git a/chromium/media/filters/fuchsia/fuchsia_video_decoder_unittest.cc b/chromium/media/filters/fuchsia/fuchsia_video_decoder_unittest.cc index 5e4c36998e3..2b319eae494 100644 --- a/chromium/media/filters/fuchsia/fuchsia_video_decoder_unittest.cc +++ b/chromium/media/filters/fuchsia/fuchsia_video_decoder_unittest.cc @@ -13,6 +13,7 @@ #include "base/containers/flat_set.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/process_context.h" +#include "base/process/process_handle.h" #include "base/test/bind.h" #include "base/test/task_environment.h" #include "components/viz/common/gpu/raster_context_provider.h" @@ -24,6 +25,7 @@ #include "media/base/video_decoder.h" #include "media/base/video_frame.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/gpu_fence.h" #include "ui/gfx/gpu_memory_buffer.h" namespace media { @@ -40,6 +42,8 @@ class TestBufferCollection { ZX_LOG(FATAL, status) << "The fuchsia.sysmem.Allocator channel was terminated."; }); + sysmem_allocator_->SetDebugClientInfo("CrTestBufferCollection", + base::GetCurrentProcId()); sysmem_allocator_->BindSharedCollection( fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>( @@ -212,8 +216,8 @@ class TestRasterContextProvider TestRasterContextProvider(TestRasterContextProvider&) = delete; TestRasterContextProvider& operator=(TestRasterContextProvider&) = delete; - void SetOnDestroyedClosure(base::Closure on_destroyed) { - on_destroyed_ = on_destroyed; + void SetOnDestroyedClosure(base::OnceClosure on_destroyed) { + on_destroyed_ = std::move(on_destroyed); } // viz::RasterContextProvider implementation; @@ -277,7 +281,7 @@ class TestRasterContextProvider TestSharedImageInterface shared_image_interface_; viz::TestContextSupport gpu_context_support_; - base::Closure on_destroyed_; + base::OnceClosure on_destroyed_; }; } // namespace diff --git a/chromium/media/filters/gav1_video_decoder.cc b/chromium/media/filters/gav1_video_decoder.cc index 8e0e69c3aac..729c562c123 100644 --- a/chromium/media/filters/gav1_video_decoder.cc +++ b/chromium/media/filters/gav1_video_decoder.cc @@ -224,13 +224,23 @@ scoped_refptr<VideoFrame> FormatVideoFrame( color_space = container_color_space; frame->set_color_space(color_space.ToGfxColorSpace()); - frame->metadata()->power_efficient = false; + frame->metadata().power_efficient = false; return frame; } } // namespace +// static +SupportedVideoDecoderConfigs Gav1VideoDecoder::SupportedConfigs() { + return {{/*profile_min=*/AV1PROFILE_PROFILE_MAIN, + /*profile_max=*/AV1PROFILE_PROFILE_HIGH, + /*coded_size_min=*/kDefaultSwDecodeSizeMin, + /*coded_size_max=*/kDefaultSwDecodeSizeMax, + /*allow_encrypted=*/false, + /*require_encrypted=*/false}}; +} + Gav1VideoDecoder::Gav1VideoDecoder(MediaLog* media_log, OffloadState offload_state) : media_log_(media_log), @@ -247,6 +257,10 @@ std::string Gav1VideoDecoder::GetDisplayName() const { return "Gav1VideoDecoder"; } +VideoDecoderType Gav1VideoDecoder::GetDecoderType() const { + return VideoDecoderType::kGav1; +} + void Gav1VideoDecoder::Initialize(const VideoDecoderConfig& config, bool low_delay, CdmContext* /* cdm_context */, diff --git a/chromium/media/filters/gav1_video_decoder.h b/chromium/media/filters/gav1_video_decoder.h index 22cd0053923..b4e78e07f04 100644 --- a/chromium/media/filters/gav1_video_decoder.h +++ b/chromium/media/filters/gav1_video_decoder.h @@ -15,6 +15,7 @@ #include "base/memory/scoped_refptr.h" #include "base/sequence_checker.h" #include "media/base/media_export.h" +#include "media/base/supported_video_decoder_config.h" #include "media/base/video_decoder_config.h" #include "media/base/video_frame_pool.h" #include "media/filters/offloading_video_decoder.h" @@ -28,6 +29,8 @@ class MediaLog; class MEDIA_EXPORT Gav1VideoDecoder : public OffloadableVideoDecoder { public: + static SupportedVideoDecoderConfigs SupportedConfigs(); + explicit Gav1VideoDecoder(MediaLog* media_log, OffloadState offload_state = OffloadState::kNormal); ~Gav1VideoDecoder() override; @@ -36,6 +39,7 @@ class MEDIA_EXPORT Gav1VideoDecoder : public OffloadableVideoDecoder { // VideoDecoder implementation. std::string GetDisplayName() const override; + VideoDecoderType GetDecoderType() const override; void Initialize(const VideoDecoderConfig& config, bool low_delay, CdmContext* cdm_context, diff --git a/chromium/media/filters/gav1_video_decoder_unittest.cc b/chromium/media/filters/gav1_video_decoder_unittest.cc index 4169f610d4f..33ccf13e2c4 100644 --- a/chromium/media/filters/gav1_video_decoder_unittest.cc +++ b/chromium/media/filters/gav1_video_decoder_unittest.cc @@ -194,7 +194,7 @@ class Gav1VideoDecoderTest : public testing::Test { } void FrameReady(scoped_refptr<VideoFrame> frame) { - DCHECK(!frame->metadata()->end_of_stream); + DCHECK(!frame->metadata().end_of_stream); output_frames_.push_back(std::move(frame)); } diff --git a/chromium/media/filters/memory_data_source.h b/chromium/media/filters/memory_data_source.h index 716cb776157..d78dee1530a 100644 --- a/chromium/media/filters/memory_data_source.h +++ b/chromium/media/filters/memory_data_source.h @@ -41,7 +41,10 @@ class MEDIA_EXPORT MemoryDataSource final : public DataSource { const uint8_t* data_ = nullptr; const size_t size_ = 0; - bool is_stopped_ = false; + // Stop may be called from the render thread while this class is being used by + // the media thread. It's harmless if we fulfill a read after Stop() has been + // called, so an atomic without a lock is safe. + std::atomic<bool> is_stopped_{false}; DISALLOW_COPY_AND_ASSIGN(MemoryDataSource); }; diff --git a/chromium/media/filters/offloading_video_decoder.cc b/chromium/media/filters/offloading_video_decoder.cc index 242190f8009..c5379ebf0ec 100644 --- a/chromium/media/filters/offloading_video_decoder.cc +++ b/chromium/media/filters/offloading_video_decoder.cc @@ -75,6 +75,11 @@ OffloadingVideoDecoder::~OffloadingVideoDecoder() { offload_task_runner_->DeleteSoon(FROM_HERE, std::move(helper_)); } +VideoDecoderType OffloadingVideoDecoder::GetDecoderType() const { + // This call is expected to be static and safe to call from any thread. + return helper_->decoder()->GetDecoderType(); +} + std::string OffloadingVideoDecoder::GetDisplayName() const { // This call is expected to be static and safe to call from any thread. return helper_->decoder()->GetDisplayName(); diff --git a/chromium/media/filters/offloading_video_decoder.h b/chromium/media/filters/offloading_video_decoder.h index 0ff14bb3d45..5caf47895f8 100644 --- a/chromium/media/filters/offloading_video_decoder.h +++ b/chromium/media/filters/offloading_video_decoder.h @@ -88,6 +88,7 @@ class MEDIA_EXPORT OffloadingVideoDecoder : public VideoDecoder { ~OffloadingVideoDecoder() override; // VideoDecoder implementation. + VideoDecoderType GetDecoderType() const override; std::string GetDisplayName() const override; void Initialize(const VideoDecoderConfig& config, bool low_delay, diff --git a/chromium/media/filters/offloading_video_decoder_unittest.cc b/chromium/media/filters/offloading_video_decoder_unittest.cc index 29813ad3120..b6d2abf4971 100644 --- a/chromium/media/filters/offloading_video_decoder_unittest.cc +++ b/chromium/media/filters/offloading_video_decoder_unittest.cc @@ -39,6 +39,11 @@ class MockOffloadableVideoDecoder : public OffloadableVideoDecoder { std::string GetDisplayName() const override { return "MockOffloadableVideoDecoder"; } + + VideoDecoderType GetDecoderType() const override { + return VideoDecoderType::kUnknown; + } + void Initialize(const VideoDecoderConfig& config, bool low_delay, CdmContext* cdm_context, diff --git a/chromium/media/filters/pipeline_controller.cc b/chromium/media/filters/pipeline_controller.cc index 97e430651a7..68b39ebe31a 100644 --- a/chromium/media/filters/pipeline_controller.cc +++ b/chromium/media/filters/pipeline_controller.cc @@ -393,6 +393,10 @@ void PipelineController::SetPreservesPitch(bool preserves_pitch) { pipeline_->SetPreservesPitch(preserves_pitch); } +void PipelineController::SetAutoplayInitiated(bool autoplay_initiated) { + pipeline_->SetAutoplayInitiated(autoplay_initiated); +} + base::TimeDelta PipelineController::GetMediaTime() const { return pipeline_->GetMediaTime(); } diff --git a/chromium/media/filters/pipeline_controller.h b/chromium/media/filters/pipeline_controller.h index 08db9dcf214..a77ab00085b 100644 --- a/chromium/media/filters/pipeline_controller.h +++ b/chromium/media/filters/pipeline_controller.h @@ -133,6 +133,7 @@ class MEDIA_EXPORT PipelineController { void SetVolume(float volume); void SetLatencyHint(base::Optional<base::TimeDelta> latency_hint); void SetPreservesPitch(bool preserves_pitch); + void SetAutoplayInitiated(bool autoplay_initiated); base::TimeDelta GetMediaTime() const; Ranges<base::TimeDelta> GetBufferedTimeRanges() const; base::TimeDelta GetMediaDuration() const; diff --git a/chromium/media/filters/pipeline_controller_unittest.cc b/chromium/media/filters/pipeline_controller_unittest.cc index 0bced42367d..390dbc69409 100644 --- a/chromium/media/filters/pipeline_controller_unittest.cc +++ b/chromium/media/filters/pipeline_controller_unittest.cc @@ -152,8 +152,8 @@ class PipelineControllerTest : public ::testing::Test, public Pipeline::Client { void OnVideoOpacityChange(bool opaque) override {} void OnVideoFrameRateChange(base::Optional<int>) override {} void OnVideoAverageKeyframeDistanceUpdate() override {} - void OnAudioDecoderChange(const PipelineDecoderInfo& info) override {} - void OnVideoDecoderChange(const PipelineDecoderInfo& info) override {} + void OnAudioDecoderChange(const AudioDecoderInfo& info) override {} + void OnVideoDecoderChange(const VideoDecoderInfo& info) override {} base::test::SingleThreadTaskEnvironment task_environment_; diff --git a/chromium/media/filters/source_buffer_state.cc b/chromium/media/filters/source_buffer_state.cc index 9ce7a5934b6..3b4c9e16f79 100644 --- a/chromium/media/filters/source_buffer_state.cc +++ b/chromium/media/filters/source_buffer_state.cc @@ -16,6 +16,7 @@ #include "media/filters/chunk_demuxer.h" #include "media/filters/frame_processor.h" #include "media/filters/source_buffer_stream.h" +#include "media/media_buildflags.h" namespace media { @@ -214,8 +215,8 @@ bool SourceBufferState::Append(const uint8_t* data, append_window_end_during_append_ = append_window_end; timestamp_offset_during_append_ = timestamp_offset; - // TODO(wolenetz/acolwell): Curry and pass a NewBuffersCB here bound with - // append window and timestamp offset pointer. See http://crbug.com/351454. + // TODO(wolenetz): Curry and pass a NewBuffersCB here bound with append window + // and timestamp offset pointer. See http://crbug.com/351454. bool result = stream_parser_->Parse(data, length); if (!result) { MEDIA_LOG(ERROR, media_log_) @@ -229,6 +230,31 @@ bool SourceBufferState::Append(const uint8_t* data, return result; } +bool SourceBufferState::AppendChunks( + std::unique_ptr<StreamParser::BufferQueue> buffer_queue, + TimeDelta append_window_start, + TimeDelta append_window_end, + TimeDelta* timestamp_offset) { + append_in_progress_ = true; + DCHECK(timestamp_offset); + DCHECK(!timestamp_offset_during_append_); + append_window_start_during_append_ = append_window_start; + append_window_end_during_append_ = append_window_end; + timestamp_offset_during_append_ = timestamp_offset; + + // TODO(wolenetz): Curry and pass a NewBuffersCB here bound with append window + // and timestamp offset pointer. See http://crbug.com/351454. + bool result = stream_parser_->ProcessChunks(std::move(buffer_queue)); + if (!result) { + MEDIA_LOG(ERROR, media_log_) + << __func__ << ": Processing encoded chunks for buffering failed."; + } + + timestamp_offset_during_append_ = nullptr; + append_in_progress_ = false; + return result; +} + void SourceBufferState::ResetParserState(TimeDelta append_window_start, TimeDelta append_window_end, base::TimeDelta* timestamp_offset) { @@ -689,6 +715,31 @@ bool SourceBufferState::OnNewConfigs( << " config: " << video_config.AsHumanReadableString(); DCHECK(video_config.IsValidConfig()); + if (video_config.codec() == kCodecHEVC) { +#if BUILDFLAG(ENABLE_PLATFORM_HEVC) +#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA) + // On ChromeOS, HEVC is only supported through EME, so require the + // config to be for an encrypted track if on ChromeOS. Even so, + // conditionally allow clear HEVC on ChromeOS if cmdline has test + // override. + if (video_config.encryption_scheme() == + EncryptionScheme::kUnencrypted && + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableClearHevcForTesting)) { + MEDIA_LOG(ERROR, media_log_) + << "MSE playback of HEVC on ChromeOS is only supported via " + "platform decryptor, but the provided HEVC track is not " + "encrypted."; + return false; + } +#endif // BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA) +#else + NOTREACHED() + << "MSE parser must not emit HEVC tracks on build configurations " + "that do not support HEVC playback via platform."; +#endif // BUILDFLAG(ENABLE_PLATFORM_HEVC) + } + const auto& it = std::find(expected_vcodecs.begin(), expected_vcodecs.end(), video_config.codec()); if (it == expected_vcodecs.end()) { diff --git a/chromium/media/filters/source_buffer_state.h b/chromium/media/filters/source_buffer_state.h index ae4c76abbc2..fdf6be2f9c8 100644 --- a/chromium/media/filters/source_buffer_state.h +++ b/chromium/media/filters/source_buffer_state.h @@ -62,11 +62,16 @@ class MEDIA_EXPORT SourceBufferState { // append. |append_window_start| and |append_window_end| correspond to the MSE // spec's similarly named source buffer attributes that are used in coded // frame processing. + // AppendChunks appends the provided BufferQueue. bool Append(const uint8_t* data, size_t length, TimeDelta append_window_start, TimeDelta append_window_end, TimeDelta* timestamp_offset); + bool AppendChunks(std::unique_ptr<StreamParser::BufferQueue> buffer_queue, + TimeDelta append_window_start, + TimeDelta append_window_end, + TimeDelta* timestamp_offset); // Aborts the current append sequence and resets the parser. void ResetParserState(TimeDelta append_window_start, diff --git a/chromium/media/filters/stream_parser_factory.cc b/chromium/media/filters/stream_parser_factory.cc index 126c1814a16..29fb715ec7e 100644 --- a/chromium/media/filters/stream_parser_factory.cc +++ b/chromium/media/filters/stream_parser_factory.cc @@ -14,13 +14,16 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "build/build_config.h" +#include "media/base/audio_decoder_config.h" #include "media/base/media.h" #include "media/base/media_switches.h" #include "media/base/media_util.h" #include "media/base/video_codecs.h" +#include "media/base/video_decoder_config.h" #include "media/formats/mp4/mp4_stream_parser.h" #include "media/formats/mpeg/adts_stream_parser.h" #include "media/formats/mpeg/mpeg1_audio_stream_parser.h" +#include "media/formats/webcodecs/webcodecs_encoded_chunk_stream_parser.h" #include "media/formats/webm/webm_stream_parser.h" #include "media/media_buildflags.h" @@ -517,6 +520,7 @@ static SupportsType CheckTypeAndCodecs( return IsNotSupported; } +// static SupportsType StreamParserFactory::IsTypeSupported( const std::string& type, const std::vector<std::string>& codecs) { @@ -526,6 +530,7 @@ SupportsType StreamParserFactory::IsTypeSupported( nullptr); } +// static std::unique_ptr<StreamParser> StreamParserFactory::Create( const std::string& type, const std::vector<std::string>& codecs, @@ -535,18 +540,19 @@ std::unique_ptr<StreamParser> StreamParserFactory::Create( std::vector<CodecInfo::HistogramTag> audio_codecs; std::vector<CodecInfo::HistogramTag> video_codecs; - if (IsSupported == CheckTypeAndCodecs(type, codecs, media_log, - &factory_function, &audio_codecs, - &video_codecs)) { + // TODO(crbug.com/535738): Relax the requirement for specific codecs (allow + // MayBeSupported here), and relocate the logging to the parser configuration + // callback. This creation method is called in AddId(), and also in + // CanChangeType() and ChangeType(), so potentially overlogs codecs leading to + // disproportion versus actually parsed codec configurations from + // initialization segments. For this work and also recording when implicit + // codec switching occurs (without explicit ChangeType), see + // https://crbug.com/535738. + SupportsType supportsType = CheckTypeAndCodecs( + type, codecs, media_log, &factory_function, &audio_codecs, &video_codecs); + + if (IsSupported == supportsType) { // Log the expected codecs. - // TODO(wolenetz): Relax the requirement for specific codecs (allow - // MayBeSupported here), and relocate the logging to the parser - // configuration callback. This creation method is called in AddId(), and - // also in CanChangeType() and ChangeType(), so potentially overlogs codecs - // leading to disproportion versus actually parsed codec configurations from - // initialization segments. For this work and also recording when implicit - // codec switching occurs (without explicit ChangeType), see - // https://crbug.com/535738. for (size_t i = 0; i < audio_codecs.size(); ++i) { UMA_HISTOGRAM_ENUMERATION("Media.MSE.AudioCodec", audio_codecs[i], CodecInfo::HISTOGRAM_MAX + 1); @@ -569,4 +575,28 @@ std::unique_ptr<StreamParser> StreamParserFactory::Create( return stream_parser; } +// static +std::unique_ptr<StreamParser> StreamParserFactory::Create( + std::unique_ptr<AudioDecoderConfig> audio_config) { + DCHECK(audio_config); + + // TODO(crbug.com/1144908): Histogram-log the codec used for buffering + // WebCodecs in MSE? + + return std::make_unique<media::WebCodecsEncodedChunkStreamParser>( + std::move(audio_config)); +} + +// static +std::unique_ptr<StreamParser> StreamParserFactory::Create( + std::unique_ptr<VideoDecoderConfig> video_config) { + DCHECK(video_config); + + // TODO(crbug.com/1144908): Histogram-log the codec used for buffering + // WebCodecs in MSE? + + return std::make_unique<media::WebCodecsEncodedChunkStreamParser>( + std::move(video_config)); +} + } // namespace media diff --git a/chromium/media/filters/stream_parser_factory.h b/chromium/media/filters/stream_parser_factory.h index 50544a5a93e..0f28977ab73 100644 --- a/chromium/media/filters/stream_parser_factory.h +++ b/chromium/media/filters/stream_parser_factory.h @@ -15,7 +15,9 @@ namespace media { +class AudioDecoderConfig; class StreamParser; +class VideoDecoderConfig; class MEDIA_EXPORT StreamParserFactory { public: @@ -36,10 +38,28 @@ class MEDIA_EXPORT StreamParserFactory { // Returns a new StreamParser object if |type| and all codecs listed in // |codecs| are supported. // Returns NULL otherwise. + // The |audio_config| and |video_config| overloads behave similarly, except + // the caller must provide a valid, supported decoder config; those overloads' + // usage indicates that we intend to buffer WebCodecs encoded audio or video + // chunks with this parser's ProcessChunks() method. Note that + // these overloads do not check support, unlike the |type| and |codecs| + // version. Support checking for WebCodecs-originated decoder configs could be + // async, and should be done by the caller if necessary as part of the decoder + // config creation rather than relying upon parser creation to do this + // potentially expensive step (this step is typically done in a synchronous + // API call by the web app, such as addSourceBuffer().) Like |type| and + // |codecs| versions, basic IsValidConfig() is done on configs emitted from + // the parser. Failing that catching an unsupported config, eventual pipeline + // error should occur for unsupported or invalid decoder configs during + // attempted decode. static std::unique_ptr<StreamParser> Create( const std::string& type, const std::vector<std::string>& codecs, MediaLog* media_log); + static std::unique_ptr<StreamParser> Create( + std::unique_ptr<AudioDecoderConfig> audio_config); + static std::unique_ptr<StreamParser> Create( + std::unique_ptr<VideoDecoderConfig> video_config); }; } // namespace media diff --git a/chromium/media/filters/video_decoder_stream_unittest.cc b/chromium/media/filters/video_decoder_stream_unittest.cc index 75d0f304c78..9293b3b772e 100644 --- a/chromium/media/filters/video_decoder_stream_unittest.cc +++ b/chromium/media/filters/video_decoder_stream_unittest.cc @@ -11,6 +11,7 @@ #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/test/gmock_callback_support.h" +#include "base/test/mock_callback.h" #include "base/test/task_environment.h" #include "build/build_config.h" #include "media/base/fake_demuxer_stream.h" @@ -100,11 +101,10 @@ class VideoDecoderStreamTest base::BindRepeating(&VideoDecoderStreamTest::CreateVideoDecodersForTest, base::Unretained(this)), &media_log_)); - video_decoder_stream_->set_decoder_change_observer_for_testing( - base::BindRepeating(&VideoDecoderStreamTest::OnDecoderChanged, - base::Unretained(this))); + video_decoder_stream_->set_decoder_change_observer(base::BindRepeating( + &VideoDecoderStreamTest::OnDecoderChanged, base::Unretained(this))); video_decoder_stream_ - ->GetDecoderSelectorForTesting(util::PassKey<VideoDecoderStreamTest>()) + ->GetDecoderSelectorForTesting(base::PassKey<VideoDecoderStreamTest>()) .OverrideDecoderPriorityCBForTesting( base::BindRepeating(MockDecoderPriority)); if (GetParam().has_prepare) { @@ -354,14 +354,15 @@ class VideoDecoderStreamTest } // Callback for VideoDecoderStream::Read(). - void FrameReady(VideoDecoderStream::ReadStatus status, - scoped_refptr<VideoFrame> frame) { + void FrameReady(VideoDecoderStream::ReadResult result) { DCHECK(pending_read_); + last_read_status_code_ = result.code(); + scoped_refptr<VideoFrame> frame = last_read_status_code_ == StatusCode::kOk + ? std::move(result).value() + : nullptr; frame_read_ = frame; - last_read_status_ = status; - if (frame && !frame->metadata()->end_of_stream) { - EXPECT_EQ(*frame->metadata()->frame_duration, - demuxer_stream_->duration()); + if (frame && !frame->metadata().end_of_stream) { + EXPECT_EQ(*frame->metadata().frame_duration, demuxer_stream_->duration()); num_decoded_frames_++; } @@ -391,7 +392,7 @@ class VideoDecoderStreamTest void ReadAllFrames(int expected_decoded_frames) { do { ReadOneFrame(); - } while (frame_read_.get() && !frame_read_->metadata()->end_of_stream); + } while (frame_read_.get() && !frame_read_->metadata().end_of_stream); DCHECK_EQ(expected_decoded_frames, num_decoded_frames_); } @@ -541,7 +542,7 @@ class VideoDecoderStreamTest bool pending_stop_; int num_decoded_bytes_unreported_; scoped_refptr<VideoFrame> frame_read_; - VideoDecoderStream::ReadStatus last_read_status_; + StatusCode last_read_status_code_; // Decryptor has no key to decrypt a frame. bool has_no_key_; @@ -692,17 +693,17 @@ TEST_P(VideoDecoderStreamTest, Read_ProperMetadata) { EXPECT_TRUE(frame_read_); - auto* metadata = frame_read_->metadata(); + const VideoFrameMetadata& metadata = frame_read_->metadata(); // Verify the decoding metadata is accurate. - EXPECT_EQ(*metadata->decode_end_time - *metadata->decode_begin_time, + EXPECT_EQ(*metadata.decode_end_time - *metadata.decode_begin_time, kDecodeDelay); // Verify the processing metadata is accurate. const base::TimeDelta expected_processing_time = GetParam().has_prepare ? (kDecodeDelay + kPrepareDelay) : kDecodeDelay; - EXPECT_EQ(*metadata->processing_time, expected_processing_time); + EXPECT_EQ(*metadata.processing_time, expected_processing_time); } TEST_P(VideoDecoderStreamTest, Read_BlockedDemuxer) { @@ -815,11 +816,11 @@ TEST_P(VideoDecoderStreamTest, Read_DuringEndOfStreamDecode) { decoder_->SatisfySingleDecode(); base::RunLoop().RunUntilIdle(); ASSERT_FALSE(pending_read_); - EXPECT_EQ(last_read_status_, VideoDecoderStream::OK); + EXPECT_EQ(last_read_status_code_, StatusCode::kOk); // The read output should indicate end of stream. ASSERT_TRUE(frame_read_.get()); - EXPECT_TRUE(frame_read_->metadata()->end_of_stream); + EXPECT_TRUE(frame_read_->metadata().end_of_stream); } TEST_P(VideoDecoderStreamTest, Read_DemuxerStreamReadError) { @@ -838,7 +839,8 @@ TEST_P(VideoDecoderStreamTest, Read_DemuxerStreamReadError) { base::RunLoop().RunUntilIdle(); ASSERT_FALSE(pending_read_); - EXPECT_EQ(last_read_status_, VideoDecoderStream::DECODE_ERROR); + EXPECT_NE(last_read_status_code_, StatusCode::kOk); + EXPECT_NE(last_read_status_code_, StatusCode::kAborted); } // No Reset() before initialization is successfully completed. @@ -1026,7 +1028,7 @@ TEST_P(VideoDecoderStreamTest, FallbackDecoder_DecodeError) { ASSERT_EQ(GetDecoderName(1), decoder_->GetDisplayName()); ASSERT_FALSE(pending_read_); - ASSERT_EQ(VideoDecoderStream::OK, last_read_status_); + ASSERT_EQ(last_read_status_code_, StatusCode::kOk); // Check that we fell back to Decoder2. ASSERT_GT(decoder_->total_bytes_decoded(), 0); @@ -1066,15 +1068,15 @@ TEST_P(VideoDecoderStreamTest, // A frame should have been emitted. EXPECT_FALSE(pending_read_); - EXPECT_EQ(last_read_status_, VideoDecoderStream::OK); - EXPECT_FALSE(frame_read_->metadata()->end_of_stream); + EXPECT_EQ(last_read_status_code_, StatusCode::kOk); + EXPECT_FALSE(frame_read_->metadata().end_of_stream); EXPECT_GT(decoder_->total_bytes_decoded(), 0); ReadOneFrame(); EXPECT_FALSE(pending_read_); EXPECT_EQ(0, video_decoder_stream_->get_fallback_buffers_size_for_testing()); - EXPECT_TRUE(frame_read_->metadata()->end_of_stream); + EXPECT_TRUE(frame_read_->metadata().end_of_stream); } TEST_P(VideoDecoderStreamTest, @@ -1140,7 +1142,8 @@ TEST_P(VideoDecoderStreamTest, FallbackDecoder_DecodeErrorRepeated) { // No decoders left, expect failure. EXPECT_EQ(decoder_, nullptr); EXPECT_FALSE(pending_read_); - EXPECT_EQ(VideoDecoderStream::DECODE_ERROR, last_read_status_); + EXPECT_NE(last_read_status_code_, StatusCode::kOk); + EXPECT_NE(last_read_status_code_, StatusCode::kAborted); } // This tests verifies that we properly fallback to a new decoder if the first @@ -1161,7 +1164,7 @@ TEST_P(VideoDecoderStreamTest, // Verify that the first frame was decoded successfully. EXPECT_FALSE(pending_read_); EXPECT_GT(decoder_->total_bytes_decoded(), 0); - EXPECT_EQ(VideoDecoderStream::OK, last_read_status_); + EXPECT_EQ(last_read_status_code_, StatusCode::kOk); // Continue up to the point of reinitialization. EnterPendingState(DEMUXER_READ_CONFIG_CHANGE); @@ -1185,7 +1188,7 @@ TEST_P(VideoDecoderStreamTest, // Verify that fallback happened. EXPECT_EQ(GetDecoderName(0), decoder_->GetDisplayName()); EXPECT_FALSE(pending_read_); - EXPECT_EQ(VideoDecoderStream::OK, last_read_status_); + EXPECT_EQ(last_read_status_code_, StatusCode::kOk); EXPECT_GT(decoder_->total_bytes_decoded(), 0); } @@ -1222,7 +1225,8 @@ TEST_P(VideoDecoderStreamTest, // No decoders left. EXPECT_EQ(decoder_, nullptr); EXPECT_FALSE(pending_read_); - EXPECT_EQ(VideoDecoderStream::DECODE_ERROR, last_read_status_); + EXPECT_NE(last_read_status_code_, StatusCode::kOk); + EXPECT_NE(last_read_status_code_, StatusCode::kAborted); } TEST_P(VideoDecoderStreamTest, @@ -1375,7 +1379,7 @@ TEST_P(VideoDecoderStreamTest, FallbackDecoder_SelectedOnDecodeThenInitErrors) { ASSERT_EQ(GetDecoderName(2), decoder_->GetDisplayName()); ASSERT_FALSE(pending_read_); - ASSERT_EQ(VideoDecoderStream::OK, last_read_status_); + ASSERT_EQ(last_read_status_code_, StatusCode::kOk); // Can't check previously selected decoder(s) right now, they might have been // destroyed already. @@ -1400,7 +1404,7 @@ TEST_P(VideoDecoderStreamTest, FallbackDecoder_SelectedOnInitThenDecodeErrors) { ASSERT_EQ(GetDecoderName(2), decoder_->GetDisplayName()); ASSERT_FALSE(pending_read_); - ASSERT_EQ(VideoDecoderStream::OK, last_read_status_); + ASSERT_EQ(last_read_status_code_, StatusCode::kOk); // Can't check previously selected decoder(s) right now, they might have been // destroyed already. @@ -1422,7 +1426,7 @@ TEST_P(VideoDecoderStreamTest, decoder_->SimulateError(); // The error must surface from Read() as DECODE_ERROR. - while (last_read_status_ == VideoDecoderStream::OK) { + while (last_read_status_code_ == StatusCode::kOk) { ReadOneFrame(); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(pending_read_); @@ -1431,7 +1435,8 @@ TEST_P(VideoDecoderStreamTest, // Verify the error was surfaced, rather than falling back to other decoders. ASSERT_EQ(GetDecoderName(0), decoder_->GetDisplayName()); EXPECT_FALSE(pending_read_); - ASSERT_EQ(VideoDecoderStream::DECODE_ERROR, last_read_status_); + EXPECT_NE(last_read_status_code_, StatusCode::kOk); + EXPECT_NE(last_read_status_code_, StatusCode::kAborted); } TEST_P(VideoDecoderStreamTest, DecoderErrorWhenNotReading) { @@ -1450,12 +1455,13 @@ TEST_P(VideoDecoderStreamTest, DecoderErrorWhenNotReading) { decoder_->SimulateError(); // The error must surface from Read() as DECODE_ERROR. - while (last_read_status_ == VideoDecoderStream::OK) { + while (last_read_status_code_ == StatusCode::kOk) { ReadOneFrame(); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(pending_read_); } - EXPECT_EQ(VideoDecoderStream::DECODE_ERROR, last_read_status_); + EXPECT_NE(last_read_status_code_, StatusCode::kOk); + EXPECT_NE(last_read_status_code_, StatusCode::kAborted); } TEST_P(VideoDecoderStreamTest, ReinitializeFailure_Once) { @@ -1516,12 +1522,13 @@ TEST_P(VideoDecoderStreamTest, ReinitializeFailure_NoSupportedDecoder) { ReadUntilDecoderReinitialized(); // The error will surface from Read() as DECODE_ERROR. - while (last_read_status_ == VideoDecoderStream::OK) { + while (last_read_status_code_ == StatusCode::kOk) { ReadOneFrame(); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(pending_read_); } - EXPECT_EQ(VideoDecoderStream::DECODE_ERROR, last_read_status_); + EXPECT_NE(last_read_status_code_, StatusCode::kOk); + EXPECT_NE(last_read_status_code_, StatusCode::kAborted); } TEST_P(VideoDecoderStreamTest, Destroy_DuringFallbackDecoderSelection) { diff --git a/chromium/media/filters/video_renderer_algorithm.cc b/chromium/media/filters/video_renderer_algorithm.cc index 0cef397855b..863359fd01a 100644 --- a/chromium/media/filters/video_renderer_algorithm.cc +++ b/chromium/media/filters/video_renderer_algorithm.cc @@ -327,12 +327,12 @@ int64_t VideoRendererAlgorithm::GetMemoryUsage() const { void VideoRendererAlgorithm::EnqueueFrame(scoped_refptr<VideoFrame> frame) { DCHECK(frame); - DCHECK(!frame->metadata()->end_of_stream); + DCHECK(!frame->metadata().end_of_stream); // Note: Not all frames have duration. E.g., this class is used with WebRTC // which does not provide duration information for its frames. base::TimeDelta metadata_frame_duration = - frame->metadata()->frame_duration.value_or(base::TimeDelta()); + frame->metadata().frame_duration.value_or(base::TimeDelta()); auto timestamp = frame->timestamp(); ReadyFrame ready_frame(std::move(frame)); auto it = frame_queue_.empty() @@ -399,7 +399,7 @@ void VideoRendererAlgorithm::EnqueueFrame(scoped_refptr<VideoFrame> frame) { wallclock_duration = ready_frame.end_time - ready_frame.start_time; } - ready_frame.frame->metadata()->wallclock_frame_duration = wallclock_duration; + ready_frame.frame->metadata().wallclock_frame_duration = wallclock_duration; // The vast majority of cases should always append to the back, but in rare // circumstance we get out of order timestamps, http://crbug.com/386551. @@ -481,7 +481,7 @@ void VideoRendererAlgorithm::UpdateFrameStatistics() { { const auto& last_frame = frame_queue_.back().frame; base::TimeDelta metadata_frame_duration = - last_frame->metadata()->frame_duration.value_or(base::TimeDelta()); + last_frame->metadata().frame_duration.value_or(base::TimeDelta()); if (metadata_frame_duration > base::TimeDelta()) { have_metadata_duration = true; media_timestamps.push_back(last_frame->timestamp() + diff --git a/chromium/media/filters/video_renderer_algorithm_unittest.cc b/chromium/media/filters/video_renderer_algorithm_unittest.cc index b3e7b5b9d99..a1511eda89d 100644 --- a/chromium/media/filters/video_renderer_algorithm_unittest.cc +++ b/chromium/media/filters/video_renderer_algorithm_unittest.cc @@ -1212,7 +1212,7 @@ TEST_F(VideoRendererAlgorithmTest, RemoveExpiredFramesWithoutRendering) { // as effective since we know the duration of it. It is not removed since we // only have one frame in the queue though. auto frame = CreateFrame(tg.interval(0)); - frame->metadata()->frame_duration = tg.interval(1); + frame->metadata().frame_duration = tg.interval(1); algorithm_.EnqueueFrame(frame); ASSERT_EQ(0u, algorithm_.RemoveExpiredFrames(tg.current() + tg.interval(3))); EXPECT_EQ(0u, EffectiveFramesQueued()); @@ -1623,7 +1623,7 @@ TEST_F(VideoRendererAlgorithmTest, InfiniteDurationMetadata) { TickGenerator tg(tick_clock_->NowTicks(), 50); auto frame = CreateFrame(kInfiniteDuration); - frame->metadata()->frame_duration = tg.interval(1); + frame->metadata().frame_duration = tg.interval(1); algorithm_.EnqueueFrame(frame); // This should not crash or fail. @@ -1636,7 +1636,7 @@ TEST_F(VideoRendererAlgorithmTest, UsesFrameDuration) { TickGenerator tg(tick_clock_->NowTicks(), 50); auto frame = CreateFrame(tg.interval(0)); - frame->metadata()->frame_duration = tg.interval(1); + frame->metadata().frame_duration = tg.interval(1); algorithm_.EnqueueFrame(frame); // This should not crash or fail. @@ -1648,7 +1648,7 @@ TEST_F(VideoRendererAlgorithmTest, UsesFrameDuration) { constexpr base::TimeDelta kLongDuration = base::TimeDelta::FromSeconds(3); for (int i = 1; i < 4; ++i) { frame = CreateFrame(tg.interval(i)); - frame->metadata()->frame_duration = i == 3 ? kLongDuration : tg.interval(1); + frame->metadata().frame_duration = i == 3 ? kLongDuration : tg.interval(1); algorithm_.EnqueueFrame(frame); } @@ -1670,7 +1670,7 @@ TEST_F(VideoRendererAlgorithmTest, WallClockDurationMetadataSet) { for (int i = 0; i < frame_count; i++) { auto frame = CreateFrame(tg.interval(i)); - frame->metadata()->frame_duration = tg.interval(1); + frame->metadata().frame_duration = tg.interval(1); algorithm_.EnqueueFrame(frame); } @@ -1680,7 +1680,7 @@ TEST_F(VideoRendererAlgorithmTest, WallClockDurationMetadataSet) { SCOPED_TRACE(base::StringPrintf("Frame #%d", i)); - EXPECT_EQ(*frame->metadata()->wallclock_frame_duration, intended_duration); + EXPECT_EQ(*frame->metadata().wallclock_frame_duration, intended_duration); EXPECT_EQ(algorithm_.average_frame_duration(), intended_duration); } } diff --git a/chromium/media/filters/vp9_parser.h b/chromium/media/filters/vp9_parser.h index 4f360385062..f85b5217836 100644 --- a/chromium/media/filters/vp9_parser.h +++ b/chromium/media/filters/vp9_parser.h @@ -300,7 +300,7 @@ class MEDIA_EXPORT Vp9Parser { // The parsing context that persists across frames. class Context { public: - class Vp9FrameContextManager { + class MEDIA_EXPORT Vp9FrameContextManager { public: Vp9FrameContextManager(); ~Vp9FrameContextManager(); diff --git a/chromium/media/filters/vp9_uncompressed_header_parser.cc b/chromium/media/filters/vp9_uncompressed_header_parser.cc index 6c99ef5b468..294c709ae57 100644 --- a/chromium/media/filters/vp9_uncompressed_header_parser.cc +++ b/chromium/media/filters/vp9_uncompressed_header_parser.cc @@ -622,6 +622,11 @@ Vp9UncompressedHeaderParser::Vp9UncompressedHeaderParser( Vp9Parser::Context* context) : context_(context) {} +const Vp9FrameContext& +Vp9UncompressedHeaderParser::GetVp9DefaultFrameContextForTesting() const { + return kVp9DefaultFrameContext; +} + uint8_t Vp9UncompressedHeaderParser::ReadProfile() { uint8_t profile = 0; @@ -765,6 +770,8 @@ Vp9InterpolationFilter Vp9UncompressedHeaderParser::ReadInterpolationFilter() { void Vp9UncompressedHeaderParser::SetupPastIndependence(Vp9FrameHeader* fhdr) { memset(&context_->segmentation_, 0, sizeof(context_->segmentation_)); + memset(fhdr->ref_frame_sign_bias, 0, sizeof(fhdr->ref_frame_sign_bias)); + ResetLoopfilter(); fhdr->frame_context = kVp9DefaultFrameContext; DCHECK(fhdr->frame_context.IsValid()); @@ -1069,7 +1076,7 @@ bool Vp9UncompressedHeaderParser::Parse(const uint8_t* stream, fhdr->frame_context_idx_to_save_probs = fhdr->frame_context_idx = reader_.ReadLiteral(kVp9NumFrameContextsLog2); - if (fhdr->IsIntra()) { + if (fhdr->IsIntra() || fhdr->error_resilient_mode) { SetupPastIndependence(fhdr); if (fhdr->IsKeyframe() || fhdr->error_resilient_mode || fhdr->reset_frame_context == 3) { diff --git a/chromium/media/filters/vp9_uncompressed_header_parser.h b/chromium/media/filters/vp9_uncompressed_header_parser.h index ea85af4769b..1cf5a408eb3 100644 --- a/chromium/media/filters/vp9_uncompressed_header_parser.h +++ b/chromium/media/filters/vp9_uncompressed_header_parser.h @@ -8,9 +8,11 @@ #include "media/filters/vp9_parser.h" #include "media/filters/vp9_raw_bits_reader.h" +#include "media/base/media_export.h" + namespace media { -class Vp9UncompressedHeaderParser { +class MEDIA_EXPORT Vp9UncompressedHeaderParser { public: Vp9UncompressedHeaderParser(Vp9Parser::Context* context); @@ -18,7 +20,11 @@ class Vp9UncompressedHeaderParser { // Returns true if no error. bool Parse(const uint8_t* stream, off_t frame_size, Vp9FrameHeader* fhdr); + const Vp9FrameContext& GetVp9DefaultFrameContextForTesting() const; + private: + friend class Vp9UncompressedHeaderParserTest; + uint8_t ReadProfile(); bool VerifySyncCode(); bool ReadColorConfig(Vp9FrameHeader* fhdr); diff --git a/chromium/media/filters/vp9_uncompressed_header_parser_unittest.cc b/chromium/media/filters/vp9_uncompressed_header_parser_unittest.cc new file mode 100644 index 00000000000..53fbfafe261 --- /dev/null +++ b/chromium/media/filters/vp9_uncompressed_header_parser_unittest.cc @@ -0,0 +1,65 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/filters/vp9_uncompressed_header_parser.h" + +#include "media/filters/vp9_parser.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +class Vp9UncompressedHeaderParserTest : public testing::Test { + public: + void SetupPastIndependence(Vp9FrameHeader* fhdr) { + vp9_uncompressed_header_parser_.SetupPastIndependence(fhdr); + } + + const Vp9FrameContext& GetVp9DefaultFrameContextForTesting() const { + return vp9_uncompressed_header_parser_ + .GetVp9DefaultFrameContextForTesting(); + } + + Vp9UncompressedHeaderParserTest() + : vp9_uncompressed_header_parser_((&vp9_parser_context_)) {} + + protected: + const Vp9LoopFilterParams& GetLoopFilter() const { + return vp9_parser_context_.loop_filter(); + } + + Vp9Parser::Context vp9_parser_context_; + Vp9UncompressedHeaderParser vp9_uncompressed_header_parser_; +}; + +TEST_F(Vp9UncompressedHeaderParserTest, SetupPastIndependence) { + Vp9FrameHeader frame_header = {}; + + SetupPastIndependence(&frame_header); + + EXPECT_EQ(0, frame_header.ref_frame_sign_bias[VP9_FRAME_INTRA]); + EXPECT_EQ(0, frame_header.ref_frame_sign_bias[VP9_FRAME_LAST]); + EXPECT_EQ(0, frame_header.ref_frame_sign_bias[VP9_FRAME_GOLDEN]); + EXPECT_EQ(0, frame_header.ref_frame_sign_bias[VP9_FRAME_ALTREF]); + + // Verify ResetLoopfilter() result + const Vp9LoopFilterParams& lf = GetLoopFilter(); + EXPECT_TRUE(lf.delta_enabled); + EXPECT_TRUE(lf.delta_update); + EXPECT_EQ(1, lf.ref_deltas[VP9_FRAME_INTRA]); + EXPECT_EQ(0, lf.ref_deltas[VP9_FRAME_LAST]); + EXPECT_EQ(-1, lf.ref_deltas[VP9_FRAME_GOLDEN]); + EXPECT_EQ(-1, lf.ref_deltas[VP9_FRAME_ALTREF]); + EXPECT_EQ(0, lf.mode_deltas[0]); + EXPECT_EQ(0, lf.mode_deltas[1]); + + EXPECT_TRUE(frame_header.frame_context.IsValid()); + + static_assert(std::is_pod<Vp9FrameContext>::value, + "Vp9FrameContext is not POD, rewrite the next EXPECT_TRUE"); + EXPECT_TRUE(std::memcmp(&frame_header.frame_context, + &GetVp9DefaultFrameContextForTesting(), + sizeof(GetVp9DefaultFrameContextForTesting())) == 0); +} + +} // namespace media diff --git a/chromium/media/filters/vpx_video_decoder.cc b/chromium/media/filters/vpx_video_decoder.cc index 7cac1f7fe78..3351761ff07 100644 --- a/chromium/media/filters/vpx_video_decoder.cc +++ b/chromium/media/filters/vpx_video_decoder.cc @@ -98,6 +98,25 @@ static int32_t ReleaseVP9FrameBuffer(void* user_priv, return 0; } +// static +SupportedVideoDecoderConfigs VpxVideoDecoder::SupportedConfigs() { + SupportedVideoDecoderConfigs supported_configs; + supported_configs.emplace_back(/*profile_min=*/VP8PROFILE_ANY, + /*profile_max=*/VP8PROFILE_ANY, + /*coded_size_min=*/kDefaultSwDecodeSizeMin, + /*coded_size_max=*/kDefaultSwDecodeSizeMax, + /*allow_encrypted=*/false, + /*require_encrypted=*/false); + + supported_configs.emplace_back(/*profile_min=*/VP9PROFILE_PROFILE0, + /*profile_max=*/VP9PROFILE_PROFILE2, + /*coded_size_min=*/kDefaultSwDecodeSizeMin, + /*coded_size_max=*/kDefaultSwDecodeSizeMax, + /*allow_encrypted=*/false, + /*require_encrypted=*/false); + return supported_configs; +} + VpxVideoDecoder::VpxVideoDecoder(OffloadState offload_state) : bind_callbacks_(offload_state == OffloadState::kNormal) { DETACH_FROM_SEQUENCE(sequence_checker_); @@ -108,6 +127,10 @@ VpxVideoDecoder::~VpxVideoDecoder() { CloseDecoder(); } +VideoDecoderType VpxVideoDecoder::GetDecoderType() const { + return VideoDecoderType::kVpx; +} + std::string VpxVideoDecoder::GetDisplayName() const { return "VpxVideoDecoder"; } @@ -182,7 +205,7 @@ void VpxVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer, // We might get a successful VpxDecode but not a frame if only a partial // decode happened. if (video_frame) { - video_frame->metadata()->power_efficient = false; + video_frame->metadata().power_efficient = false; output_cb_.Run(video_frame); } @@ -338,6 +361,7 @@ bool VpxVideoDecoder::VpxDecode(const DecoderBuffer* buffer, } (*video_frame)->set_timestamp(buffer->timestamp()); + (*video_frame)->set_hdr_metadata(config_.hdr_metadata()); // Prefer the color space from the config if available. It generally comes // from the color tag which is more expressive than the vp8 and vp9 bitstream. @@ -395,7 +419,6 @@ bool VpxVideoDecoder::VpxDecode(const DecoderBuffer* buffer, (*video_frame) ->set_color_space(gfx::ColorSpace(primaries, transfer, matrix, range)); } - (*video_frame)->set_hdr_metadata(config_.hdr_metadata()); return true; } diff --git a/chromium/media/filters/vpx_video_decoder.h b/chromium/media/filters/vpx_video_decoder.h index 98eff915fe8..d4e5f74acb9 100644 --- a/chromium/media/filters/vpx_video_decoder.h +++ b/chromium/media/filters/vpx_video_decoder.h @@ -8,6 +8,7 @@ #include "base/callback.h" #include "base/macros.h" #include "base/sequence_checker.h" +#include "media/base/supported_video_decoder_config.h" #include "media/base/video_decoder.h" #include "media/base/video_decoder_config.h" #include "media/base/video_frame.h" @@ -29,10 +30,13 @@ class FrameBufferPool; // [1] http://wiki.webmproject.org/alpha-channel class MEDIA_EXPORT VpxVideoDecoder : public OffloadableVideoDecoder { public: + static SupportedVideoDecoderConfigs SupportedConfigs(); + explicit VpxVideoDecoder(OffloadState offload_state = OffloadState::kNormal); ~VpxVideoDecoder() override; // VideoDecoder implementation. + VideoDecoderType GetDecoderType() const override; std::string GetDisplayName() const override; void Initialize(const VideoDecoderConfig& config, bool low_delay, diff --git a/chromium/media/filters/vpx_video_decoder_fuzzertest.cc b/chromium/media/filters/vpx_video_decoder_fuzzertest.cc index 85cc43f6adc..087d46547e4 100644 --- a/chromium/media/filters/vpx_video_decoder_fuzzertest.cc +++ b/chromium/media/filters/vpx_video_decoder_fuzzertest.cc @@ -30,15 +30,15 @@ struct Env { base::test::SingleThreadTaskEnvironment task_environment; }; -void OnDecodeComplete(const base::Closure& quit_closure, media::Status status) { - quit_closure.Run(); +void OnDecodeComplete(base::OnceClosure quit_closure, media::Status status) { + std::move(quit_closure).Run(); } -void OnInitDone(const base::Closure& quit_closure, +void OnInitDone(base::OnceClosure quit_closure, bool* success_dest, media::Status status) { *success_dest = status.is_ok(); - quit_closure.Run(); + std::move(quit_closure).Run(); } void OnOutputComplete(scoped_refptr<media::VideoFrame> frame) {} diff --git a/chromium/media/filters/vpx_video_decoder_unittest.cc b/chromium/media/filters/vpx_video_decoder_unittest.cc index be6824bf297..0eab44301f2 100644 --- a/chromium/media/filters/vpx_video_decoder_unittest.cc +++ b/chromium/media/filters/vpx_video_decoder_unittest.cc @@ -160,7 +160,7 @@ class VpxVideoDecoderTest : public testing::Test { } void FrameReady(scoped_refptr<VideoFrame> frame) { - DCHECK(!frame->metadata()->end_of_stream); + DCHECK(!frame->metadata().end_of_stream); output_frames_.push_back(std::move(frame)); } |