diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 15:28:34 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 13:54:51 +0000 |
commit | 2a19c63448c84c1805fb1a585c3651318bb86ca7 (patch) | |
tree | eb17888e8531aa6ee5e85721bd553b832a7e5156 /chromium/media/filters | |
parent | b014812705fc80bff0a5c120dfcef88f349816dc (diff) | |
download | qtwebengine-chromium-2a19c63448c84c1805fb1a585c3651318bb86ca7.tar.gz |
BASELINE: Update Chromium to 69.0.3497.70
Change-Id: I2b7b56e4e7a8b26656930def0d4575dc32b900a0
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/media/filters')
39 files changed, 756 insertions, 1112 deletions
diff --git a/chromium/media/filters/android/media_codec_audio_decoder.cc b/chromium/media/filters/android/media_codec_audio_decoder.cc index 9429b426ceb..70e1dd2f6b0 100644 --- a/chromium/media/filters/android/media_codec_audio_decoder.cc +++ b/chromium/media/filters/android/media_codec_audio_decoder.cc @@ -188,7 +188,7 @@ void MediaCodecAudioDecoder::Decode(scoped_refptr<DecoderBuffer> buffer, input_queue_.push_back(std::make_pair(std::move(buffer), bound_decode_cb)); - codec_loop_->DoPendingWork(); + codec_loop_->ExpectWork(); } void MediaCodecAudioDecoder::Reset(const base::Closure& closure) { diff --git a/chromium/media/filters/aom_video_decoder.cc b/chromium/media/filters/aom_video_decoder.cc index 41250ae6dad..fa5b8a53ced 100644 --- a/chromium/media/filters/aom_video_decoder.cc +++ b/chromium/media/filters/aom_video_decoder.cc @@ -118,61 +118,16 @@ static void SetColorSpaceForFrame(const aom_image_t* img, return; } - ColorSpace color_space = config.color_space(); - gfx::ColorSpace::PrimaryID primaries = gfx::ColorSpace::PrimaryID::INVALID; - gfx::ColorSpace::TransferID transfer = gfx::ColorSpace::TransferID::INVALID; - gfx::ColorSpace::MatrixID matrix = gfx::ColorSpace::MatrixID::INVALID; gfx::ColorSpace::RangeID range = img->range == AOM_CR_FULL_RANGE ? gfx::ColorSpace::RangeID::FULL : gfx::ColorSpace::RangeID::LIMITED; - switch (img->cs) { - case AOM_CS_BT_601: - case AOM_CS_SMPTE_170: - primaries = gfx::ColorSpace::PrimaryID::SMPTE170M; - transfer = gfx::ColorSpace::TransferID::SMPTE170M; - matrix = gfx::ColorSpace::MatrixID::SMPTE170M; - color_space = COLOR_SPACE_SD_REC601; - break; - case AOM_CS_SMPTE_240: - primaries = gfx::ColorSpace::PrimaryID::SMPTE240M; - transfer = gfx::ColorSpace::TransferID::SMPTE240M; - matrix = gfx::ColorSpace::MatrixID::SMPTE240M; - break; - case AOM_CS_BT_709: - primaries = gfx::ColorSpace::PrimaryID::BT709; - transfer = gfx::ColorSpace::TransferID::BT709; - matrix = gfx::ColorSpace::MatrixID::BT709; - color_space = COLOR_SPACE_HD_REC709; - break; - case AOM_CS_BT_2020_NCL: - case AOM_CS_BT_2020_CL: - primaries = gfx::ColorSpace::PrimaryID::BT2020; - if (img->bit_depth >= 12) { - transfer = gfx::ColorSpace::TransferID::BT2020_12; - } else if (img->bit_depth >= 10) { - transfer = gfx::ColorSpace::TransferID::BT2020_10; - } else { - transfer = gfx::ColorSpace::TransferID::BT709; - } - matrix = img->cs == AOM_CS_BT_2020_NCL - ? gfx::ColorSpace::MatrixID::BT2020_NCL - : gfx::ColorSpace::MatrixID::BT2020_CL; - break; - case AOM_CS_SRGB: - primaries = gfx::ColorSpace::PrimaryID::BT709; - transfer = gfx::ColorSpace::TransferID::IEC61966_2_1; - matrix = gfx::ColorSpace::MatrixID::BT709; - break; - default: - NOTIMPLEMENTED() << "Unsupported color space encountered: " << img->cs; - break; - } - - // TODO(ccameron): Set a color space even for unspecified values. - if (primaries != gfx::ColorSpace::PrimaryID::INVALID) - frame->set_color_space(gfx::ColorSpace(primaries, transfer, matrix, range)); - frame->metadata()->SetInteger(VideoFrameMetadata::COLOR_SPACE, color_space); + // AOM color space defines match ISO 23001-8:2016 via ISO/IEC 23091-4/ITU-T + // H.273. + // http://av1-spec.argondesign.com/av1-spec/av1-spec.html#color-config-semantics + frame->set_color_space( + media::VideoColorSpace(img->cp, img->tc, img->mc, range) + .ToGfxColorSpace()); } // Copies plane of 8-bit pixels out of a 16-bit values. @@ -308,8 +263,8 @@ bool AomVideoDecoder::DecodeBuffer(const DecoderBuffer* buffer) { if (aom_codec_decode( aom_decoder_.get(), buffer->data(), buffer->data_size(), - reinterpret_cast<void*>(buffer->timestamp().InMicroseconds()), - 0 /* deadline */) != AOM_CODEC_OK) { + reinterpret_cast<void*>(buffer->timestamp().InMicroseconds())) != + AOM_CODEC_OK) { const char* detail = aom_codec_error_detail(aom_decoder_.get()); MEDIA_LOG(ERROR, media_log_) << "aom_codec_decode() failed: " << aom_codec_error(aom_decoder_.get()) diff --git a/chromium/media/filters/aom_video_decoder_unittest.cc b/chromium/media/filters/aom_video_decoder_unittest.cc index 7ab2b07ad95..f48a31f640b 100644 --- a/chromium/media/filters/aom_video_decoder_unittest.cc +++ b/chromium/media/filters/aom_video_decoder_unittest.cc @@ -33,7 +33,7 @@ class AomVideoDecoderTest : public testing::Test { public: AomVideoDecoderTest() : decoder_(new AomVideoDecoder(&media_log_)), - i_frame_buffer_(ReadTestDataFile("av1-I-frame-352x288")) {} + i_frame_buffer_(ReadTestDataFile("av1-I-frame-320x240")) {} ~AomVideoDecoderTest() override { Destroy(); } @@ -208,7 +208,7 @@ TEST_F(AomVideoDecoderTest, DecodeFrame_Normal) { // the output size was adjusted. // TODO(dalecurtis): Get an I-frame from a larger video. TEST_F(AomVideoDecoderTest, DISABLED_DecodeFrame_LargerWidth) { - DecodeIFrameThenTestFile("av1-I-frame-352x288", gfx::Size(1280, 720)); + DecodeIFrameThenTestFile("av1-I-frame-320x240", gfx::Size(1280, 720)); } // Decode a VP9 frame which should trigger a decoder error. diff --git a/chromium/media/filters/audio_file_reader.cc b/chromium/media/filters/audio_file_reader.cc index ae37ab7b375..ded5a7d200f 100644 --- a/chromium/media/filters/audio_file_reader.cc +++ b/chromium/media/filters/audio_file_reader.cc @@ -215,7 +215,7 @@ bool AudioFileReader::OnNewFrame( int* total_frames, std::vector<std::unique_ptr<AudioBus>>* decoded_audio_packets, AVFrame* frame) { - const int frames_read = frame->nb_samples; + int frames_read = frame->nb_samples; if (frames_read < 0) return false; @@ -233,6 +233,27 @@ bool AudioFileReader::OnNewFrame( return false; } + // AAC decoding doesn't properly trim the last packet in a stream, so if we + // have duration information, use it to set the correct length to avoid extra + // silence from being output. In the case where we are also discarding some + // portion of the packet (as indicated by a negative pts), we further want to + // adjust the duration downward by however much exists before zero. + if (audio_codec_ == kCodecAAC && frame->pkt_duration) { + const base::TimeDelta pkt_duration = ConvertFromTimeBase( + glue_->format_context()->streams[stream_index_]->time_base, + frame->pkt_duration + std::min(static_cast<int64_t>(0), frame->pts)); + const base::TimeDelta frame_duration = base::TimeDelta::FromSecondsD( + frames_read / static_cast<double>(sample_rate_)); + + if (pkt_duration < frame_duration && pkt_duration > base::TimeDelta()) { + const int new_frames_read = frames_read * (pkt_duration.InSecondsF() / + frame_duration.InSecondsF()); + DVLOG(2) << "Shrinking AAC frame from " << frames_read << " to " + << new_frames_read << " based on packet duration."; + frames_read = new_frames_read; + } + } + // Deinterleave each channel and convert to 32bit floating-point with // nominal range -1.0 -> +1.0. If the output is already in float planar // format, just copy it into the AudioBus. diff --git a/chromium/media/filters/audio_file_reader_unittest.cc b/chromium/media/filters/audio_file_reader_unittest.cc index 6df10b2a8fd..f8d4b839243 100644 --- a/chromium/media/filters/audio_file_reader_unittest.cc +++ b/chromium/media/filters/audio_file_reader_unittest.cc @@ -125,9 +125,7 @@ class AudioFileReaderTest : public testing::Test { EXPECT_EQ(reader_->Read(&decoded_audio_packets), 0); } - void disable_packet_verification() { - packet_verification_disabled_ = true; - } + void disable_packet_verification() { packet_verification_disabled_ = true; } protected: scoped_refptr<DecoderBuffer> data_; @@ -162,72 +160,52 @@ TEST_F(AudioFileReaderTest, Vorbis) { } TEST_F(AudioFileReaderTest, WaveU8) { - RunTest("sfx_u8.wav", - "-1.23,-1.57,-1.14,-0.91,-0.87,-0.07,", - 1, - 44100, - base::TimeDelta::FromMicroseconds(288414), - 12720, - 12719); + RunTest("sfx_u8.wav", "-1.23,-1.57,-1.14,-0.91,-0.87,-0.07,", 1, 44100, + base::TimeDelta::FromMicroseconds(288414), 12720, 12719); } TEST_F(AudioFileReaderTest, WaveS16LE) { - RunTest("sfx_s16le.wav", - "3.05,2.87,3.00,3.32,3.58,4.08,", - 1, - 44100, - base::TimeDelta::FromMicroseconds(288414), - 12720, - 12719); + RunTest("sfx_s16le.wav", "3.05,2.87,3.00,3.32,3.58,4.08,", 1, 44100, + base::TimeDelta::FromMicroseconds(288414), 12720, 12719); } TEST_F(AudioFileReaderTest, WaveS24LE) { - RunTest("sfx_s24le.wav", - "3.03,2.86,2.99,3.31,3.57,4.06,", - 1, - 44100, - base::TimeDelta::FromMicroseconds(288414), - 12720, - 12719); + RunTest("sfx_s24le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100, + base::TimeDelta::FromMicroseconds(288414), 12720, 12719); } TEST_F(AudioFileReaderTest, WaveF32LE) { - RunTest("sfx_f32le.wav", - "3.03,2.86,2.99,3.31,3.57,4.06,", - 1, - 44100, - base::TimeDelta::FromMicroseconds(288414), - 12720, - 12719); + RunTest("sfx_f32le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100, + base::TimeDelta::FromMicroseconds(288414), 12720, 12719); } TEST_F(AudioFileReaderTest, MP3) { - RunTest("sfx.mp3", - "1.30,2.72,4.56,5.08,3.74,2.03,", - 1, - 44100, - base::TimeDelta::FromMicroseconds(313470), - 13825, - 11025); + RunTest("sfx.mp3", "1.30,2.72,4.56,5.08,3.74,2.03,", 1, 44100, + base::TimeDelta::FromMicroseconds(313470), 13825, 11025); } TEST_F(AudioFileReaderTest, CorruptMP3) { // Disable packet verification since the file is corrupt and FFmpeg does not // make any guarantees on packet consistency in this case. disable_packet_verification(); - RunTest("corrupt.mp3", - "-4.95,-2.95,-0.44,1.16,0.31,-2.21,", - 1, - 44100, - base::TimeDelta::FromMicroseconds(1018801), - 44930, - 44928); + RunTest("corrupt.mp3", "-4.95,-2.95,-0.44,1.16,0.31,-2.21,", 1, 44100, + base::TimeDelta::FromMicroseconds(1018801), 44930, 44928); } #if BUILDFLAG(USE_PROPRIETARY_CODECS) TEST_F(AudioFileReaderTest, AAC) { - RunTest("sfx.m4a", "1.81,1.66,2.32,3.27,4.46,3.36,", 1, 44100, - base::TimeDelta::FromMicroseconds(371660), 16391, 13312); + RunTest("sfx.m4a", "0.79,2.31,4.15,4.92,4.04,1.44,", 1, 44100, + base::TimeDelta::FromMicroseconds(371660), 16391, 12701); +} + +TEST_F(AudioFileReaderTest, AAC_SinglePacket) { + RunTest("440hz-10ms.m4a", "3.77,4.53,4.75,3.48,3.67,3.76,", 1, 44100, + base::TimeDelta::FromMicroseconds(69660), 3073, 441); +} + +TEST_F(AudioFileReaderTest, AAC_ADTS) { + RunTest("sfx.adts", "1.80,1.66,2.31,3.26,4.46,3.36,", 1, 44100, + base::TimeDelta::FromMicroseconds(2825180), 124591, 13312); } TEST_F(AudioFileReaderTest, MidStreamConfigChangesFail) { @@ -240,13 +218,8 @@ TEST_F(AudioFileReaderTest, VorbisInvalidChannelLayout) { } TEST_F(AudioFileReaderTest, WaveValidFourChannelLayout) { - RunTest("4ch.wav", - "131.71,38.02,130.31,44.89,135.98,42.52,", - 4, - 44100, - base::TimeDelta::FromMicroseconds(100001), - 4411, - 4410); + RunTest("4ch.wav", "131.71,38.02,130.31,44.89,135.98,42.52,", 4, 44100, + base::TimeDelta::FromMicroseconds(100001), 4411, 4410); } } // namespace media diff --git a/chromium/media/filters/chunk_demuxer.cc b/chromium/media/filters/chunk_demuxer.cc index 50fc1bc8ba5..230c3fbad5a 100644 --- a/chromium/media/filters/chunk_demuxer.cc +++ b/chromium/media/filters/chunk_demuxer.cc @@ -52,6 +52,37 @@ using base::TimeDelta; } \ } +namespace { + +// Helper to attempt construction of a StreamParser specific to |content_type| +// and |codecs|. +// TODO(wolenetz): Consider relocating this to StreamParserFactory in +// conjunction with updating StreamParserFactory's isTypeSupported() to also +// parse codecs, rather than require preparsed vector. +std::unique_ptr<media::StreamParser> CreateParserForTypeAndCodecs( + const std::string& content_type, + const std::string& codecs, + media::MediaLog* media_log) { + std::vector<std::string> parsed_codec_ids; + media::SplitCodecsToVector(codecs, &parsed_codec_ids, false); + return media::StreamParserFactory::Create(content_type, parsed_codec_ids, + media_log); +} + +// Helper to calculate the expected codecs parsed from initialization segments +// for a few mime types that have an implicit codec. +std::string ExpectedCodecs(const std::string& content_type, + const std::string& codecs) { + if (codecs == "" && content_type == "audio/aac") + return "aac"; + if (codecs == "" && + (content_type == "audio/mpeg" || content_type == "audio/mp3")) + return "mp3"; + return codecs; +} + +} // namespace + namespace media { ChunkDemuxerStream::ChunkDemuxerStream(Type type, @@ -228,6 +259,7 @@ void ChunkDemuxerStream::OnStartOfCodedFrameGroup(DecodeTimestamp start_dts, } bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config, + bool allow_codec_change, MediaLog* media_log) { DCHECK(config.IsValidConfig()); DCHECK_EQ(type_, AUDIO); @@ -245,10 +277,11 @@ bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config, return true; } - return SBSTREAM_OP(UpdateAudioConfig(config)); + return SBSTREAM_OP(UpdateAudioConfig(config, allow_codec_change)); } bool ChunkDemuxerStream::UpdateVideoConfig(const VideoDecoderConfig& config, + bool allow_codec_change, MediaLog* media_log) { DCHECK(config.IsValidConfig()); DCHECK_EQ(type_, VIDEO); @@ -260,7 +293,7 @@ bool ChunkDemuxerStream::UpdateVideoConfig(const VideoDecoderConfig& config, return true; } - return SBSTREAM_OP(UpdateVideoConfig(config)); + return SBSTREAM_OP(UpdateVideoConfig(config, allow_codec_change)); } void ChunkDemuxerStream::UpdateTextConfig(const TextTrackConfig& config, @@ -440,7 +473,6 @@ ChunkDemuxer::ChunkDemuxer( open_cb_(open_cb), progress_cb_(progress_cb), encrypted_media_init_data_cb_(encrypted_media_init_data_cb), - enable_text_(false), media_log_(media_log), duration_(kNoTimestamp), user_specified_duration_(-1), @@ -461,8 +493,7 @@ std::string ChunkDemuxer::GetDisplayName() const { } void ChunkDemuxer::Initialize(DemuxerHost* host, - const PipelineStatusCB& init_cb, - bool enable_text_tracks) { + const PipelineStatusCB& init_cb) { DVLOG(1) << "Init(), buffering_by_pts_=" << buffering_by_pts_; base::AutoLock auto_lock(lock_); @@ -480,7 +511,6 @@ void ChunkDemuxer::Initialize(DemuxerHost* host, // has a chance to run. This is because ChunkDemuxer::ReportError_Locked // directly calls DemuxerHost::OnDemuxerError: crbug.com/633016. init_cb_ = init_cb; - enable_text_ = enable_text_tracks; ChangeState_Locked(INITIALIZING); @@ -617,9 +647,9 @@ void ChunkDemuxer::CancelPendingSeek(TimeDelta seek_time) { } ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, - const std::string& type, + const std::string& content_type, const std::string& codecs) { - DVLOG(1) << __func__ << " id=" << id << " mime_type=" << type + DVLOG(1) << __func__ << " id=" << id << " content_type=" << content_type << " codecs=" << codecs; base::AutoLock auto_lock(lock_); @@ -630,14 +660,10 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, // needed. See https://crbug.com/786975. CHECK(!init_cb_.is_null()); - std::vector<std::string> parsed_codec_ids; - media::SplitCodecsToVector(codecs, &parsed_codec_ids, false); - std::unique_ptr<media::StreamParser> stream_parser( - StreamParserFactory::Create(type, parsed_codec_ids, media_log_)); - + CreateParserForTypeAndCodecs(content_type, codecs, media_log_)); if (!stream_parser) { - DVLOG(1) << __func__ << " failed: unsupported mime_type=" << type + DVLOG(1) << __func__ << " failed: unsupported content_type=" << content_type << " codecs=" << codecs; return ChunkDemuxer::kNotSupported; } @@ -657,11 +683,6 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, SourceBufferState::NewTextTrackCB new_text_track_cb; - if (enable_text_) { - new_text_track_cb = base::Bind(&ChunkDemuxer::OnNewTextTrack, - base::Unretained(this)); - } - // TODO(wolenetz): Change these to DCHECKs or switch to returning // kReachedIdLimit once less verification in release build is needed. See // https://crbug.com/786975. @@ -671,15 +692,10 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id, CHECK(*insert_result.first == id); CHECK(insert_result.second); // Only true if insertion succeeded. - std::string expected_sbs_codecs = codecs; - if (codecs == "" && type == "audio/aac") - expected_sbs_codecs = "aac"; - if (codecs == "" && (type == "audio/mpeg" || type == "audio/mp3")) - expected_sbs_codecs = "mp3"; - - source_state->Init( - base::Bind(&ChunkDemuxer::OnSourceInitDone, base::Unretained(this), id), - expected_sbs_codecs, encrypted_media_init_data_cb_, new_text_track_cb); + source_state->Init(base::BindOnce(&ChunkDemuxer::OnSourceInitDone, + base::Unretained(this), id), + ExpectedCodecs(content_type, codecs), + encrypted_media_init_data_cb_, new_text_track_cb); // TODO(wolenetz): Change to DCHECKs once less verification in release build // is needed. See https://crbug.com/786975. @@ -947,6 +963,51 @@ void ChunkDemuxer::Remove(const std::string& id, TimeDelta start, host_->OnBufferedTimeRangesChanged(GetBufferedRanges_Locked()); } +bool ChunkDemuxer::CanChangeType(const std::string& id, + const std::string& content_type, + const std::string& codecs) { + // Note, Chromium currently will not compare content_type and codecs, if any, + // with previous content_type and codecs of the SourceBuffer. + // TODO(wolenetz): Consider returning false if the codecs parameters are ever + // made to be precise such that they signal that the number of tracks of + // various media types differ from the first initialization segment (if + // received already). Switching to an audio-only container, when the first + // initialization segment only contained non-audio tracks, is one example we + // could enforce earlier here. + + DVLOG(1) << __func__ << " id=" << id << " content_type=" << content_type + << " codecs=" << codecs; + base::AutoLock auto_lock(lock_); + + DCHECK(IsValidId(id)); + + // CanChangeType() doesn't care if there has or hasn't been received a first + // initialization segment for the source buffer corresponding to |id|. + + std::unique_ptr<media::StreamParser> stream_parser( + CreateParserForTypeAndCodecs(content_type, codecs, media_log_)); + return !!stream_parser; +} + +void ChunkDemuxer::ChangeType(const std::string& id, + const std::string& content_type, + const std::string& codecs) { + DVLOG(1) << __func__ << " id=" << id << " content_type=" << content_type + << " codecs=" << codecs; + + base::AutoLock auto_lock(lock_); + + DCHECK(state_ == INITIALIZING || state_ == INITIALIZED) << state_; + DCHECK(IsValidId(id)); + + std::unique_ptr<media::StreamParser> stream_parser( + CreateParserForTypeAndCodecs(content_type, codecs, media_log_)); + // Caller should query CanChangeType() first to protect from failing this. + DCHECK(stream_parser); + source_state_map_[id]->ChangeType(std::move(stream_parser), + ExpectedCodecs(content_type, codecs)); +} + double ChunkDemuxer::GetDuration() { base::AutoLock auto_lock(lock_); return GetDuration_Locked(); @@ -1018,6 +1079,14 @@ bool ChunkDemuxer::IsParsingMediaSegment(const std::string& id) { return source_state_map_[id]->parsing_media_segment(); } +bool ChunkDemuxer::GetGenerateTimestampsFlag(const std::string& id) { + base::AutoLock auto_lock(lock_); + DVLOG(1) << "GetGenerateTimestampsFlag(" << id << ")"; + CHECK(IsValidId(id)); + + return source_state_map_[id]->generate_timestamps_flag(); +} + void ChunkDemuxer::SetSequenceMode(const std::string& id, bool sequence_mode) { base::AutoLock auto_lock(lock_); @@ -1312,13 +1381,6 @@ ChunkDemuxerStream* ChunkDemuxer::CreateDemuxerStream( return owning_vector->back().get(); } -void ChunkDemuxer::OnNewTextTrack(ChunkDemuxerStream* text_stream, - const TextTrackConfig& config) { - lock_.AssertAcquired(); - DCHECK_NE(state_, SHUTDOWN); - host_->AddTextStream(text_stream, config); -} - bool ChunkDemuxer::IsValidId(const std::string& source_id) const { lock_.AssertAcquired(); return source_state_map_.count(source_id) > 0u; diff --git a/chromium/media/filters/chunk_demuxer.h b/chromium/media/filters/chunk_demuxer.h index 9253d2d50a5..d4fdfb4c7e3 100644 --- a/chromium/media/filters/chunk_demuxer.h +++ b/chromium/media/filters/chunk_demuxer.h @@ -103,10 +103,16 @@ class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream { base::TimeDelta start_pts); // Called when midstream config updates occur. + // For audio and video, if the codec is allowed to change, the caller should + // set |allow_codec_change| to true. // Returns true if the new config is accepted. // Returns false if the new config should trigger an error. - bool UpdateAudioConfig(const AudioDecoderConfig& config, MediaLog* media_log); - bool UpdateVideoConfig(const VideoDecoderConfig& config, MediaLog* media_log); + bool UpdateAudioConfig(const AudioDecoderConfig& config, + bool allow_codec_change, + MediaLog* media_log); + bool UpdateVideoConfig(const VideoDecoderConfig& config, + bool allow_codec_change, + MediaLog* media_log); void UpdateTextConfig(const TextTrackConfig& config, MediaLog* media_log); void MarkEndOfStream(); @@ -200,9 +206,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // |enable_text| Process inband text tracks in the normal way when true, // otherwise ignore them. - void Initialize(DemuxerHost* host, - const PipelineStatusCB& init_cb, - bool enable_text_tracks) override; + void Initialize(DemuxerHost* host, const PipelineStatusCB& init_cb) override; void Stop() override; void Seek(base::TimeDelta time, const PipelineStatusCB& cb) override; base::Time GetTimelineOffset() const override; @@ -218,15 +222,16 @@ 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. |type| indicates - // the MIME type 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 |type|. - // kNotSupported is returned if |type| is not a supported format. - // kReachedIdLimit is returned if the demuxer cannot handle another ID right - // now. + // Registers a new |id| to use for AppendData() 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 + // not a supported format. Status AddId(const std::string& id, - const std::string& type, + const std::string& content_type, const std::string& codecs); // Notifies a caller via |tracks_updated_cb| that the set of media tracks @@ -285,6 +290,26 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { void Remove(const std::string& id, base::TimeDelta start, base::TimeDelta end); + // Returns whether or not the source buffer associated with |id| can change + // its parser type to one which parses |content_type| and |codecs|. + // |content_type| indicates the ContentType of the MIME type for the data that + // we intend to append for this |id|; |codecs| similarly indicates the MIME + // type's "codecs" parameter, if any. + bool CanChangeType(const std::string& id, + const std::string& content_type, + const std::string& codecs); + + // For the source buffer associated with |id|, changes its parser type to one + // which parses |content_type| and |codecs|. |content_type| indicates the + // ContentType of the MIME type for the data that we intend to append for this + // |id|; |codecs| similarly indicates the MIME type's "codecs" parameter, if + // any. Caller must first ensure CanChangeType() returns true for the same + // parameters. Caller must also ensure that ResetParserState() is done before + // calling this, to flush any pending frames. + void ChangeType(const std::string& id, + const std::string& content_type, + const std::string& codecs); + // If the buffer is full, attempts to try to free up space, as specified in // the "Coded Frame Eviction Algorithm" in the Media Source Extensions Spec. // Returns false iff buffer is still full after running eviction. @@ -310,6 +335,10 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { // a media segment, or false otherwise. bool IsParsingMediaSegment(const std::string& id); + // Returns the 'Generate Timestamps Flag', as described in the MSE Byte Stream + // Format Registry, for the source buffer associated with |id|. + bool GetGenerateTimestampsFlag(const std::string& id); + // Set the append mode to be applied to subsequent buffers appended to the // source buffer associated with |id|. If |sequence_mode| is true, caller // is requesting "sequence" mode. Otherwise, caller is requesting "segments" @@ -430,7 +459,6 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { base::Closure open_cb_; base::Closure progress_cb_; EncryptedMediaInitDataCB encrypted_media_init_data_cb_; - bool enable_text_; // MediaLog for reporting messages and properties to debug content and engine. MediaLog* media_log_; diff --git a/chromium/media/filters/chunk_demuxer_unittest.cc b/chromium/media/filters/chunk_demuxer_unittest.cc index 53adadf198c..57ed2458fe5 100644 --- a/chromium/media/filters/chunk_demuxer_unittest.cc +++ b/chromium/media/filters/chunk_demuxer_unittest.cc @@ -97,7 +97,6 @@ const int kVideoTrackEntryHeaderSize = const int kVideoTrackNum = 1; const int kAudioTrackNum = 2; -const int kTextTrackNum = 3; const int kAlternateVideoTrackNum = 4; const int kAlternateAudioTrackNum = 5; const int kAlternateTextTrackNum = 6; @@ -209,8 +208,9 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { base::Bind(&ChunkDemuxerTest::DemuxerOpened, base::Unretained(this)); base::Closure progress_cb = base::Bind(&ChunkDemuxerTest::OnProgress, base::Unretained(this)); - Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb = base::Bind( - &ChunkDemuxerTest::OnEncryptedMediaInitData, base::Unretained(this)); + Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb = + base::BindRepeating(&ChunkDemuxerTest::OnEncryptedMediaInitData, + base::Unretained(this)); EXPECT_MEDIA_LOG( BufferingByPtsDts(buffering_api_ == BufferingApi::kNewByPts)); demuxer_.reset(new ChunkDemuxer(open_cb, progress_cb, @@ -228,7 +228,6 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { int* size) { bool has_audio = (stream_flags & HAS_AUDIO) != 0; bool has_video = (stream_flags & HAS_VIDEO) != 0; - bool has_text = (stream_flags & HAS_TEXT) != 0; scoped_refptr<DecoderBuffer> ebml_header; scoped_refptr<DecoderBuffer> info; scoped_refptr<DecoderBuffer> audio_track_entry; @@ -279,32 +278,6 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { } } - if (has_text) { - // TODO(matthewjheaney): create an abstraction to do - // this (http://crbug/321454). - // We need it to also handle the creation of multiple text tracks. - // - // This is the track entry for a text track, - // TrackEntry [AE], size=30 - // TrackNum [D7], size=1, val=3 (or 4 if USE_ALTERNATE_TEXT_TRACK_ID) - // TrackUID [73] [C5], size=1, value=3 (must remain constant for same - // track, even if TrackNum changes) - // TrackType [83], size=1, val=0x11 - // CodecId [86], size=18, val="D_WEBVTT/SUBTITLES" - char str[] = "\xAE\x9E\xD7\x81\x03\x73\xC5\x81\x03" - "\x83\x81\x11\x86\x92" - "D_WEBVTT/SUBTITLES"; - DCHECK_EQ(str[4], kTextTrackNum); - if (stream_flags & USE_ALTERNATE_TEXT_TRACK_ID) - str[4] = kAlternateTextTrackNum; - - const int len = strlen(str); - DCHECK_EQ(len, 32); - const uint8_t* const buf = reinterpret_cast<const uint8_t*>(str); - text_track_entry = DecoderBuffer::CopyFrom(buf, len); - tracks_element_size += text_track_entry->data_size(); - } - *size = ebml_header->data_size() + info->data_size() + kTracksHeaderSize + tracks_element_size; @@ -353,12 +326,6 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { } buf += audio_track_entry->data_size(); } - - if (has_text) { - memcpy(buf, text_track_entry->data(), - text_track_entry->data_size()); - buf += text_track_entry->data_size(); - } } ChunkDemuxer::Status AddId() { @@ -433,7 +400,6 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { case kAlternateAudioTrackNum: block_duration = kAudioBlockDuration; break; - case kTextTrackNum: // Fall-through. case kAlternateTextTrackNum: block_duration = kTextBlockDuration; break; @@ -513,8 +479,7 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { CHECK(base::StringToInt(timestamp_str, &block_info.timestamp_in_ms)); - if (track_number == kTextTrackNum || - track_number == kAlternateTextTrackNum) { + if (track_number == kAlternateTextTrackNum) { block_info.duration = kTextBlockDuration; ASSERT_EQ(kWebMFlagKeyframe, block_info.flags) << "Text block with timestamp " << block_info.timestamp_in_ms @@ -727,7 +692,6 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { enum StreamFlags { HAS_AUDIO = 1 << 0, HAS_VIDEO = 1 << 1, - HAS_TEXT = 1 << 2, USE_ALTERNATE_AUDIO_TRACK_ID = 1 << 3, USE_ALTERNATE_VIDEO_TRACK_ID = 1 << 4, USE_ALTERNATE_TEXT_TRACK_ID = 1 << 5, @@ -788,8 +752,8 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { EXPECT_MEDIA_LOG(StreamParsingFailed()); } - demuxer_->Initialize( - &host_, CreateInitDoneCB(expected_duration, expected_status), true); + demuxer_->Initialize(&host_, + CreateInitDoneCB(expected_duration, expected_status)); if (AddId(kSourceId, stream_flags) != ChunkDemuxer::kOk) return false; @@ -799,11 +763,10 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { } bool InitDemuxerAudioAndVideoSourcesText(const std::string& audio_id, - const std::string& video_id, - bool has_text) { + const std::string& video_id) { EXPECT_CALL(*this, DemuxerOpened()); - demuxer_->Initialize( - &host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true); + demuxer_->Initialize(&host_, + CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK)); if (AddId(audio_id, HAS_AUDIO) != ChunkDemuxer::kOk) return false; @@ -813,11 +776,6 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { int audio_flags = HAS_AUDIO; int video_flags = HAS_VIDEO; - if (has_text) { - audio_flags |= HAS_TEXT; - video_flags |= HAS_TEXT; - } - // Note: Unlike InitDemuxerWithEncryptionInfo, this method is currently // incompatible with InSequence tests. Refactoring of the duration // set expectation to not be added during CreateInitDoneCB() could fix this. @@ -833,7 +791,7 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { bool InitDemuxerAudioAndVideoSources(const std::string& audio_id, const std::string& video_id) { - return InitDemuxerAudioAndVideoSourcesText(audio_id, video_id, false); + return InitDemuxerAudioAndVideoSourcesText(audio_id, video_id); } // Initializes the demuxer with data from 2 files with different @@ -864,8 +822,8 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { ExpectInitMediaLogs(HAS_AUDIO | HAS_VIDEO); EXPECT_CALL(*this, InitSegmentReceivedMock(_)); demuxer_->Initialize( - &host_, CreateInitDoneCB(base::TimeDelta::FromMilliseconds(2744), - PIPELINE_OK), true); + &host_, + CreateInitDoneCB(base::TimeDelta::FromMilliseconds(2744), PIPELINE_OK)); if (AddId(kSourceId, HAS_AUDIO | HAS_VIDEO) != ChunkDemuxer::kOk) return false; @@ -874,7 +832,7 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(2)).Times(7); // Expect duration adjustment since actual duration differs slightly from // duration in the init segment. - EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2746))); + EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2768))); EXPECT_TRUE(AppendData(bear1->data(), bear1->data_size())); // Last audio frame has timestamp 2721 and duration 24 (estimated from max // seen so far for audio track). @@ -901,7 +859,7 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { // segment. EXPECT_CALL(*this, InitSegmentReceivedMock(_)); EXPECT_TRUE(AppendData(bear1->data(), 4370)); - EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(23)); + EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(24)); EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(779000, 759000, 3000)); EXPECT_TRUE(AppendData(bear1->data() + 72737, 28183)); CheckExpectedRanges("{ [0,2736) }"); @@ -1235,8 +1193,7 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { const base::TimeDelta& duration, int stream_flags) { EXPECT_CALL(*this, DemuxerOpened()); - demuxer_->Initialize( - &host_, CreateInitDoneCB(duration, PIPELINE_OK), true); + demuxer_->Initialize(&host_, CreateInitDoneCB(duration, PIPELINE_OK)); if (AddId(kSourceId, stream_flags) != ChunkDemuxer::kOk) return false; @@ -1244,6 +1201,7 @@ class ChunkDemuxerTest : public ::testing::TestWithParam<BufferingApi> { // Read a WebM file into memory and send the data to the demuxer. scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename); EXPECT_CALL(*this, InitSegmentReceivedMock(_)); + EXPECT_TRUE(AppendDataInPieces(buffer->data(), buffer->data_size(), 512)); // Verify that the timestamps on the first few packets match what we @@ -1428,123 +1386,6 @@ TEST_P(ChunkDemuxerTest, Init) { } } -// TODO(acolwell): Fold this test into Init tests since the tests are -// almost identical. -TEST_P(ChunkDemuxerTest, InitText) { - // Test with 1 video stream and 1 text streams, and 0 or 1 audio streams. - // No encryption cases handled here. - bool has_video = true; - bool is_audio_encrypted = false; - bool is_video_encrypted = false; - for (int i = 0; i < 2; i++) { - bool has_audio = (i & 0x1) != 0; - - CreateNewDemuxer(); - - DemuxerStream* text_stream = NULL; - TextTrackConfig text_config; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(DoAll(SaveArg<0>(&text_stream), - SaveArg<1>(&text_config))); - - int stream_flags = HAS_TEXT; - if (has_audio) - stream_flags |= HAS_AUDIO; - - if (has_video) - stream_flags |= HAS_VIDEO; - - ASSERT_TRUE(InitDemuxerWithEncryptionInfo( - stream_flags, is_audio_encrypted, is_video_encrypted)); - ASSERT_TRUE(text_stream); - EXPECT_EQ(DemuxerStream::TEXT, text_stream->type()); - EXPECT_EQ(kTextSubtitles, text_config.kind()); - EXPECT_FALSE(static_cast<ChunkDemuxerStream*>(text_stream) - ->supports_partial_append_window_trimming()); - - DemuxerStream* audio_stream = GetStream(DemuxerStream::AUDIO); - if (has_audio) { - ASSERT_TRUE(audio_stream); - - const AudioDecoderConfig& config = audio_stream->audio_decoder_config(); - EXPECT_EQ(kCodecVorbis, config.codec()); - EXPECT_EQ(32, config.bits_per_channel()); - EXPECT_EQ(CHANNEL_LAYOUT_STEREO, config.channel_layout()); - EXPECT_EQ(44100, config.samples_per_second()); - EXPECT_GT(config.extra_data().size(), 0u); - EXPECT_EQ(kSampleFormatPlanarF32, config.sample_format()); - EXPECT_EQ(is_audio_encrypted, - audio_stream->audio_decoder_config().is_encrypted()); - EXPECT_TRUE(static_cast<ChunkDemuxerStream*>(audio_stream) - ->supports_partial_append_window_trimming()); - } else { - EXPECT_FALSE(audio_stream); - } - - DemuxerStream* video_stream = GetStream(DemuxerStream::VIDEO); - if (has_video) { - EXPECT_TRUE(video_stream); - EXPECT_EQ(is_video_encrypted, - video_stream->video_decoder_config().is_encrypted()); - EXPECT_FALSE(static_cast<ChunkDemuxerStream*>(video_stream) - ->supports_partial_append_window_trimming()); - } else { - EXPECT_FALSE(video_stream); - } - - ShutdownDemuxer(); - demuxer_.reset(); - } -} - -TEST_P(ChunkDemuxerTest, SingleTextTrackIdChange) { - // Test with 1 video stream, 1 audio, and 1 text stream. Send a second init - // segment in which the text track ID changes. Verify appended buffers before - // and after the second init segment map to the same underlying track buffers. - CreateNewDemuxer(); - DemuxerStream* text_stream = NULL; - TextTrackConfig text_config; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(DoAll(SaveArg<0>(&text_stream), - SaveArg<1>(&text_config))); - ASSERT_TRUE(InitDemuxerWithEncryptionInfo( - HAS_TEXT | HAS_AUDIO | HAS_VIDEO, false, false)); - DemuxerStream* audio_stream = GetStream(DemuxerStream::AUDIO); - DemuxerStream* video_stream = GetStream(DemuxerStream::VIDEO); - ASSERT_TRUE(audio_stream); - ASSERT_TRUE(video_stream); - ASSERT_TRUE(text_stream); - - AppendMuxedCluster(MuxedStreamInfo(kAudioTrackNum, "0K 23K", 23), - MuxedStreamInfo(kVideoTrackNum, "0K 30", 30), - MuxedStreamInfo(kTextTrackNum, "10K")); - CheckExpectedRanges("{ [0,46) }"); - - std::unique_ptr<uint8_t[]> info_tracks; - int info_tracks_size = 0; - CreateInitSegment( - HAS_TEXT | HAS_AUDIO | HAS_VIDEO | USE_ALTERNATE_TEXT_TRACK_ID, false, - false, &info_tracks, &info_tracks_size); - EXPECT_CALL(*this, InitSegmentReceivedMock(_)); - ASSERT_TRUE(demuxer_->AppendData( - kSourceId, info_tracks.get(), info_tracks_size, - append_window_start_for_next_append_, append_window_end_for_next_append_, - ×tamp_offset_map_[kSourceId])); - - AppendMuxedCluster( - MuxedStreamInfo(kAudioTrackNum, "46K 69K", 23), - MuxedStreamInfo(kVideoTrackNum, "60K", - WebMClusterParser::kDefaultVideoBufferDurationInMs), - MuxedStreamInfo(kAlternateTextTrackNum, "45K")); - - CheckExpectedRanges("{ [0,92) }"); - CheckExpectedBuffers(audio_stream, "0K 23K 46K 69K"); - CheckExpectedBuffers(video_stream, "0K 30 60K"); - CheckExpectedBuffers(text_stream, "10K 45K"); - - ShutdownDemuxer(); -} - TEST_P(ChunkDemuxerTest, AudioVideoTrackIdsChange) { // Test with 1 audio and 1 video stream. Send a second init segment in which // the audio and video track IDs change. Verify that appended buffers before @@ -1582,39 +1423,32 @@ TEST_P(ChunkDemuxerTest, InitSegmentSetsNeedRandomAccessPointFlag) { // to fully test this since needs random access point flag initializes to // true. CreateNewDemuxer(); - DemuxerStream* text_stream = NULL; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - ASSERT_TRUE(InitDemuxerWithEncryptionInfo( - HAS_TEXT | HAS_AUDIO | HAS_VIDEO, false, false)); + ASSERT_TRUE( + InitDemuxerWithEncryptionInfo(HAS_AUDIO | HAS_VIDEO, false, false)); DemuxerStream* audio_stream = GetStream(DemuxerStream::AUDIO); DemuxerStream* video_stream = GetStream(DemuxerStream::VIDEO); - ASSERT_TRUE(audio_stream && video_stream && text_stream); + ASSERT_TRUE(audio_stream && video_stream); AppendMuxedCluster( MuxedStreamInfo(kAudioTrackNum, "23K", WebMClusterParser::kDefaultAudioBufferDurationInMs), - MuxedStreamInfo(kVideoTrackNum, "0 30K", 30), - MuxedStreamInfo(kTextTrackNum, "25K 40K")); + MuxedStreamInfo(kVideoTrackNum, "0 30K", 30)); CheckExpectedRanges("{ [23,46) }"); EXPECT_CALL(*this, InitSegmentReceivedMock(_)); - ASSERT_TRUE(AppendInitSegment(HAS_TEXT | HAS_AUDIO | HAS_VIDEO)); + ASSERT_TRUE(AppendInitSegment(HAS_AUDIO | HAS_VIDEO)); AppendMuxedCluster(MuxedStreamInfo(kAudioTrackNum, "46K 69K", 23), - MuxedStreamInfo(kVideoTrackNum, "60 90K", 30), - MuxedStreamInfo(kTextTrackNum, "80K 90K")); + MuxedStreamInfo(kVideoTrackNum, "60 90K", 30)); CheckExpectedRanges("{ [23,92) }"); CheckExpectedBuffers(audio_stream, "23K 46K 69K"); CheckExpectedBuffers(video_stream, "30K 90K"); - CheckExpectedBuffers(text_stream, "25K 40K 80K 90K"); } TEST_P(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppended) { EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize(&host_, base::Bind(&ChunkDemuxerTest::DemuxerInitialized, - base::Unretained(this)), - true); + base::Unretained(this))); EXPECT_EQ(AddId("audio", HAS_AUDIO), ChunkDemuxer::kOk); EXPECT_EQ(AddId("video", HAS_VIDEO), ChunkDemuxer::kOk); @@ -1626,54 +1460,27 @@ TEST_P(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppended) { ShutdownDemuxer(); } -TEST_P(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppendedText) { - EXPECT_CALL(*this, DemuxerOpened()); - demuxer_->Initialize(&host_, base::Bind(&ChunkDemuxerTest::DemuxerInitialized, - base::Unretained(this)), - true); - - EXPECT_EQ(AddId("audio", HAS_AUDIO), ChunkDemuxer::kOk); - EXPECT_EQ(AddId("video_and_text", HAS_VIDEO), ChunkDemuxer::kOk); - - EXPECT_CALL(host_, AddTextStream(_, _)) - .Times(Exactly(1)); - - ExpectInitMediaLogs(HAS_VIDEO); - EXPECT_CALL(*this, InitSegmentReceivedMock(_)); - ASSERT_TRUE( - AppendInitSegmentWithSourceId("video_and_text", HAS_VIDEO | HAS_TEXT)); - - ShutdownDemuxer(); -} - // Verifies that all streams waiting for data receive an end of stream // buffer when Shutdown() is called. TEST_P(ChunkDemuxerTest, Shutdown_EndOfStreamWhileWaitingForData) { - DemuxerStream* text_stream = NULL; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT)); + ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO)); DemuxerStream* audio_stream = GetStream(DemuxerStream::AUDIO); DemuxerStream* video_stream = GetStream(DemuxerStream::VIDEO); bool audio_read_done = false; bool video_read_done = false; - bool text_read_done = false; audio_stream->Read(base::Bind(&OnReadDone_EOSExpected, &audio_read_done)); video_stream->Read(base::Bind(&OnReadDone_EOSExpected, &video_read_done)); - text_stream->Read(base::Bind(&OnReadDone_EOSExpected, &text_read_done)); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(audio_read_done); EXPECT_FALSE(video_read_done); - EXPECT_FALSE(text_read_done); ShutdownDemuxer(); EXPECT_TRUE(audio_read_done); EXPECT_TRUE(video_read_done); - EXPECT_TRUE(text_read_done); } // Test that Seek() completes successfully when the first cluster @@ -1887,8 +1694,8 @@ TEST_P(ChunkDemuxerTest, PerStreamMonotonicallyIncreasingTimestamps) { // INFO & TRACKS data. TEST_P(ChunkDemuxerTest, ClusterBeforeInitSegment) { EXPECT_CALL(*this, DemuxerOpened()); - demuxer_->Initialize( - &host_, NewExpectedStatusCB(CHUNK_DEMUXER_ERROR_APPEND_FAILED), true); + demuxer_->Initialize(&host_, + NewExpectedStatusCB(CHUNK_DEMUXER_ERROR_APPEND_FAILED)); ASSERT_EQ(AddId(), ChunkDemuxer::kOk); @@ -1900,15 +1707,15 @@ TEST_P(ChunkDemuxerTest, ClusterBeforeInitSegment) { // Test cases where we get an MarkEndOfStream() call during initialization. TEST_P(ChunkDemuxerTest, EOSDuringInit) { EXPECT_CALL(*this, DemuxerOpened()); - demuxer_->Initialize( - &host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN), true); + demuxer_->Initialize(&host_, + NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN)); MarkEndOfStream(PIPELINE_OK); } TEST_P(ChunkDemuxerTest, EndOfStreamWithNoAppend) { EXPECT_CALL(*this, DemuxerOpened()); - demuxer_->Initialize( - &host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN), true); + demuxer_->Initialize(&host_, + NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_OPEN)); ASSERT_EQ(AddId(), ChunkDemuxer::kOk); @@ -2108,50 +1915,24 @@ TEST_P(ChunkDemuxerTest, EndOfStreamDuringCanceledSeek) { // Verify buffered range change behavior for audio/video/text tracks. TEST_P(ChunkDemuxerTest, EndOfStreamRangeChanges) { - DemuxerStream* text_stream = NULL; - - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT)); + ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO)); AppendMuxedCluster(MuxedStreamInfo(kVideoTrackNum, "0K 33", 33), MuxedStreamInfo(kAudioTrackNum, "0K 23K", 23)); - // Check expected ranges and verify that an empty text track does not - // affect the expected ranges. CheckExpectedRanges("{ [0,46) }"); EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(66))); MarkEndOfStream(PIPELINE_OK); - // Check expected ranges and verify that an empty text track does not - // affect the expected ranges. CheckExpectedRanges("{ [0,66) }"); - - // Unmark end of stream state and verify that the ranges return to - // their pre-"end of stream" values. - demuxer_->UnmarkEndOfStream(); - CheckExpectedRanges("{ [0,46) }"); - - // Add text track data and verify that the buffered ranges don't change - // since the intersection of all the tracks doesn't change. - EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(200))); - AppendMuxedCluster(MuxedStreamInfo(kVideoTrackNum, "0K 33", 33), - MuxedStreamInfo(kAudioTrackNum, "0K 23K", 23), - MuxedStreamInfo(kTextTrackNum, "0K 100K")); - CheckExpectedRanges("{ [0,46) }"); - - // Mark end of stream and verify that text track data is reflected in - // the new range. - MarkEndOfStream(PIPELINE_OK); - CheckExpectedRanges("{ [0,200) }"); } // Make sure AppendData() will accept elements that span multiple calls. TEST_P(ChunkDemuxerTest, AppendingInPieces) { EXPECT_CALL(*this, DemuxerOpened()); - demuxer_->Initialize( - &host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true); + demuxer_->Initialize(&host_, + CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK)); ASSERT_EQ(AddId(), ChunkDemuxer::kOk); @@ -2199,7 +1980,7 @@ TEST_P(ChunkDemuxerTest, WebMFile_AudioAndVideo) { // Expect duration adjustment since actual duration differs slightly from // duration in the init segment. - EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2746))); + EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2768))); ASSERT_TRUE(ParseWebMFile("bear-320x240.webm", buffer_timestamps, base::TimeDelta::FromMilliseconds(2744))); @@ -2243,7 +2024,7 @@ TEST_P(ChunkDemuxerTest, WebMFile_AudioOnly) { // Expect duration adjustment since actual duration differs slightly from // duration in the init segment. - EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2746))); + EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2768))); ASSERT_TRUE(ParseWebMFile("bear-320x240-audio-only.webm", buffer_timestamps, base::TimeDelta::FromMilliseconds(2744), @@ -2283,6 +2064,10 @@ TEST_P(ChunkDemuxerTest, WebMFile_AltRefFrames) { {kSkip, kSkip}, }; + // Expect duration adjustment since actual duration differs slightly from + // duration in the init segment. + EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(2768))); + ExpectInitMediaLogs(HAS_AUDIO | HAS_VIDEO); EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(2)); ASSERT_TRUE(ParseWebMFile("bear-320x240-altref.webm", buffer_timestamps, @@ -2345,8 +2130,8 @@ TEST_P(ChunkDemuxerTest, IncrementalClusterParsing) { TEST_P(ChunkDemuxerTest, ParseErrorDuringInit) { EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize( - &host_, CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED), - true); + &host_, + CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED)); ASSERT_EQ(AddId(), ChunkDemuxer::kOk); @@ -2360,8 +2145,8 @@ TEST_P(ChunkDemuxerTest, ParseErrorDuringInit) { TEST_P(ChunkDemuxerTest, AVHeadersWithAudioOnlyType) { EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize( - &host_, CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED), - true); + &host_, + CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED)); ASSERT_EQ(AddId(kSourceId, "audio/webm", "vorbis"), ChunkDemuxer::kOk); @@ -2374,8 +2159,8 @@ TEST_P(ChunkDemuxerTest, AVHeadersWithAudioOnlyType) { TEST_P(ChunkDemuxerTest, AVHeadersWithVideoOnlyType) { EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize( - &host_, CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED), - true); + &host_, + CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED)); ASSERT_EQ(AddId(kSourceId, "video/webm", "vp8"), ChunkDemuxer::kOk); @@ -2390,8 +2175,8 @@ TEST_P(ChunkDemuxerTest, AVHeadersWithVideoOnlyType) { TEST_P(ChunkDemuxerTest, AudioOnlyHeaderWithAVType) { EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize( - &host_, CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED), - true); + &host_, + CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED)); ASSERT_EQ(AddId(kSourceId, "video/webm", "vorbis,vp8"), ChunkDemuxer::kOk); @@ -2406,8 +2191,8 @@ TEST_P(ChunkDemuxerTest, AudioOnlyHeaderWithAVType) { TEST_P(ChunkDemuxerTest, VideoOnlyHeaderWithAVType) { EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize( - &host_, CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED), - true); + &host_, + CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED)); ASSERT_EQ(AddId(kSourceId, "video/webm", "vorbis,vp8"), ChunkDemuxer::kOk); @@ -2450,15 +2235,10 @@ TEST_P(ChunkDemuxerTest, AddSeparateSourcesForAudioAndVideo) { } TEST_P(ChunkDemuxerTest, AddSeparateSourcesForAudioAndVideoText) { - // TODO(matthewjheaney): Here and elsewhere, we need more tests - // for inband text tracks (http://crbug/321455). - std::string audio_id = "audio1"; std::string video_id = "video1"; - EXPECT_CALL(host_, AddTextStream(_, _)) - .Times(Exactly(2)); - ASSERT_TRUE(InitDemuxerAudioAndVideoSourcesText(audio_id, video_id, true)); + ASSERT_TRUE(InitDemuxerAudioAndVideoSourcesText(audio_id, video_id)); // Append audio and video data into separate source ids. ASSERT_TRUE(AppendCluster( @@ -2473,8 +2253,8 @@ TEST_P(ChunkDemuxerTest, AddSeparateSourcesForAudioAndVideoText) { TEST_P(ChunkDemuxerTest, AddIdFailures) { EXPECT_CALL(*this, DemuxerOpened()); - demuxer_->Initialize( - &host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true); + demuxer_->Initialize(&host_, + CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK)); std::string audio_id = "audio1"; std::string video_id = "video1"; @@ -2527,10 +2307,8 @@ TEST_P(ChunkDemuxerTest, RemoveId) { // quota for new IDs in the future. TEST_P(ChunkDemuxerTest, RemoveAndAddId) { demuxer_->Initialize( - &host_, - base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized, - base::Unretained(this)), - true); + &host_, base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized, + base::Unretained(this))); std::string audio_id_1 = "audio1"; ASSERT_TRUE(AddId(audio_id_1, HAS_AUDIO) == ChunkDemuxer::kOk); @@ -2940,32 +2718,6 @@ TEST_P(ChunkDemuxerTest, GetBufferedRanges_AudioVideo) { CheckExpectedRanges("{ [0,23) [300,400) [600,670) [900,950) [1200,1250) }"); } -TEST_P(ChunkDemuxerTest, GetBufferedRanges_AudioVideoText) { - EXPECT_CALL(host_, AddTextStream(_, _)); - ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT)); - - // Append audio & video data - AppendMuxedCluster(MuxedStreamInfo(kAudioTrackNum, "0K 23K", 23), - MuxedStreamInfo(kVideoTrackNum, "0K 33", 33)); - - // Verify that a text track with no cues does not result in an empty buffered - // range. - CheckExpectedRanges("{ [0,46) }"); - - // Add some text cues. - AppendMuxedCluster(MuxedStreamInfo(kAudioTrackNum, "100K 123K", 23), - MuxedStreamInfo(kVideoTrackNum, "100K 133", 33), - MuxedStreamInfo(kTextTrackNum, "100K 200K")); - - // Verify that the text cues are not reflected in the buffered ranges. - CheckExpectedRanges("{ [0,46) [100,146) }"); - - // Remove the buffered ranges. - demuxer_->Remove(kSourceId, base::TimeDelta(), - base::TimeDelta::FromMilliseconds(250)); - CheckExpectedRanges("{ }"); -} - // Once MarkEndOfStream() is called, GetBufferedRanges should not cut off any // over-hanging tails at the end of the ranges as this is likely due to block // duration differences. @@ -3092,10 +2844,8 @@ TEST_P(ChunkDemuxerTest, DifferentStreamTimecodesOutOfRange) { TEST_P(ChunkDemuxerTest, CodecPrefixMatching) { demuxer_->Initialize( - &host_, - base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized, - base::Unretained(this)), - true); + &host_, base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized, + base::Unretained(this))); ChunkDemuxer::Status expected = ChunkDemuxer::kNotSupported; #if BUILDFLAG(USE_PROPRIETARY_CODECS) @@ -3126,10 +2876,8 @@ TEST_P(ChunkDemuxerTest, CodecIDsThatAreNotRFC6381Compliant) { }; demuxer_->Initialize( - &host_, - base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized, - base::Unretained(this)), - true); + &host_, base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized, + base::Unretained(this))); for (size_t i = 0; i < arraysize(codec_ids); ++i) { #if BUILDFLAG(USE_PROPRIETARY_CODECS) @@ -3188,8 +2936,7 @@ TEST_P(ChunkDemuxerTest, EndOfStreamStillSetAfterSeek) { TEST_P(ChunkDemuxerTest, GetBufferedRangesBeforeInitSegment) { EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize(&host_, base::Bind(&ChunkDemuxerTest::DemuxerInitialized, - base::Unretained(this)), - true); + base::Unretained(this))); ASSERT_EQ(AddId("audio", HAS_AUDIO), ChunkDemuxer::kOk); ASSERT_EQ(AddId("video", HAS_VIDEO), ChunkDemuxer::kOk); @@ -3453,8 +3200,8 @@ TEST_P(ChunkDemuxerTest, EmitBuffersDuringAbort) { EXPECT_MEDIA_LOG(CodecName("audio", "aac")); EXPECT_MEDIA_LOG(FoundStream("video")); EXPECT_MEDIA_LOG(CodecName("video", "h264")); - demuxer_->Initialize(&host_, CreateInitDoneCB(kInfiniteDuration, PIPELINE_OK), - true); + demuxer_->Initialize(&host_, + CreateInitDoneCB(kInfiniteDuration, PIPELINE_OK)); EXPECT_EQ(ChunkDemuxer::kOk, AddId(kSourceId, kMp2tMimeType, kMp2tCodecs)); // For info: @@ -3523,8 +3270,8 @@ TEST_P(ChunkDemuxerTest, SeekCompleteDuringAbort) { EXPECT_MEDIA_LOG(CodecName("audio", "aac")); EXPECT_MEDIA_LOG(FoundStream("video")); EXPECT_MEDIA_LOG(CodecName("video", "h264")); - demuxer_->Initialize(&host_, CreateInitDoneCB(kInfiniteDuration, PIPELINE_OK), - true); + demuxer_->Initialize(&host_, + CreateInitDoneCB(kInfiniteDuration, PIPELINE_OK)); EXPECT_EQ(ChunkDemuxer::kOk, AddId(kSourceId, kMp2tMimeType, kMp2tCodecs)); // For info: @@ -3655,7 +3402,6 @@ TEST_P(ChunkDemuxerTest, WebMIsParsingMediaSegmentDetection) { ASSERT_TRUE(InitDemuxer(HAS_AUDIO)); EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(23)).Times(2); - EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(2000, 1000, 22000)); for (size_t i = 0; i < sizeof(kBuffer); i++) { DVLOG(3) << "Appending and testing index " << i; ASSERT_TRUE(AppendData(kBuffer + i, 1)); @@ -3745,8 +3491,7 @@ TEST_P(ChunkDemuxerTest, AppendAfterEndOfStream) { // the pipeline has a chance to initialize the demuxer. TEST_P(ChunkDemuxerTest, Shutdown_BeforeInitialize) { demuxer_->Shutdown(); - demuxer_->Initialize( - &host_, CreateInitDoneCB(DEMUXER_ERROR_COULD_NOT_OPEN), true); + demuxer_->Initialize(&host_, CreateInitDoneCB(DEMUXER_ERROR_COULD_NOT_OPEN)); base::RunLoop().RunUntilIdle(); } @@ -4184,8 +3929,7 @@ TEST_P(ChunkDemuxerTest, AppendWindow_WebMFile_AudioOnly) { EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize( &host_, - CreateInitDoneCB(base::TimeDelta::FromMilliseconds(2744), PIPELINE_OK), - true); + CreateInitDoneCB(base::TimeDelta::FromMilliseconds(2744), PIPELINE_OK)); ASSERT_EQ(ChunkDemuxer::kOk, AddId(kSourceId, HAS_AUDIO)); // Set the append window to [50,150). @@ -4218,15 +3962,14 @@ TEST_P(ChunkDemuxerTest, AppendWindow_AudioConfigUpdateRemovesPreroll) { EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize( &host_, - CreateInitDoneCB(base::TimeDelta::FromMilliseconds(2744), PIPELINE_OK), - true); + CreateInitDoneCB(base::TimeDelta::FromMilliseconds(2744), PIPELINE_OK)); ASSERT_EQ(ChunkDemuxer::kOk, AddId(kSourceId, HAS_AUDIO)); // Set the append window such that the first file is completely before the // append window. // Expect duration adjustment since actual duration differs slightly from // duration in the init segment. - const base::TimeDelta duration_1 = base::TimeDelta::FromMilliseconds(2746); + const base::TimeDelta duration_1 = base::TimeDelta::FromMilliseconds(2768); append_window_start_for_next_append_ = duration_1; EXPECT_MEDIA_LOG(DroppedFrameCheckAppendWindow( @@ -4251,69 +3994,16 @@ TEST_P(ChunkDemuxerTest, AppendWindow_AudioConfigUpdateRemovesPreroll) { scoped_refptr<DecoderBuffer> buffer2 = ReadTestDataFile("bear-320x240-audio-only-48khz.webm"); EXPECT_CALL(*this, InitSegmentReceivedMock(_)); - EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(21)); + EXPECT_MEDIA_LOG(WebMSimpleBlockDurationEstimated(22)); EXPECT_CALL(host_, SetDuration(_)).Times(AnyNumber()); ASSERT_TRUE(SetTimestampOffset(kSourceId, duration_1)); ASSERT_TRUE(AppendDataInPieces(buffer2->data(), buffer2->data_size(), 512)); - CheckExpectedRanges("{ [2746,5519) }"); + CheckExpectedRanges("{ [2768,5542) }"); Seek(duration_1); ExpectConfigChanged(DemuxerStream::AUDIO); ASSERT_FALSE(config_1.Matches(stream->audio_decoder_config())); - CheckExpectedBuffers(stream, "2746K 2767K 2789K 2810K"); -} - -TEST_P(ChunkDemuxerTest, AppendWindow_Text) { - DemuxerStream* text_stream = NULL; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - ASSERT_TRUE(InitDemuxer(HAS_VIDEO | HAS_TEXT)); - DemuxerStream* video_stream = GetStream(DemuxerStream::VIDEO); - - // Set the append window to [20,280). - append_window_start_for_next_append_ = base::TimeDelta::FromMilliseconds(20); - append_window_end_for_next_append_ = base::TimeDelta::FromMilliseconds(280); - - EXPECT_MEDIA_LOG(DroppedFrame("video", 0)); - EXPECT_MEDIA_LOG(DroppedFrame("text", 0)); - EXPECT_MEDIA_LOG(DroppedFrame("text", 200000)); - EXPECT_MEDIA_LOG(DroppedFrame("video", 270000)); - EXPECT_MEDIA_LOG(DroppedFrame("video", 300000)); - EXPECT_MEDIA_LOG(DroppedFrame("text", 300000)); - EXPECT_MEDIA_LOG(DroppedFrame("video", 330000)); - - // Append a cluster that starts before and ends after the append - // window. - AppendMuxedCluster( - MuxedStreamInfo(kVideoTrackNum, - "0K 30 60 90 120K 150 180 210 240K 270 300 330K", 30), - MuxedStreamInfo(kTextTrackNum, "0K 100K 200K 300K")); - - // Verify that text cues that start outside the window are not included - // in the buffer. Also verify that cues that extend beyond the - // window are not included. - CheckExpectedRanges("{ [100,270) }"); - CheckExpectedBuffers(video_stream, "120K 150 180 210 240K"); - CheckExpectedBuffers(text_stream, "100K"); - - // Extend the append window to [20,650). - append_window_end_for_next_append_ = base::TimeDelta::FromMilliseconds(650); - - EXPECT_MEDIA_LOG(DroppedFrame("text", 600000)); - EXPECT_MEDIA_LOG(DroppedFrame("video", 630000)); - EXPECT_MEDIA_LOG(DroppedFrame("text", 700000)); - - // Append more data and verify that a new range is created. - AppendMuxedCluster( - MuxedStreamInfo(kVideoTrackNum, - "360 390 420K 450 480 510 540K 570 600 630K", 30), - MuxedStreamInfo(kTextTrackNum, "400K 500K 600K 700K")); - CheckExpectedRanges("{ [100,270) [400,630) }"); - - // Seek to the new range and verify that the expected buffers are returned. - Seek(base::TimeDelta::FromMilliseconds(420)); - CheckExpectedBuffers(video_stream, "420K 450 480 510 540K 570 600"); - CheckExpectedBuffers(text_stream, "400K 500K"); + CheckExpectedBuffers(stream, "2768K 2789K 2811K 2832K"); } TEST_P(ChunkDemuxerTest, StartWaitingForSeekAfterParseError) { @@ -4326,22 +4016,17 @@ TEST_P(ChunkDemuxerTest, StartWaitingForSeekAfterParseError) { } TEST_P(ChunkDemuxerTest, Remove_AudioVideoText) { - DemuxerStream* text_stream = NULL; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT)); + ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO)); DemuxerStream* audio_stream = GetStream(DemuxerStream::AUDIO); DemuxerStream* video_stream = GetStream(DemuxerStream::VIDEO); AppendMuxedCluster( MuxedStreamInfo(kAudioTrackNum, "0K 20K 40K 60K 80K 100K 120K 140K", 20), - MuxedStreamInfo(kVideoTrackNum, "0K 30 60 90 120K 150 180", 30), - MuxedStreamInfo(kTextTrackNum, "0K 100K 200K")); + MuxedStreamInfo(kVideoTrackNum, "0K 30 60 90 120K 150 180", 30)); CheckExpectedBuffers(audio_stream, "0K 20K 40K 60K 80K 100K 120K 140K"); CheckExpectedBuffers(video_stream, "0K 30 60 90 120K 150 180"); - CheckExpectedBuffers(text_stream, "0K 100K 200K"); // Remove the buffers that were added. demuxer_->Remove(kSourceId, base::TimeDelta(), @@ -4354,13 +4039,11 @@ TEST_P(ChunkDemuxerTest, Remove_AudioVideoText) { // ones and verify that only the new buffers are returned. AppendMuxedCluster( MuxedStreamInfo(kAudioTrackNum, "1K 21K 41K 61K 81K 101K 121K 141K", 20), - MuxedStreamInfo(kVideoTrackNum, "1K 31 61 91 121K 151 181", 30), - MuxedStreamInfo(kTextTrackNum, "1K 101K 201K")); + MuxedStreamInfo(kVideoTrackNum, "1K 31 61 91 121K 151 181", 30)); Seek(base::TimeDelta()); CheckExpectedBuffers(audio_stream, "1K 21K 41K 61K 81K 101K 121K 141K"); CheckExpectedBuffers(video_stream, "1K 31 61 91 121K 151 181"); - CheckExpectedBuffers(text_stream, "1K 101K 201K"); } TEST_P(ChunkDemuxerTest, Remove_StartAtDuration) { @@ -4394,10 +4077,7 @@ TEST_P(ChunkDemuxerTest, Remove_StartAtDuration) { // the seek point and will return cues after the seek position // when they are eventually appended. TEST_P(ChunkDemuxerTest, SeekCompletesWithoutTextCues) { - DemuxerStream* text_stream = NULL; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO | HAS_TEXT)); + ASSERT_TRUE(InitDemuxer(HAS_AUDIO | HAS_VIDEO)); DemuxerStream* audio_stream = GetStream(DemuxerStream::AUDIO); DemuxerStream* video_stream = GetStream(DemuxerStream::VIDEO); @@ -4411,11 +4091,6 @@ TEST_P(ChunkDemuxerTest, SeekCompletesWithoutTextCues) { EXPECT_FALSE(seek_cb_was_called); - bool text_read_done = false; - text_stream->Read(base::Bind(&OnReadDone, - base::TimeDelta::FromMilliseconds(225), - &text_read_done)); - // Append audio & video data so the seek completes. AppendMuxedCluster( MuxedStreamInfo(kAudioTrackNum, @@ -4424,26 +4099,17 @@ TEST_P(ChunkDemuxerTest, SeekCompletesWithoutTextCues) { base::RunLoop().RunUntilIdle(); EXPECT_TRUE(seek_cb_was_called); - EXPECT_FALSE(text_read_done); // Read some audio & video buffers to further verify seek completion. CheckExpectedBuffers(audio_stream, "120K 140K"); CheckExpectedBuffers(video_stream, "120K 150"); - EXPECT_FALSE(text_read_done); - // Append text cues that start after the seek point and verify that // they are returned by Read() calls. AppendMuxedCluster(MuxedStreamInfo(kAudioTrackNum, "220K 240K 260K 280K", 20), - MuxedStreamInfo(kVideoTrackNum, "240K 270 300 330", 30), - MuxedStreamInfo(kTextTrackNum, "225K 275K 325K")); + MuxedStreamInfo(kVideoTrackNum, "240K 270 300 330", 30)); base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(text_read_done); - - // NOTE: we start at 275 here because the buffer at 225 was returned - // to the pending read initiated above. - CheckExpectedBuffers(text_stream, "275K 325K"); // Verify that audio & video streams continue to return expected values. CheckExpectedBuffers(audio_stream, "160K 180K"); @@ -4845,8 +4511,7 @@ TEST_P(ChunkDemuxerTest, MultipleIds) { CreateNewDemuxer(); EXPECT_CALL(*this, DemuxerOpened()); EXPECT_CALL(host_, SetDuration(_)).Times(2); - demuxer_->Initialize(&host_, CreateInitDoneCB(kNoTimestamp, PIPELINE_OK), - true); + demuxer_->Initialize(&host_, CreateInitDoneCB(kNoTimestamp, PIPELINE_OK)); const char* kId1 = "id1"; const char* kId2 = "id2"; @@ -4872,7 +4537,7 @@ TEST_P(ChunkDemuxerTest, CompleteInitAfterIdRemoved) { CreateNewDemuxer(); EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize(&host_, - CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true); + CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK)); // Add two ids, then remove one of the ids and verify that adding init segment // only for the remaining id still triggers the InitDoneCB. @@ -4895,7 +4560,7 @@ TEST_P(ChunkDemuxerTest, RemovingIdMustRemoveStreams) { CreateNewDemuxer(); EXPECT_CALL(*this, DemuxerOpened()); demuxer_->Initialize(&host_, - CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK), true); + CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK)); const char* kId1 = "id1"; EXPECT_EQ(AddId(kId1, "video/webm", "vorbis,vp8"), ChunkDemuxer::kOk); @@ -4952,10 +4617,8 @@ TEST_P(ChunkDemuxerTest, SequenceModeSingleTrackNoWarning) { TEST_P(ChunkDemuxerTest, Mp4Vp9CodecSupport) { demuxer_->Initialize( - &host_, - base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized, - base::Unretained(this)), - true); + &host_, base::BindRepeating(&ChunkDemuxerTest::DemuxerInitialized, + base::Unretained(this))); ChunkDemuxer::Status expected = ChunkDemuxer::kOk; EXPECT_EQ(AddId("source_id", "video/mp4", "vp09.00.10.08"), expected); } @@ -4966,8 +4629,8 @@ TEST_P(ChunkDemuxerTest, UnmarkEOSRetainsParseErrorState_BeforeInit) { EXPECT_CALL(*this, DemuxerOpened()); EXPECT_MEDIA_LOG(StreamParsingFailed()); demuxer_->Initialize( - &host_, CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED), - true); + &host_, + CreateInitDoneCB(kNoTimestamp, CHUNK_DEMUXER_ERROR_APPEND_FAILED)); ASSERT_EQ(AddId(kSourceId, HAS_AUDIO | HAS_VIDEO), ChunkDemuxer::kOk); AppendGarbage(); diff --git a/chromium/media/filters/decoder_stream.cc b/chromium/media/filters/decoder_stream.cc index c4ecd423fbc..09d61733315 100644 --- a/chromium/media/filters/decoder_stream.cc +++ b/chromium/media/filters/decoder_stream.cc @@ -73,13 +73,13 @@ DecoderStream<StreamType>::~DecoderStream() { DCHECK(task_runner_->BelongsToCurrentThread()); if (init_cb_) { - task_runner_->PostTask( - FROM_HERE, base::BindOnce(base::ResetAndReturn(&init_cb_), false)); + task_runner_->PostTask(FROM_HERE, + base::BindOnce(std::move(init_cb_), false)); } if (read_cb_) { task_runner_->PostTask( - FROM_HERE, base::BindOnce(base::ResetAndReturn(&read_cb_), ABORTED, - scoped_refptr<Output>())); + FROM_HERE, + base::BindOnce(std::move(read_cb_), ABORTED, scoped_refptr<Output>())); } if (reset_cb_) task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&reset_cb_)); @@ -96,10 +96,10 @@ std::string DecoderStream<StreamType>::GetStreamTypeString() { template <DemuxerStream::Type StreamType> void DecoderStream<StreamType>::Initialize( DemuxerStream* stream, - const InitCB& init_cb, + InitCB init_cb, CdmContext* cdm_context, - const StatisticsCB& statistics_cb, - const base::Closure& waiting_for_decryption_key_cb) { + StatisticsCB statistics_cb, + base::RepeatingClosure waiting_for_decryption_key_cb) { FUNCTION_DVLOG(1); DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(state_, STATE_UNINITIALIZED); @@ -107,10 +107,10 @@ void DecoderStream<StreamType>::Initialize( DCHECK(init_cb); stream_ = stream; - init_cb_ = init_cb; + init_cb_ = std::move(init_cb); cdm_context_ = cdm_context; - statistics_cb_ = statistics_cb; - waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb; + statistics_cb_ = std::move(statistics_cb); + waiting_for_decryption_key_cb_ = std::move(waiting_for_decryption_key_cb); traits_->OnStreamReset(stream_); @@ -119,7 +119,7 @@ void DecoderStream<StreamType>::Initialize( } template <DemuxerStream::Type StreamType> -void DecoderStream<StreamType>::Read(const ReadCB& read_cb) { +void DecoderStream<StreamType>::Read(ReadCB read_cb) { FUNCTION_DVLOG(3); DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_INITIALIZING) @@ -130,26 +130,27 @@ void DecoderStream<StreamType>::Read(const ReadCB& read_cb) { DCHECK(!reset_cb_); if (state_ == STATE_ERROR) { - task_runner_->PostTask(FROM_HERE, base::BindOnce(read_cb, DECODE_ERROR, - scoped_refptr<Output>())); + task_runner_->PostTask(FROM_HERE, + base::BindOnce(std::move(read_cb), DECODE_ERROR, + scoped_refptr<Output>())); return; } if (state_ == STATE_END_OF_STREAM && ready_outputs_.empty() && unprepared_outputs_.empty()) { - task_runner_->PostTask( - FROM_HERE, - base::BindOnce(read_cb, OK, StreamTraits::CreateEOSOutput())); + task_runner_->PostTask(FROM_HERE, + base::BindOnce(std::move(read_cb), OK, + StreamTraits::CreateEOSOutput())); return; } if (!ready_outputs_.empty()) { - task_runner_->PostTask(FROM_HERE, - base::BindOnce(read_cb, OK, ready_outputs_.front())); + task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(read_cb), OK, + ready_outputs_.front())); ready_outputs_.pop_front(); MaybePrepareAnotherOutput(); } else { - read_cb_ = read_cb; + read_cb_ = std::move(read_cb); } if (state_ == STATE_NORMAL && CanDecodeMore()) @@ -157,18 +158,18 @@ void DecoderStream<StreamType>::Read(const ReadCB& read_cb) { } template <DemuxerStream::Type StreamType> -void DecoderStream<StreamType>::Reset(const base::Closure& closure) { +void DecoderStream<StreamType>::Reset(base::OnceClosure closure) { FUNCTION_DVLOG(2); DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_NE(state_, STATE_UNINITIALIZED); DCHECK(!reset_cb_); - reset_cb_ = closure; + reset_cb_ = std::move(closure); if (read_cb_) { task_runner_->PostTask( - FROM_HERE, base::BindOnce(base::ResetAndReturn(&read_cb_), ABORTED, - scoped_refptr<Output>())); + FROM_HERE, + base::BindOnce(std::move(read_cb_), ABORTED, scoped_refptr<Output>())); } ClearOutputs(); @@ -328,7 +329,7 @@ void DecoderStream<StreamType>::OnDecoderSelected( state_ = STATE_UNINITIALIZED; MEDIA_LOG(ERROR, media_log_) << GetStreamTypeString() << " decoder initialization failed"; - base::ResetAndReturn(&init_cb_).Run(false); + std::move(init_cb_).Run(false); } else { CompleteDecoderReinitialization(false); } @@ -341,6 +342,9 @@ void DecoderStream<StreamType>::OnDecoderSelected( !!decrypting_demuxer_stream_); media_log_->SetStringProperty(GetStreamTypeString() + "_decoder", decoder_->GetDisplayName()); + media_log_->SetBooleanProperty( + "is_platform_" + GetStreamTypeString() + "_decoder", + decoder_->IsPlatformDecoder()); MEDIA_LOG(INFO, media_log_) << "Selected " << decoder_->GetDisplayName() << " for " @@ -356,7 +360,7 @@ void DecoderStream<StreamType>::OnDecoderSelected( state_ = STATE_NORMAL; if (StreamTraits::NeedsBitstreamConversion(decoder_.get())) stream_->EnableBitstreamConverter(); - base::ResetAndReturn(&init_cb_).Run(true); + std::move(init_cb_).Run(true); } template <DemuxerStream::Type StreamType> @@ -364,7 +368,7 @@ void DecoderStream<StreamType>::SatisfyRead( Status status, const scoped_refptr<Output>& output) { DCHECK(read_cb_); - base::ResetAndReturn(&read_cb_).Run(status, output); + std::move(read_cb_).Run(status, output); } template <DemuxerStream::Type StreamType> diff --git a/chromium/media/filters/decoder_stream.h b/chromium/media/filters/decoder_stream.h index 95bd78f81eb..50707c37a26 100644 --- a/chromium/media/filters/decoder_stream.h +++ b/chromium/media/filters/decoder_stream.h @@ -56,11 +56,10 @@ class MEDIA_EXPORT DecoderStream { base::RepeatingCallback<std::vector<std::unique_ptr<Decoder>>()>; // Indicates completion of a DecoderStream initialization. - using InitCB = base::RepeatingCallback<void(bool success)>; + using InitCB = base::OnceCallback<void(bool success)>; // Indicates completion of a DecoderStream read. - using ReadCB = - base::RepeatingCallback<void(Status, const scoped_refptr<Output>&)>; + using ReadCB = base::OnceCallback<void(Status, const scoped_refptr<Output>&)>; DecoderStream(std::unique_ptr<DecoderStreamTraits<StreamType>> traits, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, @@ -76,16 +75,16 @@ class MEDIA_EXPORT DecoderStream { // |cdm_context| can be used to handle encrypted stream. Can be null if the // stream is not encrypted. void Initialize(DemuxerStream* stream, - const InitCB& init_cb, + InitCB init_cb, CdmContext* cdm_context, - const StatisticsCB& statistics_cb, - const base::Closure& waiting_for_decryption_key_cb); + StatisticsCB statistics_cb, + base::RepeatingClosure waiting_for_decryption_key_cb); // Reads a decoded Output and returns it via the |read_cb|. Note that // |read_cb| is always called asynchronously. This method should only be // called after initialization has succeeded and must not be called during // pending Reset(). - void Read(const ReadCB& read_cb); + void Read(ReadCB read_cb); // Resets the decoder, flushes all decoded outputs and/or internal buffers, // fires any existing pending read callback and calls |closure| on completion. @@ -94,7 +93,7 @@ class MEDIA_EXPORT DecoderStream { // during pending Reset(). // N.B: If the decoder stream has run into an error, calling this method does // not 'reset' it to a normal state. - void Reset(const base::Closure& closure); + void Reset(base::OnceClosure closure); // Returns true if the decoder currently has the ability to decode and return // an Output. @@ -224,10 +223,10 @@ class MEDIA_EXPORT DecoderStream { StatisticsCB statistics_cb_; InitCB init_cb_; - base::Closure waiting_for_decryption_key_cb_; + base::RepeatingClosure waiting_for_decryption_key_cb_; ReadCB read_cb_; - base::Closure reset_cb_; + base::OnceClosure reset_cb_; DemuxerStream* stream_; diff --git a/chromium/media/filters/decrypting_audio_decoder.cc b/chromium/media/filters/decrypting_audio_decoder.cc index e09411ed90e..767b405ef18 100644 --- a/chromium/media/filters/decrypting_audio_decoder.cc +++ b/chromium/media/filters/decrypting_audio_decoder.cc @@ -94,7 +94,7 @@ void DecryptingAudioDecoder::Initialize( if (state_ == kUninitialized) { if (!cdm_context->GetDecryptor()) { - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() << ": no decryptor"; + DVLOG(1) << __func__ << ": no decryptor"; base::ResetAndReturn(&init_cb_).Run(false); return; } @@ -141,10 +141,9 @@ void DecryptingAudioDecoder::Decode(scoped_refptr<DecoderBuffer> buffer, void DecryptingAudioDecoder::Reset(const base::Closure& closure) { DVLOG(2) << "Reset() - state: " << state_; DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kIdle || - state_ == kPendingDecode || - state_ == kWaitingForKey || - state_ == kDecodeFinished) << state_; + DCHECK(state_ == kIdle || state_ == kPendingDecode || + state_ == kWaitingForKey || state_ == kDecodeFinished) + << state_; DCHECK(init_cb_.is_null()); // No Reset() during pending initialization. DCHECK(reset_cb_.is_null()); @@ -194,9 +193,8 @@ DecryptingAudioDecoder::~DecryptingAudioDecoder() { void DecryptingAudioDecoder::InitializeDecoder() { state_ = kPendingDecoderInit; decryptor_->InitializeAudioDecoder( - config_, - BindToCurrentLoop(base::Bind( - &DecryptingAudioDecoder::FinishInitialization, weak_this_))); + config_, BindToCurrentLoop(base::Bind( + &DecryptingAudioDecoder::FinishInitialization, weak_this_))); } void DecryptingAudioDecoder::FinishInitialization(bool success) { @@ -204,12 +202,11 @@ void DecryptingAudioDecoder::FinishInitialization(bool success) { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(state_ == kPendingDecoderInit) << state_; DCHECK(!init_cb_.is_null()); - DCHECK(reset_cb_.is_null()); // No Reset() before initialization finished. + DCHECK(reset_cb_.is_null()); // No Reset() before initialization finished. DCHECK(decode_cb_.is_null()); // No Decode() before initialization finished. if (!success) { - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() - << ": failed to init decoder on decryptor"; + DVLOG(1) << __func__ << ": failed to init audio decoder on decryptor"; base::ResetAndReturn(&init_cb_).Run(false); decryptor_ = NULL; state_ = kError; @@ -221,9 +218,8 @@ void DecryptingAudioDecoder::FinishInitialization(bool success) { new AudioTimestampHelper(config_.samples_per_second())); decryptor_->RegisterNewKeyCB( - Decryptor::kAudio, - BindToCurrentLoop( - base::Bind(&DecryptingAudioDecoder::OnKeyAdded, weak_this_))); + Decryptor::kAudio, BindToCurrentLoop(base::Bind( + &DecryptingAudioDecoder::OnKeyAdded, weak_this_))); state_ = kIdle; base::ResetAndReturn(&init_cb_).Run(true); @@ -240,8 +236,8 @@ void DecryptingAudioDecoder::DecodePendingBuffer() { decryptor_->DecryptAndDecodeAudio( pending_buffer_to_decode_, - BindToCurrentLoop(base::Bind( - &DecryptingAudioDecoder::DeliverFrame, weak_this_, buffer_size))); + BindToCurrentLoop(base::Bind(&DecryptingAudioDecoder::DeliverFrame, + weak_this_, buffer_size))); } void DecryptingAudioDecoder::DeliverFrame( @@ -271,7 +267,7 @@ void DecryptingAudioDecoder::DeliverFrame( if (status == Decryptor::kError) { DVLOG(2) << "DeliverFrame() - kError"; MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": decode error"; - state_ = kDecodeFinished; // TODO add kError state + state_ = kDecodeFinished; // TODO add kError state base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::DECODE_ERROR); return; } @@ -279,10 +275,11 @@ void DecryptingAudioDecoder::DeliverFrame( if (status == Decryptor::kNoKey) { std::string key_id = scoped_pending_buffer_to_decode->decrypt_config()->key_id(); - std::string missing_key_id = base::HexEncode(key_id.data(), key_id.size()); - DVLOG(1) << "DeliverFrame() - no key for key ID " << missing_key_id; - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() << ": no key for key ID " - << missing_key_id; + std::string log_message = + "no key for key ID " + base::HexEncode(key_id.data(), key_id.size()) + + "; will resume decoding after new usable key is available"; + DVLOG(1) << __func__ << ": " << log_message; + MEDIA_LOG(INFO, media_log_) << GetDisplayName() << ": " << log_message; // Set |pending_buffer_to_decode_| back as we need to try decoding the // pending buffer again when new key is added to the decryptor. @@ -290,8 +287,8 @@ void DecryptingAudioDecoder::DeliverFrame( if (need_to_try_again_if_nokey_is_returned) { // The |state_| is still kPendingDecode. - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key was added, resuming decode"; + MEDIA_LOG(INFO, media_log_) + << GetDisplayName() << ": key was added, resuming decode"; DecodePendingBuffer(); return; } @@ -334,8 +331,8 @@ void DecryptingAudioDecoder::OnKeyAdded() { } if (state_ == kWaitingForKey) { - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key added, resuming decode"; + MEDIA_LOG(INFO, media_log_) + << GetDisplayName() << ": key added, resuming decode"; state_ = kPendingDecode; DecodePendingBuffer(); } @@ -352,8 +349,7 @@ void DecryptingAudioDecoder::DoReset() { void DecryptingAudioDecoder::ProcessDecodedFrames( const Decryptor::AudioFrames& frames) { for (Decryptor::AudioFrames::const_iterator iter = frames.begin(); - iter != frames.end(); - ++iter) { + iter != frames.end(); ++iter) { scoped_refptr<AudioBuffer> frame = *iter; DCHECK(!frame->end_of_stream()) << "EOS frame returned."; diff --git a/chromium/media/filters/decrypting_audio_decoder_unittest.cc b/chromium/media/filters/decrypting_audio_decoder_unittest.cc index f68e048aed9..8a5b4621a37 100644 --- a/chromium/media/filters/decrypting_audio_decoder_unittest.cc +++ b/chromium/media/filters/decrypting_audio_decoder_unittest.cc @@ -111,16 +111,14 @@ class DecryptingAudioDecoderTest : public testing::Test { InitializeAndExpectResult(config_, true); } - void Reinitialize() { - ReinitializeConfigChange(config_); - } + void Reinitialize() { ReinitializeConfigChange(config_); } void ReinitializeConfigChange(const AudioDecoderConfig& new_config) { EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kAudio)); EXPECT_CALL(*decryptor_, InitializeAudioDecoder(_, _)) .WillOnce(RunCallback<1>(true)); EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kAudio, _)) - .WillOnce(SaveArg<1>(&key_added_cb_)); + .WillOnce(SaveArg<1>(&key_added_cb_)); decoder_->Initialize( new_config, cdm_context_.get(), NewExpectedBoolCB(true), base::Bind(&DecryptingAudioDecoderTest::FrameReady, @@ -133,9 +131,8 @@ class DecryptingAudioDecoderTest : public testing::Test { void DecodeAndExpect(scoped_refptr<DecoderBuffer> buffer, DecodeStatus status) { EXPECT_CALL(*this, DecodeDone(status)); - decoder_->Decode(buffer, - base::Bind(&DecryptingAudioDecoderTest::DecodeDone, - base::Unretained(this))); + decoder_->Decode(buffer, base::Bind(&DecryptingAudioDecoderTest::DecodeDone, + base::Unretained(this))); base::RunLoop().RunUntilIdle(); } @@ -161,8 +158,9 @@ class DecryptingAudioDecoderTest : public testing::Test { // Sets up expectations and actions to put DecryptingAudioDecoder in an // active normal decoding state. void EnterNormalDecodingState() { - EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)).WillRepeatedly( - Invoke(this, &DecryptingAudioDecoderTest::DecryptAndDecodeAudio)); + EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) + .WillRepeatedly( + Invoke(this, &DecryptingAudioDecoderTest::DecryptAndDecodeAudio)); EXPECT_CALL(*this, FrameReady(decoded_frame_)); for (int i = 0; i < kDecodingDelay + 1; ++i) DecodeAndExpect(encrypted_buffer_, DecodeStatus::OK); @@ -173,8 +171,7 @@ class DecryptingAudioDecoderTest : public testing::Test { // EnterNormalDecodingState() to work. void EnterEndOfStreamState() { // The codec in the |decryptor_| will be flushed. - EXPECT_CALL(*this, FrameReady(decoded_frame_)) - .Times(kDecodingDelay); + EXPECT_CALL(*this, FrameReady(decoded_frame_)).Times(kDecodingDelay); DecodeAndExpect(DecoderBuffer::CreateEOSBuffer(), DecodeStatus::OK); EXPECT_EQ(0, num_frames_in_decryptor_); } @@ -196,8 +193,8 @@ class DecryptingAudioDecoderTest : public testing::Test { void EnterWaitingForKeyState() { EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(encrypted_buffer_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kNoKey, - Decryptor::AudioFrames())); + .WillRepeatedly( + RunCallback<1>(Decryptor::kNoKey, Decryptor::AudioFrames())); EXPECT_CALL(*this, OnWaitingForDecryptionKey()); decoder_->Decode(encrypted_buffer_, base::Bind(&DecryptingAudioDecoderTest::DecodeDone, @@ -208,8 +205,8 @@ class DecryptingAudioDecoderTest : public testing::Test { void AbortPendingAudioDecodeCB() { if (!pending_audio_decode_cb_.is_null()) { - base::ResetAndReturn(&pending_audio_decode_cb_).Run( - Decryptor::kSuccess, Decryptor::AudioFrames()); + base::ResetAndReturn(&pending_audio_decode_cb_) + .Run(Decryptor::kSuccess, Decryptor::AudioFrames()); } } @@ -315,8 +312,8 @@ TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_DecodeError) { Initialize(); EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kError, - Decryptor::AudioFrames())); + .WillRepeatedly( + RunCallback<1>(Decryptor::kError, Decryptor::AudioFrames())); DecodeAndExpect(encrypted_buffer_, DecodeStatus::DECODE_ERROR); } @@ -423,8 +420,8 @@ TEST_F(DecryptingAudioDecoderTest, KeyAdded_DruingPendingDecode) { // The audio decode callback is returned after the correct decryption key is // added. key_added_cb_.Run(); - base::ResetAndReturn(&pending_audio_decode_cb_).Run( - Decryptor::kNoKey, Decryptor::AudioFrames()); + base::ResetAndReturn(&pending_audio_decode_cb_) + .Run(Decryptor::kNoKey, Decryptor::AudioFrames()); base::RunLoop().RunUntilIdle(); } diff --git a/chromium/media/filters/decrypting_demuxer_stream.cc b/chromium/media/filters/decrypting_demuxer_stream.cc index 06009f7a947..12de15c5369 100644 --- a/chromium/media/filters/decrypting_demuxer_stream.cc +++ b/chromium/media/filters/decrypting_demuxer_stream.cc @@ -58,7 +58,7 @@ void DecryptingDemuxerStream::Initialize(DemuxerStream* stream, InitializeDecoderConfig(); if (!cdm_context->GetDecryptor()) { - DVLOG(2) << __func__ << ": no decryptor"; + DVLOG(1) << __func__ << ": no decryptor"; state_ = kUninitialized; base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); return; @@ -235,8 +235,7 @@ void DecryptingDemuxerStream::DecryptPendingBuffer() { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(state_, kPendingDecrypt) << state_; decryptor_->Decrypt( - GetDecryptorStreamType(), - pending_buffer_to_decrypt_, + GetDecryptorStreamType(), pending_buffer_to_decrypt_, BindToCurrentLoop( base::Bind(&DecryptingDemuxerStream::DeliverBuffer, weak_this_))); } @@ -274,15 +273,17 @@ void DecryptingDemuxerStream::DeliverBuffer( if (status == Decryptor::kNoKey) { std::string key_id = pending_buffer_to_decrypt_->decrypt_config()->key_id(); - std::string missing_key_id = base::HexEncode(key_id.data(), key_id.size()); - DVLOG(1) << "DeliverBuffer() - no key for key ID " << missing_key_id; - MEDIA_LOG(INFO, media_log_) << GetDisplayName() << ": no key for key ID " - << missing_key_id; + + std::string log_message = + "no key for key ID " + base::HexEncode(key_id.data(), key_id.size()) + + "; will resume decrypting after new usable key is available"; + DVLOG(1) << __func__ << ": " << log_message; + MEDIA_LOG(INFO, media_log_) << GetDisplayName() << ": " << log_message; if (need_to_try_again_if_nokey) { // The |state_| is still kPendingDecrypt. - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key was added, resuming decrypt"; + MEDIA_LOG(INFO, media_log_) + << GetDisplayName() << ": key was added, resuming decrypt"; DecryptPendingBuffer(); return; } diff --git a/chromium/media/filters/decrypting_demuxer_stream_unittest.cc b/chromium/media/filters/decrypting_demuxer_stream_unittest.cc index 2650a45a168..d156adc807f 100644 --- a/chromium/media/filters/decrypting_demuxer_stream_unittest.cc +++ b/chromium/media/filters/decrypting_demuxer_stream_unittest.cc @@ -41,8 +41,10 @@ static const uint8_t kFakeIv[DecryptConfig::kDecryptionKeySize] = {0}; static scoped_refptr<DecoderBuffer> CreateFakeEncryptedStreamBuffer( bool is_clear) { scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(kFakeBufferSize)); - std::string iv = is_clear ? std::string() : - std::string(reinterpret_cast<const char*>(kFakeIv), arraysize(kFakeIv)); + std::string iv = is_clear + ? std::string() + : std::string(reinterpret_cast<const char*>(kFakeIv), + arraysize(kFakeIv)); if (!is_clear) { buffer->set_decrypt_config(DecryptConfig::CreateCencConfig( std::string(reinterpret_cast<const char*>(kFakeKeyId), @@ -227,8 +229,8 @@ class DecryptingDemuxerStreamTest : public testing::Test { EXPECT_CALL(*input_audio_stream_, Read(_)) .WillRepeatedly(ReturnBuffer(encrypted_buffer_)); EXPECT_CALL(*decryptor_, Decrypt(_, encrypted_buffer_, _)) - .WillRepeatedly(RunCallback<2>(Decryptor::kNoKey, - scoped_refptr<DecoderBuffer>())); + .WillRepeatedly( + RunCallback<2>(Decryptor::kNoKey, scoped_refptr<DecoderBuffer>())); EXPECT_MEDIA_LOG(HasSubstr("DecryptingDemuxerStream: no key for key ID")); EXPECT_CALL(*this, OnWaitingForDecryptionKey()); demuxer_stream_->Read(base::Bind(&DecryptingDemuxerStreamTest::BufferReady, @@ -343,8 +345,8 @@ TEST_F(DecryptingDemuxerStreamTest, Read_DecryptError) { EXPECT_CALL(*input_audio_stream_, Read(_)) .WillRepeatedly(ReturnBuffer(encrypted_buffer_)); EXPECT_CALL(*decryptor_, Decrypt(_, encrypted_buffer_, _)) - .WillRepeatedly(RunCallback<2>(Decryptor::kError, - scoped_refptr<DecoderBuffer>())); + .WillRepeatedly( + RunCallback<2>(Decryptor::kError, scoped_refptr<DecoderBuffer>())); EXPECT_MEDIA_LOG(HasSubstr("DecryptingDemuxerStream: decrypt error")); ReadAndExpectBufferReadyWith(DemuxerStream::kError, nullptr); } diff --git a/chromium/media/filters/decrypting_video_decoder.cc b/chromium/media/filters/decrypting_video_decoder.cc index 1f4968d5926..132e58e1dd3 100644 --- a/chromium/media/filters/decrypting_video_decoder.cc +++ b/chromium/media/filters/decrypting_video_decoder.cc @@ -47,9 +47,9 @@ void DecryptingVideoDecoder::Initialize( DVLOG(2) << __func__ << ": " << config.AsHumanReadableString(); DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kUninitialized || - state_ == kIdle || - state_ == kDecodeFinished) << state_; + DCHECK(state_ == kUninitialized || state_ == kIdle || + state_ == kDecodeFinished) + << state_; DCHECK(decode_cb_.is_null()); DCHECK(reset_cb_.is_null()); DCHECK(config.IsValidConfig()); @@ -80,7 +80,7 @@ void DecryptingVideoDecoder::Initialize( if (state_ == kUninitialized) { if (!cdm_context->GetDecryptor()) { - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() << ": no decryptor"; + DVLOG(1) << __func__ << ": no decryptor"; base::ResetAndReturn(&init_cb_).Run(false); return; } @@ -102,9 +102,8 @@ void DecryptingVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer, const DecodeCB& decode_cb) { DVLOG(3) << "Decode()"; DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kIdle || - state_ == kDecodeFinished || - state_ == kError) << state_; + DCHECK(state_ == kIdle || state_ == kDecodeFinished || state_ == kError) + << state_; DCHECK(!decode_cb.is_null()); CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported."; @@ -129,11 +128,10 @@ void DecryptingVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer, void DecryptingVideoDecoder::Reset(const base::Closure& closure) { DVLOG(2) << "Reset() - state: " << state_; DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kIdle || - state_ == kPendingDecode || - state_ == kWaitingForKey || - state_ == kDecodeFinished || - state_ == kError) << state_; + DCHECK(state_ == kIdle || state_ == kPendingDecode || + state_ == kWaitingForKey || state_ == kDecodeFinished || + state_ == kError) + << state_; DCHECK(init_cb_.is_null()); // No Reset() during pending initialization. DCHECK(reset_cb_.is_null()); @@ -184,12 +182,11 @@ void DecryptingVideoDecoder::FinishInitialization(bool success) { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(state_, kPendingDecoderInit) << state_; DCHECK(!init_cb_.is_null()); - DCHECK(reset_cb_.is_null()); // No Reset() before initialization finished. + DCHECK(reset_cb_.is_null()); // No Reset() before initialization finished. DCHECK(decode_cb_.is_null()); // No Decode() before initialization finished. if (!success) { - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() - << ": failed to init decoder on decryptor"; + DVLOG(1) << __func__ << ": failed to init video decoder on decryptor"; base::ResetAndReturn(&init_cb_).Run(false); decryptor_ = NULL; state_ = kError; @@ -197,16 +194,14 @@ void DecryptingVideoDecoder::FinishInitialization(bool success) { } decryptor_->RegisterNewKeyCB( - Decryptor::kVideo, - BindToCurrentLoop( - base::Bind(&DecryptingVideoDecoder::OnKeyAdded, weak_this_))); + Decryptor::kVideo, BindToCurrentLoop(base::Bind( + &DecryptingVideoDecoder::OnKeyAdded, weak_this_))); // Success! state_ = kIdle; base::ResetAndReturn(&init_cb_).Run(true); } - void DecryptingVideoDecoder::DecodePendingBuffer() { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(state_, kPendingDecode) << state_; @@ -219,8 +214,9 @@ void DecryptingVideoDecoder::DecodePendingBuffer() { } decryptor_->DecryptAndDecodeVideo( - pending_buffer_to_decode_, BindToCurrentLoop(base::Bind( - &DecryptingVideoDecoder::DeliverFrame, weak_this_, buffer_size))); + pending_buffer_to_decode_, + BindToCurrentLoop(base::Bind(&DecryptingVideoDecoder::DeliverFrame, + weak_this_, buffer_size))); } void DecryptingVideoDecoder::DeliverFrame( @@ -233,9 +229,9 @@ void DecryptingVideoDecoder::DeliverFrame( DCHECK(!decode_cb_.is_null()); DCHECK(pending_buffer_to_decode_.get()); - TRACE_EVENT_ASYNC_END2( - "media", "DecryptingVideoDecoder::DecodePendingBuffer", trace_id_, - "buffer_size", buffer_size, "status", status); + TRACE_EVENT_ASYNC_END2("media", "DecryptingVideoDecoder::DecodePendingBuffer", + trace_id_, "buffer_size", buffer_size, "status", + status); bool need_to_try_again_if_nokey_is_returned = key_added_while_decode_pending_; key_added_while_decode_pending_ = false; @@ -262,10 +258,11 @@ void DecryptingVideoDecoder::DeliverFrame( if (status == Decryptor::kNoKey) { std::string key_id = scoped_pending_buffer_to_decode->decrypt_config()->key_id(); - std::string missing_key_id = base::HexEncode(key_id.data(), key_id.size()); - DVLOG(1) << "DeliverFrame() - no key for key ID " << missing_key_id; - MEDIA_LOG(INFO, media_log_) << GetDisplayName() << ": no key for key ID " - << missing_key_id; + std::string log_message = + "no key for key ID " + base::HexEncode(key_id.data(), key_id.size()) + + "; will resume decoding after new usable key is available"; + DVLOG(1) << __func__ << ": " << log_message; + MEDIA_LOG(INFO, media_log_) << GetDisplayName() << ": " << log_message; // Set |pending_buffer_to_decode_| back as we need to try decoding the // pending buffer again when new key is added to the decryptor. @@ -273,8 +270,8 @@ void DecryptingVideoDecoder::DeliverFrame( if (need_to_try_again_if_nokey_is_returned) { // The |state_| is still kPendingDecode. - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key was added, resuming decode"; + MEDIA_LOG(INFO, media_log_) + << GetDisplayName() << ": key was added, resuming decode"; DecodePendingBuffer(); return; } @@ -331,8 +328,8 @@ void DecryptingVideoDecoder::OnKeyAdded() { } if (state_ == kWaitingForKey) { - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key added, resuming decode"; + MEDIA_LOG(INFO, media_log_) + << GetDisplayName() << ": key added, resuming decode"; state_ = kPendingDecode; DecodePendingBuffer(); } diff --git a/chromium/media/filters/decrypting_video_decoder_unittest.cc b/chromium/media/filters/decrypting_video_decoder_unittest.cc index 095dc3acb54..90d131fe88d 100644 --- a/chromium/media/filters/decrypting_video_decoder_unittest.cc +++ b/chromium/media/filters/decrypting_video_decoder_unittest.cc @@ -108,9 +108,8 @@ class DecryptingVideoDecoderTest : public testing::Test { void DecodeAndExpect(scoped_refptr<DecoderBuffer> buffer, DecodeStatus status) { EXPECT_CALL(*this, DecodeDone(status)); - decoder_->Decode(buffer, - base::Bind(&DecryptingVideoDecoderTest::DecodeDone, - base::Unretained(this))); + decoder_->Decode(buffer, base::Bind(&DecryptingVideoDecoderTest::DecodeDone, + base::Unretained(this))); base::RunLoop().RunUntilIdle(); } @@ -136,8 +135,9 @@ class DecryptingVideoDecoderTest : public testing::Test { // Sets up expectations and actions to put DecryptingVideoDecoder in an // active normal decoding state. void EnterNormalDecodingState() { - EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)).WillRepeatedly( - Invoke(this, &DecryptingVideoDecoderTest::DecryptAndDecodeVideo)); + EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) + .WillRepeatedly( + Invoke(this, &DecryptingVideoDecoderTest::DecryptAndDecodeVideo)); EXPECT_CALL(*this, FrameReady(decoded_video_frame_)); for (int i = 0; i < kDecodingDelay + 1; ++i) DecodeAndExpect(encrypted_buffer_, DecodeStatus::OK); @@ -148,8 +148,7 @@ class DecryptingVideoDecoderTest : public testing::Test { // EnterNormalDecodingState() to work. void EnterEndOfStreamState() { // The codec in the |decryptor_| will be flushed. - EXPECT_CALL(*this, FrameReady(decoded_video_frame_)) - .Times(kDecodingDelay); + EXPECT_CALL(*this, FrameReady(decoded_video_frame_)).Times(kDecodingDelay); DecodeAndExpect(DecoderBuffer::CreateEOSBuffer(), DecodeStatus::OK); EXPECT_EQ(0, num_frames_in_decryptor_); } @@ -181,8 +180,8 @@ class DecryptingVideoDecoderTest : public testing::Test { void AbortPendingVideoDecodeCB() { if (!pending_video_decode_cb_.is_null()) { - base::ResetAndReturn(&pending_video_decode_cb_).Run( - Decryptor::kSuccess, scoped_refptr<VideoFrame>(NULL)); + base::ResetAndReturn(&pending_video_decode_cb_) + .Run(Decryptor::kSuccess, scoped_refptr<VideoFrame>(NULL)); } } @@ -299,8 +298,8 @@ TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_DecodeError) { Initialize(); EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kError, - scoped_refptr<VideoFrame>(NULL))); + .WillRepeatedly( + RunCallback<1>(Decryptor::kError, scoped_refptr<VideoFrame>(NULL))); DecodeAndExpect(encrypted_buffer_, DecodeStatus::DECODE_ERROR); @@ -322,8 +321,8 @@ TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringWaitingForKey) { EnterWaitingForKeyState(); EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kSuccess, - decoded_video_frame_)); + .WillRepeatedly( + RunCallback<1>(Decryptor::kSuccess, decoded_video_frame_)); EXPECT_CALL(*this, FrameReady(decoded_video_frame_)); EXPECT_CALL(*this, DecodeDone(DecodeStatus::OK)); key_added_cb_.Run(); @@ -337,15 +336,15 @@ TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringPendingDecode) { EnterPendingDecodeState(); EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kSuccess, - decoded_video_frame_)); + .WillRepeatedly( + RunCallback<1>(Decryptor::kSuccess, decoded_video_frame_)); EXPECT_CALL(*this, FrameReady(decoded_video_frame_)); EXPECT_CALL(*this, DecodeDone(DecodeStatus::OK)); // The video decode callback is returned after the correct decryption key is // added. key_added_cb_.Run(); - base::ResetAndReturn(&pending_video_decode_cb_).Run(Decryptor::kNoKey, - null_video_frame_); + base::ResetAndReturn(&pending_video_decode_cb_) + .Run(Decryptor::kNoKey, null_video_frame_); base::RunLoop().RunUntilIdle(); } diff --git a/chromium/media/filters/demuxer_perftest.cc b/chromium/media/filters/demuxer_perftest.cc index bacffe980b3..d1582abc523 100644 --- a/chromium/media/filters/demuxer_perftest.cc +++ b/chromium/media/filters/demuxer_perftest.cc @@ -40,9 +40,6 @@ class DemuxerHostImpl : public media::DemuxerHost { const Ranges<base::TimeDelta>& ranges) override {} void SetDuration(base::TimeDelta duration) override {} void OnDemuxerError(media::PipelineStatus error) override {} - void AddTextStream(media::DemuxerStream* text_stream, - const media::TextTrackConfig& config) override {} - void RemoveTextStream(media::DemuxerStream* text_stream) override {} private: DISALLOW_COPY_AND_ASSIGN(DemuxerHostImpl); @@ -185,7 +182,7 @@ static void RunDemuxerBenchmark(const std::string& filename) { ASSERT_TRUE(data_source.Initialize(file_path)); Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb = - base::Bind(&OnEncryptedMediaInitData); + base::BindRepeating(&OnEncryptedMediaInitData); Demuxer::MediaTracksUpdatedCB tracks_updated_cb = base::Bind(&OnMediaTracksUpdated); FFmpegDemuxer demuxer(base::ThreadTaskRunnerHandle::Get(), &data_source, @@ -194,9 +191,8 @@ static void RunDemuxerBenchmark(const std::string& filename) { { base::RunLoop run_loop; - demuxer.Initialize( - &demuxer_host, - base::Bind(&QuitLoopWithStatus, run_loop.QuitClosure()), false); + demuxer.Initialize(&demuxer_host, base::Bind(&QuitLoopWithStatus, + run_loop.QuitClosure())); run_loop.Run(); } diff --git a/chromium/media/filters/ffmpeg_demuxer.cc b/chromium/media/filters/ffmpeg_demuxer.cc index 7402ce16ab5..a81098549b6 100644 --- a/chromium/media/filters/ffmpeg_demuxer.cc +++ b/chromium/media/filters/ffmpeg_demuxer.cc @@ -536,7 +536,12 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { buffer->set_timestamp(stream_timestamp - start_time); - if (packet->flags & AV_PKT_FLAG_DISCARD) { + // If the packet is marked for complete discard and it doesn't already have + // any discard padding set, mark the DecoderBuffer for complete discard. We + // don't want to overwrite any existing discard padding since the discard + // padding may refer to frames beyond this packet. + if (packet->flags & AV_PKT_FLAG_DISCARD && + buffer->discard_padding() == DecoderBuffer::DiscardPadding()) { buffer->set_discard_padding( std::make_pair(kInfiniteDuration, base::TimeDelta())); if (buffer->timestamp() < base::TimeDelta()) { @@ -835,21 +840,6 @@ size_t FFmpegDemuxerStream::MemoryUsage() const { return buffer_queue_.data_size(); } -TextKind FFmpegDemuxerStream::GetTextKind() const { - DCHECK_EQ(type_, DemuxerStream::TEXT); - - if (stream_->disposition & AV_DISPOSITION_CAPTIONS) - return kTextCaptions; - - if (stream_->disposition & AV_DISPOSITION_DESCRIPTIONS) - return kTextDescriptions; - - if (stream_->disposition & AV_DISPOSITION_METADATA) - return kTextMetadata; - - return kTextSubtitles; -} - std::string FFmpegDemuxerStream::GetMetadata(const char* key) const { const AVDictionaryEntry* entry = av_dict_get(stream_->metadata, key, NULL, 0); @@ -888,7 +878,6 @@ FFmpegDemuxer::FFmpegDemuxer( media_log_(media_log), bitrate_(0), start_time_(kNoTimestamp), - text_enabled_(false), duration_known_(false), encrypted_media_init_data_cb_(encrypted_media_init_data_cb), media_tracks_updated_cb_(media_tracks_updated_cb), @@ -916,11 +905,9 @@ std::string FFmpegDemuxer::GetDisplayName() const { } void FFmpegDemuxer::Initialize(DemuxerHost* host, - const PipelineStatusCB& status_cb, - bool enable_text_tracks) { + const PipelineStatusCB& status_cb) { DCHECK(task_runner_->BelongsToCurrentThread()); host_ = host; - text_enabled_ = enable_text_tracks; weak_this_ = cancel_pending_seek_factory_.GetWeakPtr(); // Give a WeakPtr to BlockingUrlProtocol since we'll need to release it on the @@ -1106,24 +1093,6 @@ base::TimeDelta FFmpegDemuxer::GetStartTime() const { return std::max(start_time_, base::TimeDelta()); } -void FFmpegDemuxer::AddTextStreams() { - DCHECK(task_runner_->BelongsToCurrentThread()); - - for (const auto& stream : streams_) { - if (!stream || stream->type() != DemuxerStream::TEXT) - continue; - - TextKind kind = stream->GetTextKind(); - std::string title = stream->GetMetadata("title"); - std::string language = stream->GetMetadata("language"); - - // TODO: Implement "id" metadata in FFMPEG. - // See: http://crbug.com/323183 - host_->AddTextStream(stream.get(), - TextTrackConfig(kind, title, language, std::string())); - } -} - int64_t FFmpegDemuxer::GetMemoryUsage() const { int64_t allocation_size = 0; for (const auto& stream : streams_) { @@ -1308,10 +1277,8 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, #endif } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { detected_text_track_count++; - if (codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { - stream->discard = AVDISCARD_ALL; - continue; - } + stream->discard = AVDISCARD_ALL; + continue; } else { stream->discard = AVDISCARD_ALL; continue; @@ -1436,9 +1403,6 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, return; } - if (text_enabled_) - AddTextStreams(); - if (format_context->duration != kNoFFmpegTimestamp) { // If there is a duration value in the container use that to find the // maximum between it and the duration from A/V streams. diff --git a/chromium/media/filters/ffmpeg_demuxer.h b/chromium/media/filters/ffmpeg_demuxer.h index 425958d0244..01579ab0763 100644 --- a/chromium/media/filters/ffmpeg_demuxer.h +++ b/chromium/media/filters/ffmpeg_demuxer.h @@ -214,8 +214,7 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer { // Demuxer implementation. std::string GetDisplayName() const override; void Initialize(DemuxerHost* host, - const PipelineStatusCB& status_cb, - bool enable_text_tracks) override; + const PipelineStatusCB& status_cb) override; void AbortPendingReads() override; void Stop() override; void StartWaitingForSeek(base::TimeDelta seek_time) override; @@ -370,9 +369,6 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer { // time if the file doesn't have an association to Time. base::Time timeline_offset_; - // Whether text streams have been enabled for this demuxer. - bool text_enabled_; - // Set if we know duration of the audio stream. Used when processing end of // stream -- at this moment we definitely know duration. bool duration_known_; diff --git a/chromium/media/filters/ffmpeg_demuxer_unittest.cc b/chromium/media/filters/ffmpeg_demuxer_unittest.cc index b09816a3ba3..24abaff1294 100644 --- a/chromium/media/filters/ffmpeg_demuxer_unittest.cc +++ b/chromium/media/filters/ffmpeg_demuxer_unittest.cc @@ -136,34 +136,27 @@ class FFmpegDemuxerTest : public testing::Test { MOCK_METHOD1(CheckPoint, void(int v)); - void InitializeDemuxerInternal(bool enable_text, - media::PipelineStatus expected_pipeline_status, + void InitializeDemuxerInternal(media::PipelineStatus expected_pipeline_status, base::Time timeline_offset) { if (expected_pipeline_status == PIPELINE_OK) EXPECT_CALL(host_, SetDuration(_)).Times(AnyNumber()); WaitableMessageLoopEvent event; - demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), enable_text); + demuxer_->Initialize(&host_, event.GetPipelineStatusCB()); demuxer_->timeline_offset_ = timeline_offset; event.RunAndWaitForStatus(expected_pipeline_status); } void InitializeDemuxer() { - InitializeDemuxerInternal(/*enable_text=*/false, PIPELINE_OK, base::Time()); - } - - void InitializeDemuxerWithText() { - InitializeDemuxerInternal(/*enable_text=*/true, PIPELINE_OK, base::Time()); + InitializeDemuxerInternal(PIPELINE_OK, base::Time()); } void InitializeDemuxerWithTimelineOffset(base::Time timeline_offset) { - InitializeDemuxerInternal(/*enable_text=*/false, PIPELINE_OK, - timeline_offset); + InitializeDemuxerInternal(PIPELINE_OK, timeline_offset); } void InitializeDemuxerAndExpectPipelineStatus( media::PipelineStatus expected_pipeline_status) { - InitializeDemuxerInternal(/*enable_text=*/false, expected_pipeline_status, - base::Time()); + InitializeDemuxerInternal(expected_pipeline_status, base::Time()); } MOCK_METHOD2(OnReadDoneCalled, void(int, int64_t)); @@ -304,8 +297,9 @@ class FFmpegDemuxerTest : public testing::Test { CreateDataSource(name); - Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb = base::Bind( - &FFmpegDemuxerTest::OnEncryptedMediaInitData, base::Unretained(this)); + Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb = + base::BindRepeating(&FFmpegDemuxerTest::OnEncryptedMediaInitData, + base::Unretained(this)); Demuxer::MediaTracksUpdatedCB tracks_updated_cb = base::Bind( &FFmpegDemuxerTest::OnMediaTracksUpdated, base::Unretained(this)); @@ -337,7 +331,7 @@ TEST_F(FFmpegDemuxerTest, Initialize_OpenFails) { // Simulate avformat_open_input() failing. CreateDemuxer("ten_byte_file"); WaitableMessageLoopEvent event; - demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), true); + demuxer_->Initialize(&host_, event.GetPipelineStatusCB()); event.RunAndWaitForStatus(DEMUXER_ERROR_COULD_NOT_OPEN); } @@ -345,7 +339,7 @@ TEST_F(FFmpegDemuxerTest, Initialize_NoStreams) { // Open a file with no streams whatsoever. CreateDemuxer("no_streams.webm"); WaitableMessageLoopEvent event; - demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), true); + demuxer_->Initialize(&host_, event.GetPipelineStatusCB()); event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); } @@ -353,7 +347,7 @@ TEST_F(FFmpegDemuxerTest, Initialize_NoAudioVideo) { // Open a file containing streams but none of which are audio/video streams. CreateDemuxer("no_audio_video.webm"); WaitableMessageLoopEvent event; - demuxer_->Initialize(&host_, event.GetPipelineStatusCB(), true); + demuxer_->Initialize(&host_, event.GetPipelineStatusCB()); event.RunAndWaitForStatus(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); } @@ -438,35 +432,6 @@ TEST_F(FFmpegDemuxerTest, Initialize_Multitrack) { } #endif -TEST_F(FFmpegDemuxerTest, Initialize_MultitrackText) { - // Open a file containing the following streams: - // Stream #0: Video (VP8) - // Stream #1: Audio (Vorbis) - // Stream #2: Text (WebVTT) - - CreateDemuxer("bear-vp8-webvtt.webm"); - DemuxerStream* text_stream = NULL; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - InitializeDemuxerWithText(); - ASSERT_TRUE(text_stream); - EXPECT_EQ(DemuxerStream::TEXT, text_stream->type()); - - // Video stream should be VP8. - DemuxerStream* stream = GetStream(DemuxerStream::VIDEO); - ASSERT_TRUE(stream); - EXPECT_EQ(DemuxerStream::VIDEO, stream->type()); - EXPECT_EQ(kCodecVP8, stream->video_decoder_config().codec()); - - // Audio stream should be Vorbis. - stream = GetStream(DemuxerStream::AUDIO); - ASSERT_TRUE(stream); - EXPECT_EQ(DemuxerStream::AUDIO, stream->type()); - EXPECT_EQ(kCodecVorbis, stream->audio_decoder_config().codec()); - - EXPECT_EQ(3u, demuxer_->GetAllStreams().size()); -} - TEST_F(FFmpegDemuxerTest, Initialize_Encrypted) { EXPECT_CALL(*this, OnEncryptedMediaInitData( @@ -556,23 +521,6 @@ TEST_F(FFmpegDemuxerTest, Read_Video) { EXPECT_EQ(148778, demuxer_->GetMemoryUsage()); } -TEST_F(FFmpegDemuxerTest, Read_Text) { - // We test that on a successful text packet read. - CreateDemuxer("bear-vp8-webvtt.webm"); - DemuxerStream* text_stream = NULL; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - InitializeDemuxerWithText(); - ASSERT_TRUE(text_stream); - EXPECT_EQ(DemuxerStream::TEXT, text_stream->type()); - - text_stream->Read(NewReadCB(FROM_HERE, 31, 0, true)); - base::RunLoop().Run(); - - text_stream->Read(NewReadCB(FROM_HERE, 19, 500000, true)); - base::RunLoop().Run(); -} - TEST_F(FFmpegDemuxerTest, SeekInitialized_NoVideoStartTime) { CreateDemuxer("audio-start-time-only.webm"); InitializeDemuxer(); @@ -951,39 +899,6 @@ TEST_F(FFmpegDemuxerTest, Read_DiscardDisabledVideoStream) { EXPECT_LT(bytes_read_with_video_disabled, bytes_read_with_video_enabled); } -// WebM text track discarding doesn't work in ffmpeg. http://crbug.com/681886. -TEST_F(FFmpegDemuxerTest, DISABLED_Read_DiscardDisabledTextStream) { - // This test case reads the same video frame twice, first with the text track - // enabled, then with the text track disabled. When the text track is - // disabled, FFmpegDemuxer sets the AVDISCARD_ALL flag on the corresponding - // stream, which allows FFmpeg to choose the initial reading position closer - // to the requested video frame (i.e. closer to seek_target), since it doesn't - // need to consider key frames for the text stream. This results in less data - // being read compared to the case with enabled text track. - const base::TimeDelta seek_target = base::TimeDelta::FromMilliseconds(805); - - CreateDemuxer("bear-vp8-webvtt.webm"); - EXPECT_CALL(host_, AddTextStream(_, _)); - InitializeDemuxerWithText(); - Seek(seek_target); - GetStream(DemuxerStream::VIDEO) - ->Read(NewReadCB(FROM_HERE, 5425, 801000, true)); - base::RunLoop().Run(); - auto bytes_read_with_text_enabled = data_source_->bytes_read_for_testing(); - - Shutdown(); - - CreateDemuxer("bear-vp8-webvtt.webm"); - InitializeDemuxer(); - Seek(seek_target); - GetStream(DemuxerStream::VIDEO) - ->Read(NewReadCB(FROM_HERE, 5425, 801000, true)); - base::RunLoop().Run(); - auto bytes_read_with_text_disabled = data_source_->bytes_read_for_testing(); - - EXPECT_LT(bytes_read_with_text_disabled, bytes_read_with_text_enabled); -} - TEST_F(FFmpegDemuxerTest, Read_EndOfStream) { // Verify that end of stream buffers are created. CreateDemuxer("bear-320x240.webm"); @@ -991,26 +906,6 @@ TEST_F(FFmpegDemuxerTest, Read_EndOfStream) { ReadUntilEndOfStream(GetStream(DemuxerStream::AUDIO)); } -TEST_F(FFmpegDemuxerTest, Read_EndOfStreamText) { - // Verify that end of stream buffers are created. - CreateDemuxer("bear-vp8-webvtt.webm"); - DemuxerStream* text_stream = NULL; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - InitializeDemuxerWithText(); - ASSERT_TRUE(text_stream); - EXPECT_EQ(DemuxerStream::TEXT, text_stream->type()); - - bool got_eos_buffer = false; - const int kMaxBuffers = 10; - for (int i = 0; !got_eos_buffer && i < kMaxBuffers; i++) { - text_stream->Read(base::Bind(&EosOnReadDone, &got_eos_buffer)); - base::RunLoop().Run(); - } - - EXPECT_TRUE(got_eos_buffer); -} - TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration) { // Verify that end of stream buffers are created. CreateDemuxer("bear-320x240.webm"); @@ -1111,58 +1006,6 @@ TEST_F(FFmpegDemuxerTest, CancelledSeek) { event.RunAndWaitForStatus(PIPELINE_OK); } -TEST_F(FFmpegDemuxerTest, SeekText) { - // We're testing that the demuxer frees all queued packets when it receives - // a Seek(). - CreateDemuxer("bear-vp8-webvtt.webm"); - DemuxerStream* text_stream = NULL; - EXPECT_CALL(host_, AddTextStream(_, _)) - .WillOnce(SaveArg<0>(&text_stream)); - InitializeDemuxerWithText(); - ASSERT_TRUE(text_stream); - EXPECT_EQ(DemuxerStream::TEXT, text_stream->type()); - - // Get our streams. - DemuxerStream* video = GetStream(DemuxerStream::VIDEO); - DemuxerStream* audio = GetStream(DemuxerStream::AUDIO); - ASSERT_TRUE(video); - ASSERT_TRUE(audio); - - // Read a text packet and release it. - text_stream->Read(NewReadCB(FROM_HERE, 31, 0, true)); - base::RunLoop().Run(); - - // Issue a simple forward seek, which should discard queued packets. - WaitableMessageLoopEvent event; - demuxer_->Seek(base::TimeDelta::FromMicroseconds(1000000), - event.GetPipelineStatusCB()); - event.RunAndWaitForStatus(PIPELINE_OK); - - // Audio read #1. - audio->Read(NewReadCB(FROM_HERE, 145, 803000, true)); - base::RunLoop().Run(); - - // Audio read #2. - audio->Read(NewReadCB(FROM_HERE, 148, 826000, true)); - base::RunLoop().Run(); - - // Video read #1. - video->Read(NewReadCB(FROM_HERE, 5425, 801000, true)); - base::RunLoop().Run(); - - // Video read #2. - video->Read(NewReadCB(FROM_HERE, 1906, 834000, false)); - base::RunLoop().Run(); - - // Text read #1. - text_stream->Read(NewReadCB(FROM_HERE, 19, 1000000, true)); - base::RunLoop().Run(); - - // Text read #2. - text_stream->Read(NewReadCB(FROM_HERE, 19, 1500000, true)); - base::RunLoop().Run(); -} - TEST_F(FFmpegDemuxerTest, Stop) { // Tests that calling Read() on a stopped demuxer stream immediately deletes // the callback. diff --git a/chromium/media/filters/ffmpeg_glue_unittest.cc b/chromium/media/filters/ffmpeg_glue_unittest.cc index e51019cd661..660b4d4654a 100644 --- a/chromium/media/filters/ffmpeg_glue_unittest.cc +++ b/chromium/media/filters/ffmpeg_glue_unittest.cc @@ -10,7 +10,7 @@ #include "base/logging.h" #include "base/macros.h" -#include "base/test/histogram_tester.h" +#include "base/test/metrics/histogram_tester.h" #include "media/base/container_names.h" #include "media/base/mock_filters.h" #include "media/base/test_data_util.h" diff --git a/chromium/media/filters/frame_buffer_pool.cc b/chromium/media/filters/frame_buffer_pool.cc index 84c09973a1d..1bcbc8df261 100644 --- a/chromium/media/filters/frame_buffer_pool.cc +++ b/chromium/media/filters/frame_buffer_pool.cc @@ -19,8 +19,12 @@ namespace media { struct FrameBufferPool::FrameBuffer { - std::vector<uint8_t> data; - std::vector<uint8_t> alpha_data; + // Not using std::vector<uint8_t> as resize() calls take a really long time + // for large buffers. + std::unique_ptr<uint8_t[]> data; + size_t data_size = 0u; + std::unique_ptr<uint8_t[]> alpha_data; + size_t alpha_data_size = 0u; bool held_by_library = false; // Needs to be a counter since a frame buffer might be used multiple times. int held_by_frame = 0; @@ -63,12 +67,17 @@ uint8_t* FrameBufferPool::GetFrameBuffer(size_t min_size, void** fb_priv) { // Resize the frame buffer if necessary. frame_buffer->held_by_library = true; - if (frame_buffer->data.size() < min_size) - frame_buffer->data.resize(min_size); + if (frame_buffer->data_size < min_size) { + // Free the existing |data| first so that the memory can be reused, + // if possible. Note that the new array is purposely not initialized. + frame_buffer->data.reset(); + frame_buffer->data.reset(new uint8_t[min_size]); + frame_buffer->data_size = min_size; + } // Provide the client with a private identifier. *fb_priv = frame_buffer.get(); - return frame_buffer->data.data(); + return frame_buffer->data.get(); } void FrameBufferPool::ReleaseFrameBuffer(void* fb_priv) { @@ -89,9 +98,14 @@ uint8_t* FrameBufferPool::AllocateAlphaPlaneForFrameBuffer(size_t min_size, auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv); DCHECK(IsUsed(frame_buffer)); - if (frame_buffer->alpha_data.size() < min_size) - frame_buffer->alpha_data.resize(min_size); - return frame_buffer->alpha_data.data(); + if (frame_buffer->alpha_data_size < min_size) { + // Free the existing |alpha_data| first so that the memory can be reused, + // if possible. Note that the new array is purposely not initialized. + frame_buffer->alpha_data.reset(); + frame_buffer->alpha_data.reset(new uint8_t[min_size]); + frame_buffer->alpha_data_size = min_size; + } + return frame_buffer->alpha_data.get(); } base::Closure FrameBufferPool::CreateFrameCallback(void* fb_priv) { @@ -121,8 +135,8 @@ bool FrameBufferPool::OnMemoryDump( size_t bytes_reserved = 0; for (const auto& frame_buffer : frame_buffers_) { if (IsUsed(frame_buffer.get())) - bytes_used += frame_buffer->data.size(); - bytes_reserved += frame_buffer->data.size(); + bytes_used += frame_buffer->data_size + frame_buffer->alpha_data_size; + bytes_reserved += frame_buffer->data_size + frame_buffer->alpha_data_size; } memory_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, diff --git a/chromium/media/filters/frame_processor.cc b/chromium/media/filters/frame_processor.cc index c58ef7e0236..dc6c65f77a2 100644 --- a/chromium/media/filters/frame_processor.cc +++ b/chromium/media/filters/frame_processor.cc @@ -653,8 +653,9 @@ bool FrameProcessor::ProcessFrame(scoped_refptr<StreamParserBuffer> frame, // index.html#sourcebuffer-coded-frame-processing while (true) { // 1. Loop Top: - // Otherwise case: (See SourceBufferState's |auto_update_timestamp_offset_|, - // too). + // Otherwise case: (See also SourceBufferState::OnNewBuffer's conditional + // modification of timestamp_offset after frame processing returns, when + // generate_timestamps_flag is true). // 1.1. Let presentation timestamp be a double precision floating point // representation of the coded frame's presentation timestamp in // seconds. @@ -1020,8 +1021,10 @@ bool FrameProcessor::ProcessFrame(scoped_refptr<StreamParserBuffer> frame, group_end_timestamp_ = frame_end_timestamp; DCHECK(group_end_timestamp_ >= base::TimeDelta()); - // Step 21 is currently handled differently. See SourceBufferState's - // |auto_update_timestamp_offset_|. + // TODO(wolenetz): Step 21 is currently approximated by predicted + // frame_end_time by SourceBufferState::OnNewBuffers(). See + // https://crbug.com/850316. + return true; } diff --git a/chromium/media/filters/frame_processor_unittest.cc b/chromium/media/filters/frame_processor_unittest.cc index 837973c02ee..0989f0ea76d 100644 --- a/chromium/media/filters/frame_processor_unittest.cc +++ b/chromium/media/filters/frame_processor_unittest.cc @@ -352,15 +352,16 @@ class FrameProcessorTest CHANNEL_LAYOUT_STEREO, 1000, EmptyExtraData(), Unencrypted()); frame_processor_->OnPossibleAudioConfigUpdate(decoder_config); - ASSERT_TRUE(audio_->UpdateAudioConfig(decoder_config, &media_log_)); + ASSERT_TRUE( + audio_->UpdateAudioConfig(decoder_config, false, &media_log_)); break; } case DemuxerStream::VIDEO: { ASSERT_FALSE(video_); video_.reset( new ChunkDemuxerStream(DemuxerStream::VIDEO, "2", range_api_)); - ASSERT_TRUE( - video_->UpdateVideoConfig(TestVideoConfig::Normal(), &media_log_)); + ASSERT_TRUE(video_->UpdateVideoConfig(TestVideoConfig::Normal(), false, + &media_log_)); break; } // TODO(wolenetz): Test text coded frame processing. diff --git a/chromium/media/filters/gpu_video_decoder.cc b/chromium/media/filters/gpu_video_decoder.cc index b16ed0a110e..a5783c95b67 100644 --- a/chromium/media/filters/gpu_video_decoder.cc +++ b/chromium/media/filters/gpu_video_decoder.cc @@ -27,7 +27,6 @@ #include "media/base/media_log.h" #include "media/base/media_switches.h" #include "media/base/pipeline_status.h" -#include "media/base/surface_manager.h" #include "media/base/video_decoder_config.h" #include "media/base/video_util.h" #include "media/media_buildflags.h" @@ -146,6 +145,10 @@ static void ReportGpuVideoDecoderInitializeStatusToUMAAndRunCB( cb.Run(success); } +bool GpuVideoDecoder::IsPlatformDecoder() const { + return true; +} + std::string GpuVideoDecoder::GetDisplayName() const { return kDecoderName; } diff --git a/chromium/media/filters/gpu_video_decoder.h b/chromium/media/filters/gpu_video_decoder.h index 65a86c9f29d..494cc021671 100644 --- a/chromium/media/filters/gpu_video_decoder.h +++ b/chromium/media/filters/gpu_video_decoder.h @@ -20,7 +20,6 @@ #include "gpu/command_buffer/common/sync_token.h" #include "media/base/overlay_info.h" #include "media/base/pipeline_status.h" -#include "media/base/surface_manager.h" #include "media/base/video_decoder.h" #include "media/video/video_decode_accelerator.h" @@ -56,6 +55,7 @@ class MEDIA_EXPORT GpuVideoDecoder // VideoDecoder implementation. std::string GetDisplayName() const override; + bool IsPlatformDecoder() const override; void Initialize( const VideoDecoderConfig& config, bool low_delay, diff --git a/chromium/media/filters/pipeline_controller.cc b/chromium/media/filters/pipeline_controller.cc index 706b58138af..e204dc931b8 100644 --- a/chromium/media/filters/pipeline_controller.cc +++ b/chromium/media/filters/pipeline_controller.cc @@ -72,6 +72,7 @@ void PipelineController::Seek(base::TimeDelta time, bool time_updated) { if (time_updated) pending_time_updated_ = true; pending_seeked_cb_ = true; + pending_seek_except_start_ = true; // If we are already seeking to |time|, and the media is static, elide the // seek. @@ -111,6 +112,11 @@ bool PipelineController::IsStable() { return state_ == State::PLAYING; } +bool PipelineController::IsPendingSeek() { + DCHECK(thread_checker_.CalledOnValidThread()); + return pending_seek_except_start_; +} + bool PipelineController::IsSuspended() { DCHECK(thread_checker_.CalledOnValidThread()); return (pending_suspend_ || state_ == State::SUSPENDING || @@ -287,6 +293,7 @@ void PipelineController::Dispatch() { // immediately. pending_startup_ = false; pending_seeked_cb_ = false; + pending_seek_except_start_ = false; bool was_pending_time_updated = pending_time_updated_; pending_time_updated_ = false; seeked_cb_.Run(was_pending_time_updated); @@ -301,6 +308,7 @@ void PipelineController::Stop() { demuxer_ = nullptr; waiting_for_seek_ = false; pending_seeked_cb_ = false; + pending_seek_except_start_ = false; pending_time_updated_ = false; pending_seek_ = false; pending_suspend_ = false; diff --git a/chromium/media/filters/pipeline_controller.h b/chromium/media/filters/pipeline_controller.h index bf80dbb66a9..92a420a079f 100644 --- a/chromium/media/filters/pipeline_controller.h +++ b/chromium/media/filters/pipeline_controller.h @@ -114,6 +114,10 @@ class MEDIA_EXPORT PipelineController { // Returns true if the current target state is suspended. bool IsSuspended(); + // Returns true if Seek() was called and there is a seek operation which has + // not yet completed. + bool IsPendingSeek(); + // Returns true if |pipeline_| is suspended. bool IsPipelineSuspended(); @@ -189,6 +193,9 @@ class MEDIA_EXPORT PipelineController { // issued at the next stable state. bool pending_seeked_cb_ = false; + // Indicates that a seek has occurred from an explicit call to Seek(). + bool pending_seek_except_start_ = false; + // Indicates that time has been changed by a seek, which will be reported at // the next seeked callback. bool pending_time_updated_ = false; diff --git a/chromium/media/filters/source_buffer_state.cc b/chromium/media/filters/source_buffer_state.cc index aa7d625c7b2..2c86688c04d 100644 --- a/chromium/media/filters/source_buffer_state.cc +++ b/chromium/media/filters/source_buffer_state.cc @@ -55,6 +55,17 @@ bool CheckBytestreamTrackIds( return true; } +unsigned GetMSEBufferSizeLimitIfExists(base::StringPiece switch_string) { + auto* command_line = base::CommandLine::ForCurrentProcess(); + unsigned memory_limit; + if (command_line->HasSwitch(switch_string) && + base::StringToUint(command_line->GetSwitchValueASCII(switch_string), + &memory_limit)) { + return memory_limit * 1024 * 1024; + } + return 0; +} + } // namespace // List of time ranges for each SourceBuffer. @@ -126,8 +137,7 @@ SourceBufferState::SourceBufferState( frame_processor_(frame_processor.release()), create_demuxer_stream_cb_(create_demuxer_stream_cb), media_log_(media_log), - state_(UNINITIALIZED), - auto_update_timestamp_offset_(false) { + state_(UNINITIALIZED) { DCHECK(!create_demuxer_stream_cb_.is_null()); DCHECK(frame_processor_); } @@ -137,47 +147,32 @@ SourceBufferState::~SourceBufferState() { } void SourceBufferState::Init( - const StreamParser::InitCB& init_cb, + StreamParser::InitCB init_cb, const std::string& expected_codecs, const StreamParser::EncryptedMediaInitDataCB& encrypted_media_init_data_cb, const NewTextTrackCB& new_text_track_cb) { DCHECK_EQ(state_, UNINITIALIZED); - init_cb_ = init_cb; + init_cb_ = std::move(init_cb); encrypted_media_init_data_cb_ = encrypted_media_init_data_cb; new_text_track_cb_ = new_text_track_cb; + state_ = PENDING_PARSER_CONFIG; + InitializeParser(expected_codecs); +} - std::vector<std::string> expected_codecs_parsed; - SplitCodecsToVector(expected_codecs, &expected_codecs_parsed, false); +void SourceBufferState::ChangeType( + std::unique_ptr<StreamParser> new_stream_parser, + const std::string& new_expected_codecs) { + DCHECK_GE(state_, PENDING_PARSER_CONFIG); + DCHECK_NE(state_, PENDING_PARSER_INIT); + DCHECK(!parsing_media_segment_); - std::vector<AudioCodec> expected_acodecs; - std::vector<VideoCodec> expected_vcodecs; - for (const auto& codec_id : expected_codecs_parsed) { - AudioCodec acodec = StringToAudioCodec(codec_id); - if (acodec != kUnknownAudioCodec) { - expected_audio_codecs_.push_back(acodec); - continue; - } - VideoCodec vcodec = StringToVideoCodec(codec_id); - if (vcodec != kUnknownVideoCodec) { - expected_video_codecs_.push_back(vcodec); - continue; - } - MEDIA_LOG(INFO, media_log_) << "Unrecognized media codec: " << codec_id; - } + // If this source buffer has already handled an initialization segment, avoid + // running |init_cb_| again later. + if (state_ == PARSER_INITIALIZED) + state_ = PENDING_PARSER_RECONFIG; - state_ = PENDING_PARSER_CONFIG; - stream_parser_->Init( - base::Bind(&SourceBufferState::OnSourceInitDone, base::Unretained(this)), - base::Bind(&SourceBufferState::OnNewConfigs, base::Unretained(this), - expected_codecs), - base::Bind(&SourceBufferState::OnNewBuffers, base::Unretained(this)), - new_text_track_cb_.is_null(), - base::Bind(&SourceBufferState::OnEncryptedMediaInitData, - base::Unretained(this)), - base::Bind(&SourceBufferState::OnNewMediaSegment, base::Unretained(this)), - base::Bind(&SourceBufferState::OnEndOfMediaSegment, - base::Unretained(this)), - media_log_); + stream_parser_ = std::move(new_stream_parser); + InitializeParser(new_expected_codecs); } void SourceBufferState::SetSequenceMode(bool sequence_mode) { @@ -545,6 +540,46 @@ bool SourceBufferState::IsSeekWaitingForData() const { return false; } +void SourceBufferState::InitializeParser(const std::string& expected_codecs) { + expected_audio_codecs_.clear(); + expected_video_codecs_.clear(); + + std::vector<std::string> expected_codecs_parsed; + SplitCodecsToVector(expected_codecs, &expected_codecs_parsed, false); + + std::vector<AudioCodec> expected_acodecs; + std::vector<VideoCodec> expected_vcodecs; + for (const auto& codec_id : expected_codecs_parsed) { + AudioCodec acodec = StringToAudioCodec(codec_id); + if (acodec != kUnknownAudioCodec) { + expected_audio_codecs_.push_back(acodec); + continue; + } + VideoCodec vcodec = StringToVideoCodec(codec_id); + if (vcodec != kUnknownVideoCodec) { + expected_video_codecs_.push_back(vcodec); + continue; + } + MEDIA_LOG(INFO, media_log_) << "Unrecognized media codec: " << codec_id; + } + + stream_parser_->Init( + base::BindOnce(&SourceBufferState::OnSourceInitDone, + base::Unretained(this)), + base::BindRepeating(&SourceBufferState::OnNewConfigs, + base::Unretained(this), expected_codecs), + base::BindRepeating(&SourceBufferState::OnNewBuffers, + base::Unretained(this)), + new_text_track_cb_.is_null(), + base::BindRepeating(&SourceBufferState::OnEncryptedMediaInitData, + base::Unretained(this)), + base::BindRepeating(&SourceBufferState::OnNewMediaSegment, + base::Unretained(this)), + base::BindRepeating(&SourceBufferState::OnEndOfMediaSegment, + base::Unretained(this)), + media_log_); +} + bool SourceBufferState::OnNewConfigs( std::string expected_codecs, std::unique_ptr<MediaTracks> tracks, @@ -576,6 +611,11 @@ bool SourceBufferState::OnNewConfigs( std::vector<AudioCodec> expected_acodecs = expected_audio_codecs_; std::vector<VideoCodec> expected_vcodecs = expected_video_codecs_; + // TODO(wolenetz): Once codec strictness is relaxed, we can change + // |allow_codec_changes| to always be true. Until then, we only allow codec + // changes on explicit ChangeType(). + const bool allow_codec_changes = state_ == PENDING_PARSER_RECONFIG; + FrameProcessor::TrackIdChanges track_id_changes; for (const auto& track : tracks->tracks()) { const auto& track_id = track->bytestream_track_id(); @@ -635,7 +675,8 @@ bool SourceBufferState::OnNewConfigs( track->set_id(stream->media_track_id()); frame_processor_->OnPossibleAudioConfigUpdate(audio_config); - success &= stream->UpdateAudioConfig(audio_config, media_log_); + success &= stream->UpdateAudioConfig(audio_config, allow_codec_changes, + media_log_); } else if (track->type() == MediaTrack::Video) { VideoDecoderConfig video_config = tracks->getVideoConfig(track_id); DVLOG(1) << "Video track_id=" << track_id @@ -690,7 +731,8 @@ bool SourceBufferState::OnNewConfigs( } track->set_id(stream->media_track_id()); - success &= stream->UpdateVideoConfig(video_config, media_log_); + success &= stream->UpdateVideoConfig(video_config, allow_codec_changes, + media_log_); } else { MEDIA_LOG(ERROR, media_log_) << "Error: unsupported media track type " << track->type(); @@ -798,6 +840,8 @@ bool SourceBufferState::OnNewConfigs( if (success) { if (state_ == PENDING_PARSER_CONFIG) state_ = PENDING_PARSER_INIT; + if (state_ == PENDING_PARSER_RECONFIG) + state_ = PENDING_PARSER_REINIT; DCHECK(!init_segment_received_cb_.is_null()); init_segment_received_cb_.Run(std::move(tracks)); } @@ -806,32 +850,24 @@ bool SourceBufferState::OnNewConfigs( } void SourceBufferState::SetStreamMemoryLimits() { - auto* cmd_line = base::CommandLine::ForCurrentProcess(); - - std::string audio_buf_limit_switch = - cmd_line->GetSwitchValueASCII(switches::kMSEAudioBufferSizeLimit); - unsigned audio_buf_size_limit = 0; - if (base::StringToUint(audio_buf_limit_switch, &audio_buf_size_limit) && - audio_buf_size_limit > 0) { + size_t audio_buf_size_limit = + GetMSEBufferSizeLimitIfExists(switches::kMSEAudioBufferSizeLimitMb); + if (audio_buf_size_limit) { MEDIA_LOG(INFO, media_log_) << "Custom audio per-track SourceBuffer size limit=" << audio_buf_size_limit; - for (const auto& it : audio_streams_) { + for (const auto& it : audio_streams_) it.second->SetStreamMemoryLimit(audio_buf_size_limit); - } } - std::string video_buf_limit_switch = - cmd_line->GetSwitchValueASCII(switches::kMSEVideoBufferSizeLimit); - unsigned video_buf_size_limit = 0; - if (base::StringToUint(video_buf_limit_switch, &video_buf_size_limit) && - video_buf_size_limit > 0) { + size_t video_buf_size_limit = + GetMSEBufferSizeLimitIfExists(switches::kMSEVideoBufferSizeLimitMb); + if (video_buf_size_limit) { MEDIA_LOG(INFO, media_log_) << "Custom video per-track SourceBuffer size limit=" << video_buf_size_limit; - for (const auto& it : video_streams_) { + for (const auto& it : video_streams_) it.second->SetStreamMemoryLimit(video_buf_size_limit); - } } } @@ -886,9 +922,10 @@ bool SourceBufferState::OnNewBuffers( *timestamp_offset_during_append_; // Calculate the new timestamp offset for audio/video tracks if the stream - // parser has requested automatic updates. - TimeDelta new_timestamp_offset = timestamp_offset_before_processing; - if (auto_update_timestamp_offset_) { + // parser corresponds to MSE MIME type with 'Generate Timestamps Flag' set + // true. + TimeDelta predicted_timestamp_offset = timestamp_offset_before_processing; + if (generate_timestamps_flag()) { TimeDelta min_end_timestamp = kNoTimestamp; for (const auto& it : buffer_queue_map) { const StreamParser::BufferQueue& bufq = it.second; @@ -900,7 +937,7 @@ bool SourceBufferState::OnNewBuffers( } } if (min_end_timestamp != kNoTimestamp) - new_timestamp_offset += min_end_timestamp; + predicted_timestamp_offset += min_end_timestamp; } if (!frame_processor_->ProcessFrames( @@ -910,9 +947,11 @@ bool SourceBufferState::OnNewBuffers( } // Only update the timestamp offset if the frame processor hasn't already. - if (auto_update_timestamp_offset_ && + if (generate_timestamps_flag() && timestamp_offset_before_processing == *timestamp_offset_during_append_) { - *timestamp_offset_during_append_ = new_timestamp_offset; + // TODO(wolenetz): This prediction assumes the last frame in each track + // isn't dropped by append window trimming. See https://crbug.com/850316. + *timestamp_offset_during_append_ = predicted_timestamp_offset; } return true; @@ -927,10 +966,15 @@ void SourceBufferState::OnEncryptedMediaInitData( void SourceBufferState::OnSourceInitDone( const StreamParser::InitParameters& params) { - DCHECK_EQ(state_, PENDING_PARSER_INIT); + // We've either yet-to-run |init_cb_| if pending init, or we've previously + // run it if pending reinit. + DCHECK((!init_cb_.is_null() && state_ == PENDING_PARSER_INIT) || + (init_cb_.is_null() && state_ == PENDING_PARSER_REINIT)); + State old_state = state_; state_ = PARSER_INITIALIZED; - auto_update_timestamp_offset_ = params.auto_update_timestamp_offset; - base::ResetAndReturn(&init_cb_).Run(params); + + if (old_state == PENDING_PARSER_INIT) + std::move(init_cb_).Run(params); } } // namespace media diff --git a/chromium/media/filters/source_buffer_state.h b/chromium/media/filters/source_buffer_state.h index c487344eecd..7bac0c5616d 100644 --- a/chromium/media/filters/source_buffer_state.h +++ b/chromium/media/filters/source_buffer_state.h @@ -44,12 +44,18 @@ class MEDIA_EXPORT SourceBufferState { ~SourceBufferState(); - void Init(const StreamParser::InitCB& init_cb, + void Init(StreamParser::InitCB init_cb, const std::string& expected_codecs, const StreamParser::EncryptedMediaInitDataCB& encrypted_media_init_data_cb, const NewTextTrackCB& new_text_track_cb); + // Reconfigures this source buffer to use |new_stream_parser|. Caller must + // first ensure that ResetParserState() was done to flush any pending frames + // from the old stream parser. + void ChangeType(std::unique_ptr<StreamParser> new_stream_parser, + const std::string& new_expected_codecs); + // Appends new data to the StreamParser. // Returns true if the data was successfully appended. Returns false if an // error occurred. |*timestamp_offset| is used and possibly updated by the @@ -91,6 +97,12 @@ class MEDIA_EXPORT SourceBufferState { // Returns true if currently parsing a media segment, or false otherwise. bool parsing_media_segment() const { return parsing_media_segment_; } + // Returns the 'Generate Timestamps Flag' for this SourceBuffer's byte stream + // format parser as described in the MSE Byte Stream Format Registry. + bool generate_timestamps_flag() const { + return stream_parser_->GetGenerateTimestampsFlag(); + } + // Sets |frame_processor_|'s sequence mode to |sequence_mode|. void SetSequenceMode(bool sequence_mode); @@ -139,16 +151,30 @@ class MEDIA_EXPORT SourceBufferState { const SourceBufferParseWarningCB& parse_warning_cb); private: - // State advances through this list. The intent is to ensure at least one - // config is received prior to parser calling initialization callback, and - // that such initialization callback occurs at most once per parser. + // State advances through this list to PARSER_INITIALIZED. + // The intent is to ensure at least one config is received prior to parser + // calling initialization callback, and that such initialization callback + // occurs at most once per parser. + // PENDING_PARSER_RECONFIG occurs if State had reached PARSER_INITIALIZED + // before changing to a new StreamParser in ChangeType(). In such case, State + // would then advance to PENDING_PARSER_REINIT, then PARSER_INITIALIZED upon + // the next initialization segment parsed, but would not run the + // initialization callback in this case (since such would already have + // occurred on the initial transition from PENDING_PARSER_INIT to + // PARSER_INITIALIZED.) enum State { UNINITIALIZED = 0, PENDING_PARSER_CONFIG, PENDING_PARSER_INIT, - PARSER_INITIALIZED + PARSER_INITIALIZED, + PENDING_PARSER_RECONFIG, + PENDING_PARSER_REINIT }; + // Initializes |stream_parser_|. Also, updates |expected_audio_codecs| and + // |expected_video_codecs|. + void InitializeParser(const std::string& expected_codecs); + // Called by the |stream_parser_| when a new initialization segment is // encountered. // Returns true on a successful call. Returns false if an error occurred while @@ -237,12 +263,6 @@ class MEDIA_EXPORT SourceBufferState { std::vector<AudioCodec> expected_audio_codecs_; std::vector<VideoCodec> expected_video_codecs_; - // Indicates that timestampOffset should be updated automatically during - // OnNewBuffers() based on the earliest end timestamp of the buffers provided. - // TODO(wolenetz): Refactor this function while integrating April 29, 2014 - // changes to MSE spec. See http://crbug.com/371499. - bool auto_update_timestamp_offset_; - DISALLOW_COPY_AND_ASSIGN(SourceBufferState); }; diff --git a/chromium/media/filters/source_buffer_state_unittest.cc b/chromium/media/filters/source_buffer_state_unittest.cc index caa4dd25c19..8448fef6970 100644 --- a/chromium/media/filters/source_buffer_state_unittest.cc +++ b/chromium/media/filters/source_buffer_state_unittest.cc @@ -78,13 +78,20 @@ class SourceBufferStateTest std::unique_ptr<SourceBufferState> CreateAndInitSourceBufferState( const std::string& expected_codecs) { std::unique_ptr<SourceBufferState> sbs = CreateSourceBufferState(); + // Instead of using SaveArg<> to update |new_config_cb_| when mocked Init is + // called, we use a lambda because SaveArg<> doesn't work if any of the + // mocked method's arguments are move-only type. EXPECT_CALL(*mock_stream_parser_, Init(_, _, _, _, _, _, _, _)) - .WillOnce(SaveArg<1>(&new_config_cb_)); - sbs->Init(base::Bind(&SourceBufferStateTest::SourceInitDone, - base::Unretained(this)), + .WillOnce([&](auto init_cb, auto config_cb, auto new_buffers_cb, + auto ignore_text_track, auto encrypted_media_init_data_cb, + auto new_segment_cb, auto end_of_segment_cb, + auto media_log) { new_config_cb_ = config_cb; }); + sbs->Init(base::BindOnce(&SourceBufferStateTest::SourceInitDone, + base::Unretained(this)), expected_codecs, - base::Bind(&SourceBufferStateTest::StreamParserEncryptedInitData, - base::Unretained(this)), + base::BindRepeating( + &SourceBufferStateTest::StreamParserEncryptedInitData, + base::Unretained(this)), base::Bind(&SourceBufferStateTest::StreamParserNewTextTrack, base::Unretained(this))); diff --git a/chromium/media/filters/source_buffer_stream.cc b/chromium/media/filters/source_buffer_stream.cc index ec616d4b0a5..8b0173117cf 100644 --- a/chromium/media/filters/source_buffer_stream.cc +++ b/chromium/media/filters/source_buffer_stream.cc @@ -147,7 +147,7 @@ std::string BufferQueueBuffersToLogString( for (const auto& buf : buffers) { result << "\tdts=" << buf->GetDecodeTimestamp().InMicroseconds() << " " << buf->AsHumanReadableString() - << ", duration_type=" << static_cast<int>(buf->duration_type()) + << ", is_duration_estimated=" << buf->is_duration_estimated() << "\n"; } @@ -1250,7 +1250,7 @@ void SourceBufferStream<RangeClass>::TrimSpliceOverlap( " (bad content) at time " << splice_timestamp.InMicroseconds(); - MEDIA_LOG(ERROR, media_log_) + MEDIA_LOG(WARNING, media_log_) << "Media is badly muxed. Detected " << overlapped_buffers.size() << " overlapping audio buffers at time " << splice_timestamp.InMicroseconds(); @@ -1267,6 +1267,16 @@ void SourceBufferStream<RangeClass>::TrimSpliceOverlap( return; } + // Trimming a buffer with estimated duration is too risky. Estimates are rough + // and what appears to be overlap may really just be a bad estimate. Imprecise + // trimming may lead to loss of AV sync. + if (overlapped_buffer->is_duration_estimated()) { + DVLOG(3) << __func__ << " Skipping audio splice trimming at PTS=" + << splice_timestamp.InMicroseconds() << ". Overlapped buffer has " + << "estimated duration."; + return; + } + // Determine the duration of overlap. base::TimeDelta overlapped_end_time = overlapped_buffer->timestamp() + overlapped_buffer->duration(); @@ -1290,14 +1300,6 @@ void SourceBufferStream<RangeClass>::TrimSpliceOverlap( return; } - // At this point, trimming will go ahead. Log UMAs about the type of duration - // in the original overlapped buffer. The hope is that splicing on - // rough-estimated durations is rare enough that we can disable it outright. - // This would allow more liberal estimates of audio durations. - UMA_HISTOGRAM_ENUMERATION( - "Media.MSE.AudioSpliceDurationType", overlapped_buffer->duration_type(), - static_cast<int>(DurationType::kDurationTypeMax) + 1); - // Trim overlap from the existing buffer. DecoderBuffer::DiscardPadding discard_padding = overlapped_buffer->discard_padding(); @@ -1974,13 +1976,21 @@ base::TimeDelta SourceBufferStream<RangeClass>::GetMaxInterbufferDistance() template <typename RangeClass> bool SourceBufferStream<RangeClass>::UpdateAudioConfig( - const AudioDecoderConfig& config) { + const AudioDecoderConfig& config, + bool allow_codec_change) { DCHECK(!audio_configs_.empty()); DCHECK(video_configs_.empty()); DVLOG(3) << "UpdateAudioConfig."; - if (audio_configs_[0].codec() != config.codec()) { - MEDIA_LOG(ERROR, media_log_) << "Audio codec changes not allowed."; + if (!allow_codec_change && + audio_configs_[append_config_index_].codec() != config.codec()) { + // TODO(wolenetz): When we relax addSourceBuffer() and changeType() codec + // strictness, codec changes should be allowed even without changing the + // bytestream. + // TODO(wolenetz): Remove "experimental" from this error message when + // changeType() ships without needing experimental blink flag. + MEDIA_LOG(ERROR, media_log_) << "Audio codec changes not allowed unless " + "using experimental changeType()."; return false; } @@ -2002,13 +2012,21 @@ bool SourceBufferStream<RangeClass>::UpdateAudioConfig( template <typename RangeClass> bool SourceBufferStream<RangeClass>::UpdateVideoConfig( - const VideoDecoderConfig& config) { + const VideoDecoderConfig& config, + bool allow_codec_change) { DCHECK(!video_configs_.empty()); DCHECK(audio_configs_.empty()); DVLOG(3) << "UpdateVideoConfig."; - if (video_configs_[0].codec() != config.codec()) { - MEDIA_LOG(ERROR, media_log_) << "Video codec changes not allowed."; + if (!allow_codec_change && + video_configs_[append_config_index_].codec() != config.codec()) { + // TODO(wolenetz): When we relax addSourceBuffer() and changeType() codec + // strictness, codec changes should be allowed even without changing the + // bytestream. + // TODO(wolenetz): Remove "experimental" from this error message when + // changeType() ships without needing experimental blink flag. + MEDIA_LOG(ERROR, media_log_) << "Video codec changes not allowed unless " + "using experimental changeType()"; return false; } diff --git a/chromium/media/filters/source_buffer_stream.h b/chromium/media/filters/source_buffer_stream.h index a50e7c36df4..590d88d85cb 100644 --- a/chromium/media/filters/source_buffer_stream.h +++ b/chromium/media/filters/source_buffer_stream.h @@ -164,11 +164,17 @@ class MEDIA_EXPORT SourceBufferStream { // Notifies this object that the audio config has changed and buffers in // future Append() calls should be associated with this new config. - bool UpdateAudioConfig(const AudioDecoderConfig& config); + // If the codec is allowed to change, the caller should set + // |allow_codec_change| to true. + bool UpdateAudioConfig(const AudioDecoderConfig& config, + bool allow_codec_change); // Notifies this object that the video config has changed and buffers in // future Append() calls should be associated with this new config. - bool UpdateVideoConfig(const VideoDecoderConfig& config); + // If the codec is allowed to change, the caller should set + // |allow_codec_change| to true. + bool UpdateVideoConfig(const VideoDecoderConfig& config, + bool allow_codec_change); // Returns the largest distance between two adjacent buffers in this stream, // or an estimate if no two adjacent buffers have been appended to the stream diff --git a/chromium/media/filters/source_buffer_stream_unittest.cc b/chromium/media/filters/source_buffer_stream_unittest.cc index 83ec07e9820..9b57c0a5808 100644 --- a/chromium/media/filters/source_buffer_stream_unittest.cc +++ b/chromium/media/filters/source_buffer_stream_unittest.cc @@ -721,7 +721,7 @@ class SourceBufferStreamTest : public testing::TestWithParam<BufferingApi> { &kDataA, kDataSize, is_keyframe, GetStreamType(), 0); buffer->set_timestamp(buffer_timestamps[0]); if (is_duration_estimated) - buffer->set_duration_type(DurationType::kRoughEstimate); + buffer->set_is_duration_estimated(true); if (buffer_timestamps[1] != buffer_timestamps[0]) { buffer->SetDecodeTimestamp( @@ -3472,7 +3472,7 @@ TEST_P(SourceBufferStreamTest, ConfigChange_Basic) { CheckVideoConfig(video_config_); // Signal a config change. - STREAM_OP(UpdateVideoConfig(new_config)); + STREAM_OP(UpdateVideoConfig(new_config, false)); // Make sure updating the config doesn't change anything since new_config // should not be associated with the buffer GetNextBuffer() will return. @@ -3508,7 +3508,7 @@ TEST_P(SourceBufferStreamTest, ConfigChange_Seek) { Seek(0); NewCodedFrameGroupAppend(0, 5, &kDataA); - STREAM_OP(UpdateVideoConfig(new_config)); + STREAM_OP(UpdateVideoConfig(new_config, false)); NewCodedFrameGroupAppend(5, 5, &kDataB); // Seek to the start of the buffers with the new config and make sure a @@ -4545,6 +4545,27 @@ TEST_P(SourceBufferStreamTest, Audio_NoSpliceForBadOverlap) { CheckNoNextBuffer(); } +TEST_P(SourceBufferStreamTest, Audio_NoSpliceForEstimatedDuration) { + SetAudioStream(); + Seek(0); + + // Append two buffers, the latter having estimated duration. + NewCodedFrameGroupAppend("0D10K 10D10EK"); + CheckExpectedRangesByTimestamp("{ [0,20) }"); + CheckExpectedBuffers("0D10K 10D10EK"); + CheckNoNextBuffer(); + + Seek(0); + + // Add a new frame in a separate coded frame group that falls in the middle of + // the second buffer. In spite of the overlap, no splice should be performed + // due to the overlapped buffer having estimated duration. + NewCodedFrameGroupAppend("15D10K"); + CheckExpectedRangesByTimestamp("{ [0,25) }"); + CheckExpectedBuffers("0D10K 10D10EK 15D10K"); + CheckNoNextBuffer(); +} + TEST_P(SourceBufferStreamTest, Audio_SpliceTrimming_ExistingTrimming) { const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(4); const base::TimeDelta kNoDiscard = base::TimeDelta(); @@ -4688,7 +4709,7 @@ TEST_P(SourceBufferStreamTest, Audio_ConfigChangeWithPreroll) { NewCodedFrameGroupAppend("0K 3K 6K"); // Update the configuration. - STREAM_OP(UpdateAudioConfig(new_config)); + STREAM_OP(UpdateAudioConfig(new_config, false)); // We haven't read any buffers at this point, so the config for the next // buffer at time 0 should still be the original config. @@ -4948,7 +4969,7 @@ TEST_P(SourceBufferStreamTest, ConfigChange_ReSeek) { // Append a few buffers, with a config change in the middle. VideoDecoderConfig new_config = TestVideoConfig::Large(); NewCodedFrameGroupAppend("2000K 2010 2020D10"); - STREAM_OP(UpdateVideoConfig(new_config)); + STREAM_OP(UpdateVideoConfig(new_config, false)); NewCodedFrameGroupAppend("2030K 2040 2050D10"); CheckExpectedRangesByTimestamp("{ [2000,2060) }"); diff --git a/chromium/media/filters/stream_parser_factory.cc b/chromium/media/filters/stream_parser_factory.cc index 8b9ff97eab9..d515e2b0ce7 100644 --- a/chromium/media/filters/stream_parser_factory.cc +++ b/chromium/media/filters/stream_parser_factory.cc @@ -16,6 +16,7 @@ #include "build/build_config.h" #include "media/base/media.h" #include "media/base/media_switches.h" +#include "media/base/video_codecs.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" @@ -92,9 +93,8 @@ static const CodecInfo kOpusCodecInfo = {"opus", CodecInfo::AUDIO, nullptr, CodecInfo::HISTOGRAM_OPUS}; #if BUILDFLAG(ENABLE_AV1_DECODER) -// TODO(dalecurtis): This is not the correct final string. Fix before enabling -// by default. http://crbug.com/784607 -static const CodecInfo kAV1CodecInfo = {"av1", CodecInfo::VIDEO, nullptr, +// Note: Validation of the codec string is handled by the caller. +static const CodecInfo kAV1CodecInfo = {"av01.*", CodecInfo::VIDEO, nullptr, CodecInfo::HISTOGRAM_AV1}; #endif diff --git a/chromium/media/filters/video_frame_stream_unittest.cc b/chromium/media/filters/video_frame_stream_unittest.cc index 6c3f7f3006f..ee116463f87 100644 --- a/chromium/media/filters/video_frame_stream_unittest.cc +++ b/chromium/media/filters/video_frame_stream_unittest.cc @@ -85,8 +85,8 @@ class VideoFrameStreamTest video_frame_stream_.reset(new VideoFrameStream( std::make_unique<VideoFrameStream::StreamTraits>(&media_log_), message_loop_.task_runner(), - base::Bind(&VideoFrameStreamTest::CreateVideoDecodersForTest, - base::Unretained(this)), + base::BindRepeating(&VideoFrameStreamTest::CreateVideoDecodersForTest, + base::Unretained(this)), &media_log_)); video_frame_stream_->set_decoder_change_observer_for_testing(base::Bind( &VideoFrameStreamTest::OnDecoderChanged, base::Unretained(this))); @@ -259,12 +259,14 @@ class VideoFrameStreamTest void Initialize() { pending_initialize_ = true; video_frame_stream_->Initialize( - demuxer_stream_.get(), base::Bind(&VideoFrameStreamTest::OnInitialized, - base::Unretained(this)), + demuxer_stream_.get(), + base::BindOnce(&VideoFrameStreamTest::OnInitialized, + base::Unretained(this)), cdm_context_.get(), - base::Bind(&VideoFrameStreamTest::OnStatistics, base::Unretained(this)), - base::Bind(&VideoFrameStreamTest::OnWaitingForDecryptionKey, - base::Unretained(this))); + base::BindRepeating(&VideoFrameStreamTest::OnStatistics, + base::Unretained(this)), + base::BindRepeating(&VideoFrameStreamTest::OnWaitingForDecryptionKey, + base::Unretained(this))); base::RunLoop().RunUntilIdle(); } diff --git a/chromium/media/filters/video_renderer_algorithm.cc b/chromium/media/filters/video_renderer_algorithm.cc index aa98ee6cc22..68847a95d14 100644 --- a/chromium/media/filters/video_renderer_algorithm.cc +++ b/chromium/media/filters/video_renderer_algorithm.cc @@ -165,14 +165,22 @@ scoped_refptr<VideoFrame> VideoRendererAlgorithm::Render( } // Step 7: Drop frames which occur prior to the frame to be rendered. If any - // frame has a zero render count it should be reported as dropped. + // frame unexpectedly has a zero render count it should be reported as + // dropped. When using cadence some frames may be expected to be skipped and + // should not be counted as dropped. if (frame_to_render > 0) { if (frames_dropped) { for (int i = 0; i < frame_to_render; ++i) { const ReadyFrame& frame = frame_queue_[i]; + + // If a frame was ever rendered, don't count it as dropped. if (frame.render_count != frame.drop_count) continue; + // If we expected to never render the frame, don't count it as dropped. + if (cadence_estimator_.has_cadence() && !frame.ideal_render_count) + continue; + // If frame dropping is disabled, ignore the results of the algorithm // and return the earliest unrendered frame. if (frame_dropping_disabled_) { diff --git a/chromium/media/filters/video_renderer_algorithm_unittest.cc b/chromium/media/filters/video_renderer_algorithm_unittest.cc index ec93d0ec9a9..1dc9e1f34e8 100644 --- a/chromium/media/filters/video_renderer_algorithm_unittest.cc +++ b/chromium/media/filters/video_renderer_algorithm_unittest.cc @@ -1122,22 +1122,15 @@ TEST_F(VideoRendererAlgorithmTest, BestFrameByFractionalCadence) { TickGenerator frame_tg(base::TimeTicks(), test_rate[0]); TickGenerator display_tg(tick_clock_->NowTicks(), test_rate[1]); - const size_t desired_drop_pattern = test_rate[0] / test_rate[1] - 1; scoped_refptr<VideoFrame> current_frame; RunFramePumpTest( true, &frame_tg, &display_tg, - [¤t_frame, desired_drop_pattern, this]( - const scoped_refptr<VideoFrame>& frame, size_t frames_dropped) { + [¤t_frame, this](const scoped_refptr<VideoFrame>& frame, + size_t frames_dropped) { ASSERT_TRUE(frame); - // The first frame should have zero dropped frames, but each Render() - // call after should drop the same number of frames based on the - // fractional cadence. - if (!current_frame) - ASSERT_EQ(0u, frames_dropped); - else - ASSERT_EQ(desired_drop_pattern, frames_dropped); - + // We don't count frames dropped that cadence says we should skip. + ASSERT_EQ(0u, frames_dropped); ASSERT_NE(current_frame, frame); ASSERT_TRUE(is_using_cadence()); current_frame = frame; diff --git a/chromium/media/filters/vpx_video_decoder_unittest.cc b/chromium/media/filters/vpx_video_decoder_unittest.cc index 273687d27a3..611b6cc86b9 100644 --- a/chromium/media/filters/vpx_video_decoder_unittest.cc +++ b/chromium/media/filters/vpx_video_decoder_unittest.cc @@ -297,7 +297,9 @@ TEST_F(VpxVideoDecoderTest, FrameValidAfterPoolDestruction) { } // The test stream uses profile 2, which needs high bit depth support in libvpx. -#if !defined(LIBVPX_NO_HIGH_BIT_DEPTH) +// On ARM we fail to decode the final, duplicate frame, so there is no point in +// running this test (https://crbug.com/864458). +#if !defined(LIBVPX_NO_HIGH_BIT_DEPTH) && !defined(ARCH_CPU_ARM_FAMILY) TEST_F(VpxVideoDecoderTest, MemoryPoolAllowsMultipleDisplay) { // Initialize with dummy data, we could read it from the test clip, but it's // not necessary for this test. @@ -309,36 +311,27 @@ TEST_F(VpxVideoDecoderTest, MemoryPoolAllowsMultipleDisplay) { FFmpegGlue glue(&protocol); ASSERT_TRUE(glue.OpenContext()); - AVPacket packet; + AVPacket packet = {}; while (av_read_frame(glue.format_context(), &packet) >= 0) { - if (Decode(DecoderBuffer::CopyFrom(packet.data, packet.size)) != - DecodeStatus::OK) { - av_packet_unref(&packet); - break; - } + DecodeStatus decode_status = + Decode(DecoderBuffer::CopyFrom(packet.data, packet.size)); av_packet_unref(&packet); + if (decode_status != DecodeStatus::OK) + break; } - // Android returns 25 frames while other platforms return 26 for some reason; - // we don't really care about the exact number. - ASSERT_GE(output_frames_.size(), 25u); - - scoped_refptr<VideoFrame> last_frame = output_frames_.back(); + ASSERT_EQ(output_frames_.size(), 26u); - // Duplicate frame is actually two before the last in this bitstream. - scoped_refptr<VideoFrame> dupe_frame = - output_frames_[output_frames_.size() - 3]; + // The final frame is a duplicate of the third-from-final one. + scoped_refptr<VideoFrame> last_frame = output_frames_[25]; + scoped_refptr<VideoFrame> dupe_frame = output_frames_[23]; -#if !defined(OS_ANDROID) - // Android doesn't seem to expose this bug, but the rest of the test is still - // reasonable to complete even on Android. EXPECT_EQ(last_frame->data(VideoFrame::kYPlane), dupe_frame->data(VideoFrame::kYPlane)); EXPECT_EQ(last_frame->data(VideoFrame::kUPlane), dupe_frame->data(VideoFrame::kUPlane)); EXPECT_EQ(last_frame->data(VideoFrame::kVPlane), dupe_frame->data(VideoFrame::kVPlane)); -#endif // This will release all frames held by the memory pool, but should not // release |last_frame| since we still have a ref despite sharing the same @@ -351,6 +344,6 @@ TEST_F(VpxVideoDecoderTest, MemoryPoolAllowsMultipleDisplay) { memset(last_frame->data(VideoFrame::kYPlane), 0, last_frame->row_bytes(VideoFrame::kYPlane)); } -#endif // !defined(LIBVPX_NO_HIGH_BIT_DEPTH) +#endif // !defined(LIBVPX_NO_HIGH_BIT_DEPTH) && !defined(ARCH_CPU_ARM_FAMILY) } // namespace media |