From 5e3484fcaf2acc8f3cf44db4fbb9a75596440d04 Mon Sep 17 00:00:00 2001 From: "vigneshv@chromium.org" Date: Fri, 6 Sep 2013 05:09:07 +0000 Subject: media: Opus support for WebM in Media Source Matroska's specification of Opus has been standardized here: http://wiki.xiph.org/MatroskaOpus. This CL adds support for the new Matroska elements related to Opus and enables Opus playback in WebM files through Media Source API. It also adds support for end trimming. This is a first CL in a sequence of CLs that will attempt to add various features towards fully functional working of Opus in WebM (both media source and video tag). BUG= Review URL: https://chromiumcodereview.appspot.com/23014009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221574 0039d316-1c4b-4281-b951-d872f2087c98 --- media/base/audio_decoder_config.cc | 18 +++++-- media/base/audio_decoder_config.h | 16 +++++- media/base/decoder_buffer.cc | 3 +- media/base/decoder_buffer.h | 11 ++++ media/base/run_all_unittests.cc | 4 ++ media/ffmpeg/ffmpeg_common.cc | 4 +- media/filters/decrypting_audio_decoder.cc | 8 ++- media/filters/decrypting_audio_decoder_unittest.cc | 3 +- media/filters/decrypting_demuxer_stream.cc | 4 +- media/filters/opus_audio_decoder.cc | 60 +++++++++++++++------- media/filters/opus_audio_decoder.h | 4 -- media/filters/pipeline_integration_test.cc | 23 +++++++++ media/filters/stream_parser_factory.cc | 11 +++- media/mp3/mp3_stream_parser.cc | 3 +- media/mp4/mp4_stream_parser.cc | 3 +- media/webm/webm_audio_client.cc | 15 ++++-- media/webm/webm_audio_client.h | 2 + media/webm/webm_cluster_parser.cc | 30 +++++++++-- media/webm/webm_cluster_parser.h | 8 ++- media/webm/webm_constants.h | 3 ++ media/webm/webm_parser.cc | 3 ++ media/webm/webm_tracks_parser.cc | 12 ++++- media/webm/webm_tracks_parser.h | 2 + 23 files changed, 201 insertions(+), 49 deletions(-) (limited to 'media') diff --git a/media/base/audio_decoder_config.cc b/media/base/audio_decoder_config.cc index 38db05d..dfaf94a 100644 --- a/media/base/audio_decoder_config.cc +++ b/media/base/audio_decoder_config.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/metrics/histogram.h" +#include "base/time/time.h" #include "media/audio/sample_rates.h" #include "media/base/limits.h" #include "media/base/sample_format.h" @@ -30,7 +31,8 @@ AudioDecoderConfig::AudioDecoderConfig(AudioCodec codec, size_t extra_data_size, bool is_encrypted) { Initialize(codec, sample_format, channel_layout, samples_per_second, - extra_data, extra_data_size, is_encrypted, true); + extra_data, extra_data_size, is_encrypted, true, + base::TimeDelta(), base::TimeDelta()); } void AudioDecoderConfig::Initialize(AudioCodec codec, @@ -40,7 +42,9 @@ void AudioDecoderConfig::Initialize(AudioCodec codec, const uint8* extra_data, size_t extra_data_size, bool is_encrypted, - bool record_stats) { + bool record_stats, + base::TimeDelta seek_preroll, + base::TimeDelta codec_delay) { CHECK((extra_data_size != 0) == (extra_data != NULL)); if (record_stats) { @@ -66,6 +70,8 @@ void AudioDecoderConfig::Initialize(AudioCodec codec, bytes_per_channel_ = SampleFormatToBytesPerChannel(sample_format); extra_data_.assign(extra_data, extra_data + extra_data_size); is_encrypted_ = is_encrypted; + seek_preroll_ = seek_preroll; + codec_delay_ = codec_delay; int channels = ChannelLayoutToChannelCount(channel_layout_); bytes_per_frame_ = channels * bytes_per_channel_; @@ -80,7 +86,9 @@ bool AudioDecoderConfig::IsValidConfig() const { bytes_per_channel_ <= limits::kMaxBytesPerSample && samples_per_second_ > 0 && samples_per_second_ <= limits::kMaxSampleRate && - sample_format_ != kUnknownSampleFormat; + sample_format_ != kUnknownSampleFormat && + seek_preroll_ >= base::TimeDelta() && + codec_delay_ >= base::TimeDelta(); } bool AudioDecoderConfig::Matches(const AudioDecoderConfig& config) const { @@ -92,7 +100,9 @@ bool AudioDecoderConfig::Matches(const AudioDecoderConfig& config) const { (!extra_data() || !memcmp(extra_data(), config.extra_data(), extra_data_size())) && (is_encrypted() == config.is_encrypted()) && - (sample_format() == config.sample_format())); + (sample_format() == config.sample_format()) && + (seek_preroll() == config.seek_preroll()) && + (codec_delay() == config.codec_delay())); } } // namespace media diff --git a/media/base/audio_decoder_config.h b/media/base/audio_decoder_config.h index 1c61e70..a17d221 100644 --- a/media/base/audio_decoder_config.h +++ b/media/base/audio_decoder_config.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/time/time.h" #include "media/base/channel_layout.h" #include "media/base/media_export.h" #include "media/base/sample_format.h" @@ -63,7 +64,9 @@ class MEDIA_EXPORT AudioDecoderConfig { void Initialize(AudioCodec codec, SampleFormat sample_format, ChannelLayout channel_layout, int samples_per_second, const uint8* extra_data, size_t extra_data_size, - bool is_encrypted, bool record_stats); + bool is_encrypted, bool record_stats, + base::TimeDelta seek_preroll, + base::TimeDelta codec_delay); // Returns true if this object has appropriate configuration values, false // otherwise. @@ -80,6 +83,8 @@ class MEDIA_EXPORT AudioDecoderConfig { int samples_per_second() const { return samples_per_second_; } SampleFormat sample_format() const { return sample_format_; } int bytes_per_frame() const { return bytes_per_frame_; } + base::TimeDelta seek_preroll() const { return seek_preroll_; } + base::TimeDelta codec_delay() const { return codec_delay_; } // Optional byte data required to initialize audio decoders such as Vorbis // codebooks. @@ -103,6 +108,15 @@ class MEDIA_EXPORT AudioDecoderConfig { std::vector extra_data_; bool is_encrypted_; + // |seek_preroll_| is the duration of the data that the decoder must decode + // before the decoded data is valid. + base::TimeDelta seek_preroll_; + + // |codec_delay_| is the overall delay overhead added by the codec while + // encoding. This value should be subtracted from each block's timestamp to + // get the actual timestamp. + base::TimeDelta codec_delay_; + // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler // generated copy constructor and assignment operator. Since the extra data is // typically small, the performance impact is minimal. diff --git a/media/base/decoder_buffer.cc b/media/base/decoder_buffer.cc index 9eaa128..d4e7541 100644 --- a/media/base/decoder_buffer.cc +++ b/media/base/decoder_buffer.cc @@ -80,7 +80,8 @@ std::string DecoderBuffer::AsHumanReadableString() { << " duration: " << duration_.InMicroseconds() << " size: " << size_ << " side_data_size: " << side_data_size_ - << " encrypted: " << (decrypt_config_ != NULL); + << " encrypted: " << (decrypt_config_ != NULL) + << " discard_padding (ms): " << discard_padding_.InMilliseconds(); return s.str(); } diff --git a/media/base/decoder_buffer.h b/media/base/decoder_buffer.h index 6cf519f..393e586 100644 --- a/media/base/decoder_buffer.h +++ b/media/base/decoder_buffer.h @@ -105,6 +105,16 @@ class MEDIA_EXPORT DecoderBuffer return side_data_size_; } + base::TimeDelta discard_padding() const { + DCHECK(!end_of_stream()); + return discard_padding_; + } + + void set_discard_padding(const base::TimeDelta discard_padding) { + DCHECK(!end_of_stream()); + discard_padding_ = discard_padding; + } + const DecryptConfig* decrypt_config() const { DCHECK(!end_of_stream()); return decrypt_config_.get(); @@ -142,6 +152,7 @@ class MEDIA_EXPORT DecoderBuffer int side_data_size_; scoped_ptr side_data_; scoped_ptr decrypt_config_; + base::TimeDelta discard_padding_; // Constructor helper method for memory allocations. void Initialize(); diff --git a/media/base/run_all_unittests.cc b/media/base/run_all_unittests.cc index c63f1dc..1c4da93 100644 --- a/media/base/run_all_unittests.cc +++ b/media/base/run_all_unittests.cc @@ -41,6 +41,10 @@ void TestSuiteNoAtExit::Initialize() { media::InitializeMediaLibraryForTesting(); CommandLine* cmd_line = CommandLine::ForCurrentProcess(); cmd_line->AppendSwitch(switches::kEnableMP3StreamParser); + + // Enable Opus support for all media tests. + // TODO(vigneshv): Remove this once the Opus flag is removed or negated. + cmd_line->AppendSwitch(switches::kEnableOpusPlayback); } int main(int argc, char** argv) { diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc index 9693bbb..72b3125 100644 --- a/media/ffmpeg/ffmpeg_common.cc +++ b/media/ffmpeg/ffmpeg_common.cc @@ -291,7 +291,9 @@ static void AVCodecContextToAudioDecoderConfig( codec_context->extradata, codec_context->extradata_size, is_encrypted, - record_stats); + record_stats, + base::TimeDelta(), + base::TimeDelta()); if (codec != kCodecOpus) { DCHECK_EQ(av_get_bytes_per_sample(codec_context->sample_fmt) * 8, config->bits_per_channel()); diff --git a/media/filters/decrypting_audio_decoder.cc b/media/filters/decrypting_audio_decoder.cc index f516674..2c144b4 100644 --- a/media/filters/decrypting_audio_decoder.cc +++ b/media/filters/decrypting_audio_decoder.cc @@ -191,7 +191,9 @@ void DecryptingAudioDecoder::SetDecryptor(Decryptor* decryptor) { input_config.extra_data(), input_config.extra_data_size(), input_config.is_encrypted(), - false); + false, + base::TimeDelta(), + base::TimeDelta()); state_ = kPendingDecoderInit; decryptor_->InitializeAudioDecoder( @@ -282,7 +284,9 @@ void DecryptingAudioDecoder::DecryptAndDecodeBuffer( input_config.extra_data(), input_config.extra_data_size(), input_config.is_encrypted(), - false); + false, + base::TimeDelta(), + base::TimeDelta()); state_ = kPendingConfigChange; decryptor_->DeinitializeDecoder(Decryptor::kAudio); diff --git a/media/filters/decrypting_audio_decoder_unittest.cc b/media/filters/decrypting_audio_decoder_unittest.cc index fb97b91..2f07e23 100644 --- a/media/filters/decrypting_audio_decoder_unittest.cc +++ b/media/filters/decrypting_audio_decoder_unittest.cc @@ -113,7 +113,8 @@ class DecryptingAudioDecoderTest : public testing::Test { .WillOnce(SaveArg<1>(&key_added_cb_)); config_.Initialize(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, 44100, NULL, 0, true, true); + CHANNEL_LAYOUT_STEREO, 44100, NULL, 0, true, true, + base::TimeDelta(), base::TimeDelta()); InitializeAndExpectStatus(config_, PIPELINE_OK); EXPECT_EQ(DecryptingAudioDecoder::kSupportedBitsPerChannel, diff --git a/media/filters/decrypting_demuxer_stream.cc b/media/filters/decrypting_demuxer_stream.cc index 1f183ce..39386e0 100644 --- a/media/filters/decrypting_demuxer_stream.cc +++ b/media/filters/decrypting_demuxer_stream.cc @@ -302,7 +302,9 @@ void DecryptingDemuxerStream::InitializeDecoderConfig() { input_audio_config.extra_data(), input_audio_config.extra_data_size(), false, // Output audio is not encrypted. - false); + false, + base::TimeDelta(), + base::TimeDelta()); break; } diff --git a/media/filters/opus_audio_decoder.cc b/media/filters/opus_audio_decoder.cc index 115799a..b3e903b 100644 --- a/media/filters/opus_audio_decoder.cc +++ b/media/filters/opus_audio_decoder.cc @@ -4,6 +4,8 @@ #include "media/filters/opus_audio_decoder.h" +#include + #include "base/bind.h" #include "base/callback_helpers.h" #include "base/location.h" @@ -250,7 +252,6 @@ OpusAudioDecoder::OpusAudioDecoder( channel_layout_(CHANNEL_LAYOUT_NONE), samples_per_second_(0), last_input_timestamp_(kNoTimestamp()), - output_bytes_to_drop_(0), skip_samples_(0) { } @@ -457,10 +458,24 @@ bool OpusAudioDecoder::ConfigureDecoder() { config, &opus_header); - skip_samples_ = opus_header.skip_samples; - - if (skip_samples_ > 0) - output_bytes_to_drop_ = skip_samples_ * config.bytes_per_frame(); + if (!config.codec_delay().InMicroseconds()) { + // TODO(vigneshv): Replace this with return false once ffmpeg demuxer code + // starts populating the config correctly. + skip_samples_ = opus_header.skip_samples; + } else { + // Convert from seconds to samples. + skip_samples_ = std::ceil(config.codec_delay().InMicroseconds() * + config.samples_per_second() / 1000000.0); + if (skip_samples_ < 0) { + DVLOG(1) << "Invalid file. Incorrect value for codec delay."; + return false; + } + if (skip_samples_ != opus_header.skip_samples) { + DVLOG(1) << "Invalid file. Codec Delay in container does not match the " + << "value in Opus header."; + return false; + } + } uint8 channel_mapping[kMaxVorbisChannels]; memcpy(&channel_mapping, @@ -487,9 +502,6 @@ bool OpusAudioDecoder::ConfigureDecoder() { return false; } - // TODO(tomfinegan): Handle audio delay once the matroska spec is updated - // to represent the value. - bits_per_channel_ = config.bits_per_channel(); channel_layout_ = config.channel_layout(); samples_per_second_ = config.samples_per_second(); @@ -508,7 +520,7 @@ void OpusAudioDecoder::CloseDecoder() { void OpusAudioDecoder::ResetTimestampState() { output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); last_input_timestamp_ = kNoTimestamp(); - output_bytes_to_drop_ = 0; + skip_samples_ = 0; } bool OpusAudioDecoder::Decode(const scoped_refptr& input, @@ -539,16 +551,6 @@ bool OpusAudioDecoder::Decode(const scoped_refptr& input, output_timestamp_helper_->SetBaseTimestamp(input->timestamp()); } - if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) { - int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_); - DCHECK_EQ(dropped_size % kBytesPerChannel, 0); - decoded_audio_data += dropped_size; - decoded_audio_size -= dropped_size; - output_bytes_to_drop_ -= dropped_size; - samples_decoded = decoded_audio_size / - demuxer_stream_->audio_decoder_config().bytes_per_frame(); - } - if (decoded_audio_size > 0) { // Copy the audio samples into an output buffer. uint8* data[] = { decoded_audio_data }; @@ -560,8 +562,28 @@ bool OpusAudioDecoder::Decode(const scoped_refptr& input, output_timestamp_helper_->GetTimestamp(), output_timestamp_helper_->GetFrameDuration(samples_decoded)); output_timestamp_helper_->AddFrames(samples_decoded); + if (skip_samples_ > 0) { + int dropped_size = std::min(samples_decoded, skip_samples_); + output_buffer->get()->TrimStart(dropped_size); + skip_samples_ -= dropped_size; + samples_decoded -= dropped_size; + } + if (input->discard_padding().InMicroseconds() > 0) { + int discard_padding = std::ceil( + input->discard_padding().InMicroseconds() * + samples_per_second_ / 1000000.0); + if (discard_padding < 0 || discard_padding > samples_decoded) { + DVLOG(1) << "Invalid file. Incorrect discard padding value."; + return false; + } + output_buffer->get()->TrimEnd(std::min(samples_decoded, discard_padding)); + samples_decoded -= discard_padding; + } } + decoded_audio_size = + samples_decoded * + demuxer_stream_->audio_decoder_config().bytes_per_frame(); // Decoding finished successfully, update statistics. PipelineStatistics statistics; statistics.audio_bytes_decoded = decoded_audio_size; diff --git a/media/filters/opus_audio_decoder.h b/media/filters/opus_audio_decoder.h index a808ff3..77e8434 100644 --- a/media/filters/opus_audio_decoder.h +++ b/media/filters/opus_audio_decoder.h @@ -70,10 +70,6 @@ class MEDIA_EXPORT OpusAudioDecoder : public AudioDecoder { scoped_ptr output_timestamp_helper_; base::TimeDelta last_input_timestamp_; - // Number of output sample bytes to drop before generating - // output buffers. - int output_bytes_to_drop_; - ReadCB read_cb_; int skip_samples_; diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc index a767b7f..0ce2fd1 100644 --- a/media/filters/pipeline_integration_test.cc +++ b/media/filters/pipeline_integration_test.cc @@ -28,6 +28,7 @@ static const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 }; static const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\""; static const char kWebMVP9[] = "video/webm; codecs=\"vp9\""; static const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\""; +static const char kOpusAudioOnlyWebM[] = "video/webm; codecs=\"opus\""; static const char kVideoOnlyWebM[] = "video/webm; codecs=\"vp8\""; static const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\""; static const char kMP4Video[] = "video/mp4; codecs=\"avc1.4D4041\""; @@ -58,6 +59,8 @@ static const int k640WebMFileDurationMs = 2763; static const int k640IsoFileDurationMs = 2737; static const int k640IsoCencFileDurationMs = 2736; static const int k1280IsoFileDurationMs = 2736; +static const int kOpusEndTrimmingWebMFileDurationMs = 2771; +static const uint32 kOpusEndTrimmingWebMFileAudioBytes = 528676; static const int kVP9WebMFileDurationMs = 2735; static const int kVP8AWebMFileDurationMs = 2700; @@ -534,6 +537,26 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VP8A_WebM) { Stop(); } +TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_Opus_WebM) { + EXPECT_CALL(*this, OnSetOpaque(false)).Times(AnyNumber()); + MockMediaSource source("bear-opus-end-trimming.webm", kOpusAudioOnlyWebM, + kAppendWholeFile); + StartPipelineWithMediaSource(&source); + source.EndOfStream(); + + EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size()); + EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds()); + EXPECT_EQ(kOpusEndTrimmingWebMFileDurationMs, + pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds()); + Play(); + + ASSERT_TRUE(WaitUntilOnEnded()); + EXPECT_EQ(kOpusEndTrimmingWebMFileAudioBytes, + pipeline_->GetStatistics().audio_bytes_decoded); + source.Abort(); + Stop(); +} + TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_WebM) { MockMediaSource source("bear-320x240-16x9-aspect.webm", kWebM, kAppendWholeFile); diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc index 2b2ec9cb..4d2e524 100644 --- a/media/filters/stream_parser_factory.cc +++ b/media/filters/stream_parser_factory.cc @@ -41,6 +41,7 @@ struct CodecInfo { HISTOGRAM_MPEG4AAC, HISTOGRAM_EAC3, HISTOGRAM_MP3, + HISTOGRAM_OPUS, HISTOGRAM_MAX // Must be the last entry. }; @@ -66,6 +67,8 @@ static const CodecInfo kVP9CodecInfo = { "vp9", CodecInfo::VIDEO, NULL, CodecInfo::HISTOGRAM_VP9 }; static const CodecInfo kVorbisCodecInfo = { "vorbis", CodecInfo::AUDIO, NULL, CodecInfo::HISTOGRAM_VORBIS }; +static const CodecInfo kOpusCodecInfo = { "opus", CodecInfo::AUDIO, NULL, + CodecInfo::HISTOGRAM_OPUS }; static const CodecInfo* kVideoWebMCodecs[] = { &kVP8CodecInfo, @@ -75,11 +78,13 @@ static const CodecInfo* kVideoWebMCodecs[] = { &kVP9CodecInfo, #endif &kVorbisCodecInfo, + &kOpusCodecInfo, NULL }; static const CodecInfo* kAudioWebMCodecs[] = { &kVorbisCodecInfo, + &kOpusCodecInfo, NULL }; @@ -233,7 +238,11 @@ static bool VerifyCodec( return false; } #endif - + if (codec_info->tag == CodecInfo::HISTOGRAM_OPUS) { + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + if (!cmd_line->HasSwitch(switches::kEnableOpusPlayback)) + return false; + } if (audio_codecs) audio_codecs->push_back(codec_info->tag); return true; diff --git a/media/mp3/mp3_stream_parser.cc b/media/mp3/mp3_stream_parser.cc index 86348ff..319c868 100644 --- a/media/mp3/mp3_stream_parser.cc +++ b/media/mp3/mp3_stream_parser.cc @@ -378,7 +378,8 @@ int MP3StreamParser::ParseMP3Frame(const uint8* data, int size) { if (!config_.IsValidConfig()) { config_.Initialize(kCodecMP3, kSampleFormatF32, channel_layout, - sample_rate, NULL, 0, false, false); + sample_rate, NULL, 0, false, false, + base::TimeDelta(), base::TimeDelta()); base::TimeDelta base_timestamp; if (timestamp_helper_) diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_stream_parser.cc index 51ed756..26cee44 100644 --- a/media/mp4/mp4_stream_parser.cc +++ b/media/mp4/mp4_stream_parser.cc @@ -257,7 +257,8 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { audio_config.Initialize( codec, sample_format, channel_layout, sample_per_second, extra_data.size() ? &extra_data[0] : NULL, extra_data.size(), - is_audio_track_encrypted_, false); + is_audio_track_encrypted_, false, base::TimeDelta(), + base::TimeDelta()); has_audio_ = true; audio_track_id_ = track->header.track_id; } diff --git a/media/webm/webm_audio_client.cc b/media/webm/webm_audio_client.cc index e52f44b..1ef640c 100644 --- a/media/webm/webm_audio_client.cc +++ b/media/webm/webm_audio_client.cc @@ -26,12 +26,15 @@ void WebMAudioClient::Reset() { bool WebMAudioClient::InitializeConfig( const std::string& codec_id, const std::vector& codec_private, - bool is_encrypted, AudioDecoderConfig* config) { + int64 seek_preroll, int64 codec_delay, bool is_encrypted, + AudioDecoderConfig* config) { DCHECK(config); AudioCodec audio_codec = kUnknownAudioCodec; if (codec_id == "A_VORBIS") { audio_codec = kCodecVorbis; + } else if (codec_id == "A_OPUS") { + audio_codec = kCodecOpus; } else { MEDIA_LOG(log_cb_) << "Unsupported audio codec_id " << codec_id; return false; @@ -63,8 +66,14 @@ bool WebMAudioClient::InitializeConfig( } config->Initialize( - audio_codec, kSampleFormatPlanarF32, channel_layout, - samples_per_second, extra_data, extra_data_size, is_encrypted, true); + audio_codec, + (audio_codec == kCodecOpus) ? kSampleFormatS16 : kSampleFormatPlanarF32, + channel_layout, + samples_per_second, extra_data, extra_data_size, is_encrypted, true, + base::TimeDelta::FromMicroseconds( + (seek_preroll != -1 ? seek_preroll : 0) / 1000), + base::TimeDelta::FromMicroseconds( + (codec_delay != -1 ? codec_delay : 0) / 1000)); return config->IsValidConfig(); } diff --git a/media/webm/webm_audio_client.h b/media/webm/webm_audio_client.h index 1338f5c..7874cec 100644 --- a/media/webm/webm_audio_client.h +++ b/media/webm/webm_audio_client.h @@ -31,6 +31,8 @@ class WebMAudioClient : public WebMParserClient { // audio track element fields. bool InitializeConfig(const std::string& codec_id, const std::vector& codec_private, + const int64 seek_preroll, + const int64 codec_delay, bool is_encrypted, AudioDecoderConfig* config); diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc index 9991d6b..87cccae 100644 --- a/media/webm/webm_cluster_parser.cc +++ b/media/webm/webm_cluster_parser.cc @@ -64,6 +64,7 @@ WebMClusterParser::WebMClusterParser( block_duration_(-1), block_add_id_(-1), block_additional_data_size_(-1), + discard_padding_(-1), cluster_timecode_(-1), cluster_start_time_(kNoTimestamp()), cluster_ended_(false), @@ -137,6 +138,8 @@ WebMParserClient* WebMClusterParser::OnListStart(int id) { block_data_.reset(); block_data_size_ = -1; block_duration_ = -1; + discard_padding_ = -1; + discard_padding_set_ = false; } else if (id == kWebMIdBlockAdditions) { block_add_id_ = -1; block_additional_data_.reset(); @@ -158,13 +161,16 @@ bool WebMClusterParser::OnListEnd(int id) { bool result = ParseBlock(false, block_data_.get(), block_data_size_, block_additional_data_.get(), - block_additional_data_size_, block_duration_); + block_additional_data_size_, block_duration_, + discard_padding_set_ ? discard_padding_ : 0); block_data_.reset(); block_data_size_ = -1; block_duration_ = -1; block_add_id_ = -1; block_additional_data_.reset(); block_additional_data_size_ = -1; + discard_padding_ = -1; + discard_padding_set_ = false; return result; } @@ -180,6 +186,12 @@ bool WebMClusterParser::OnUInt(int id, int64 val) { case kWebMIdBlockAddID: dst = &block_add_id_; break; + case kWebMIdDiscardPadding: + if (discard_padding_set_) + return false; + discard_padding_set_ = true; + discard_padding_ = val; + return true; default: return true; } @@ -191,7 +203,8 @@ bool WebMClusterParser::OnUInt(int id, int64 val) { bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf, int size, const uint8* additional, - int additional_size, int duration) { + int additional_size, int duration, + int64 discard_padding) { if (size < 4) return false; @@ -219,13 +232,14 @@ bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf, const uint8* frame_data = buf + 4; int frame_size = size - (frame_data - buf); return OnBlock(is_simple_block, track_num, timecode, duration, flags, - frame_data, frame_size, additional, additional_size); + frame_data, frame_size, additional, additional_size, + discard_padding); } bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) { switch (id) { case kWebMIdSimpleBlock: - return ParseBlock(true, data, size, NULL, -1, -1); + return ParseBlock(true, data, size, NULL, -1, -1, 0); case kWebMIdBlock: if (block_data_) { @@ -270,7 +284,8 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num, int block_duration, int flags, const uint8* data, int size, - const uint8* additional, int additional_size) { + const uint8* additional, int additional_size, + int64 discard_padding) { DCHECK_GE(size, 0); if (cluster_timecode_ == -1) { MEDIA_LOG(log_cb_) << "Got a block before cluster timecode."; @@ -350,6 +365,11 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num, block_duration * timecode_multiplier_)); } + if (discard_padding != 0) { + buffer->set_discard_padding(base::TimeDelta::FromMicroseconds( + discard_padding / 1000)); + } + return track->AddBuffer(buffer); } diff --git a/media/webm/webm_cluster_parser.h b/media/webm/webm_cluster_parser.h index e156d47..5aa957c 100644 --- a/media/webm/webm_cluster_parser.h +++ b/media/webm/webm_cluster_parser.h @@ -110,10 +110,12 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { virtual bool OnBinary(int id, const uint8* data, int size) OVERRIDE; bool ParseBlock(bool is_simple_block, const uint8* buf, int size, - const uint8* additional, int additional_size, int duration); + const uint8* additional, int additional_size, int duration, + int64 discard_padding); bool OnBlock(bool is_simple_block, int track_num, int timecode, int duration, int flags, const uint8* data, int size, - const uint8* additional, int additional_size); + const uint8* additional, int additional_size, + int64 discard_padding); // Resets the Track objects associated with each text track. void ResetTextTracks(); @@ -137,6 +139,8 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { int64 block_add_id_; scoped_ptr block_additional_data_; int block_additional_data_size_; + int64 discard_padding_; + bool discard_padding_set_; int64 cluster_timecode_; base::TimeDelta cluster_start_time_; diff --git a/media/webm/webm_constants.h b/media/webm/webm_constants.h index cda45e0..8764eeb 100644 --- a/media/webm/webm_constants.h +++ b/media/webm/webm_constants.h @@ -57,6 +57,7 @@ const int kWebMIdChapterTranslateID = 0x69A5; const int kWebMIdChapterUID = 0x73C4; const int kWebMIdCluster = 0x1F43B675; const int kWebMIdCodecDecodeAll = 0xAA; +const int kWebMIdCodecDelay = 0x56AA; const int kWebMIdCodecID = 0x86; const int kWebMIdCodecName = 0x258688; const int kWebMIdCodecPrivate = 0x63A2; @@ -91,6 +92,7 @@ const int kWebMIdCueTrack = 0xF7; const int kWebMIdCueTrackPositions = 0xB7; const int kWebMIdDateUTC = 0x4461; const int kWebMIdDefaultDuration = 0x23E383; +const int kWebMIdDiscardPadding = 0x75A2; const int kWebMIdDisplayHeight = 0x54BA; const int kWebMIdDisplayUnit = 0x54B2; const int kWebMIdDisplayWidth = 0x54B0; @@ -147,6 +149,7 @@ const int kWebMIdSeek = 0x4DBB; const int kWebMIdSeekHead = 0x114D9B74; const int kWebMIdSeekID = 0x53AB; const int kWebMIdSeekPosition = 0x53AC; +const int kWebMIdSeekPreRoll = 0x56BB; const int kWebMIdSegment = 0x18538067; const int kWebMIdSegmentFamily = 0x4444; const int kWebMIdSegmentFilename = 0x7384; diff --git a/media/webm/webm_parser.cc b/media/webm/webm_parser.cc index 30e5c1b5..ab41e2a 100644 --- a/media/webm/webm_parser.cc +++ b/media/webm/webm_parser.cc @@ -118,6 +118,7 @@ static const ElementIdInfo kBlockGroupIds[] = { {UINT, kWebMIdReferencePriority}, {BINARY, kWebMIdReferenceBlock}, {BINARY, kWebMIdCodecState}, + {UINT, kWebMIdDiscardPadding}, {LIST, kWebMIdSlices}, }; @@ -163,6 +164,8 @@ static const ElementIdInfo kTrackEntryIds[] = { {UINT, kWebMIdAttachmentLink}, {UINT, kWebMIdCodecDecodeAll}, {UINT, kWebMIdTrackOverlay}, + {UINT, kWebMIdCodecDelay}, + {UINT, kWebMIdSeekPreRoll}, {LIST, kWebMIdTrackTranslate}, {LIST, kWebMIdVideo}, {LIST, kWebMIdAudio}, diff --git a/media/webm/webm_tracks_parser.cc b/media/webm/webm_tracks_parser.cc index 67bac04..aa28d6f 100644 --- a/media/webm/webm_tracks_parser.cc +++ b/media/webm/webm_tracks_parser.cc @@ -31,6 +31,8 @@ static TextKind CodecIdToTextKind(const std::string& codec_id) { WebMTracksParser::WebMTracksParser(const LogCB& log_cb, bool ignore_text_tracks) : track_type_(-1), track_num_(-1), + seek_preroll_(-1), + codec_delay_(-1), audio_track_num_(-1), video_track_num_(-1), ignore_text_tracks_(ignore_text_tracks), @@ -161,8 +163,8 @@ bool WebMTracksParser::OnListEnd(int id) { DCHECK(!audio_decoder_config_.IsValidConfig()); if (!audio_client_.InitializeConfig( - codec_id_, codec_private_, !audio_encryption_key_id_.empty(), - &audio_decoder_config_)) { + codec_id_, codec_private_, seek_preroll_, codec_delay_, + !audio_encryption_key_id_.empty(), &audio_decoder_config_)) { return false; } } else { @@ -226,6 +228,12 @@ bool WebMTracksParser::OnUInt(int id, int64 val) { case kWebMIdTrackType: dst = &track_type_; break; + case kWebMIdSeekPreRoll: + dst = &seek_preroll_; + break; + case kWebMIdCodecDelay: + dst = &codec_delay_; + break; default: return true; } diff --git a/media/webm/webm_tracks_parser.h b/media/webm/webm_tracks_parser.h index 81588e4..d399320 100644 --- a/media/webm/webm_tracks_parser.h +++ b/media/webm/webm_tracks_parser.h @@ -83,6 +83,8 @@ class MEDIA_EXPORT WebMTracksParser : public WebMParserClient { std::string track_language_; std::string codec_id_; std::vector codec_private_; + int64 seek_preroll_; + int64 codec_delay_; scoped_ptr track_content_encodings_client_; int64 audio_track_num_; -- cgit v1.1