diff options
author | chcunningham <chcunningham@chromium.org> | 2015-10-19 18:42:09 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-20 01:42:56 +0000 |
commit | 9812dd8df19bc65839f48d914a1c4e296107a49a (patch) | |
tree | 71925688be60d95b7a73518e0c704c142ff0ccab | |
parent | 2c81561ee4f6bf0f19810070ce594c2a109b4e03 (diff) | |
download | chromium_src-9812dd8df19bc65839f48d914a1c4e296107a49a.zip chromium_src-9812dd8df19bc65839f48d914a1c4e296107a49a.tar.gz chromium_src-9812dd8df19bc65839f48d914a1c4e296107a49a.tar.bz2 |
Fail playback when VideoDecoderConfig has invalid extra data.
Invalid extra data occurs when:
(extra_data_ptr == NULL) != (extra_data_size == 0)
We were previously crashing (CHECK) on this condition.
{Audio|Video}DecoderConfig now use a vector and pointer/size issues
are detected/handled in the path of creating the FFmpegDemuxerStream.
Skipping presubmit due to formatting issues. I'd like to have some hope of merging this back to dev.
NOPRESUBMIT=true
BUG=517163
TEST=New unit test. Manually verified graceful failure for MSE & SRC=
R=bbudge@chromium.org, gunsch@chromium.org, sandersd@chromium.org, xhwang@chromium.org
Review URL: https://codereview.chromium.org/1396583002
Cr-Commit-Position: refs/heads/master@{#354952}
52 files changed, 544 insertions, 353 deletions
diff --git a/chromecast/common/media/cma_param_traits.cc b/chromecast/common/media/cma_param_traits.cc index ab0f62a..2ae4fd9 100644 --- a/chromecast/common/media/cma_param_traits.cc +++ b/chromecast/common/media/cma_param_traits.cc @@ -34,13 +34,7 @@ void ParamTraits<media::AudioDecoderConfig>::Write( WriteParam(m, p.channel_layout()); WriteParam(m, p.samples_per_second()); WriteParam(m, p.is_encrypted()); - std::vector<uint8> extra_data; - if (p.extra_data_size() > 0) { - extra_data = - std::vector<uint8>(p.extra_data(), - p.extra_data() + p.extra_data_size()); - } - WriteParam(m, extra_data); + WriteParam(m, p.extra_data()); } bool ParamTraits<media::AudioDecoderConfig>::Read( @@ -58,13 +52,8 @@ bool ParamTraits<media::AudioDecoderConfig>::Read( !ReadParam(m, iter, &samples_per_second) || !ReadParam(m, iter, &is_encrypted) || !ReadParam(m, iter, &extra_data)) return false; - const uint8* extra_data_ptr = nullptr; - if (!extra_data.empty()) - extra_data_ptr = &extra_data[0]; *r = media::AudioDecoderConfig(codec, sample_format, channel_layout, - samples_per_second, - extra_data_ptr, extra_data.size(), - is_encrypted); + samples_per_second, extra_data, is_encrypted); return true; } @@ -83,13 +72,7 @@ void ParamTraits<media::VideoDecoderConfig>::Write( WriteParam(m, p.visible_rect()); WriteParam(m, p.natural_size()); WriteParam(m, p.is_encrypted()); - std::vector<uint8> extra_data; - if (p.extra_data_size() > 0) { - extra_data = - std::vector<uint8>(p.extra_data(), - p.extra_data() + p.extra_data_size()); - } - WriteParam(m, extra_data); + WriteParam(m, p.extra_data()); } bool ParamTraits<media::VideoDecoderConfig>::Read( @@ -111,13 +94,9 @@ bool ParamTraits<media::VideoDecoderConfig>::Read( !ReadParam(m, iter, &natural_size) || !ReadParam(m, iter, &is_encrypted) || !ReadParam(m, iter, &extra_data)) return false; - const uint8* extra_data_ptr = nullptr; - if (!extra_data.empty()) - extra_data_ptr = &extra_data[0]; *r = media::VideoDecoderConfig(codec, profile, format, color_space, coded_size, visible_rect, natural_size, - extra_data_ptr, extra_data.size(), - is_encrypted); + extra_data, is_encrypted); return true; } diff --git a/chromecast/media/audio/cast_audio_output_stream.cc b/chromecast/media/audio/cast_audio_output_stream.cc index e55f127..69eda32 100644 --- a/chromecast/media/audio/cast_audio_output_stream.cc +++ b/chromecast/media/audio/cast_audio_output_stream.cc @@ -42,8 +42,6 @@ MediaPipelineBackend::AudioDecoder* InitializeBackend( audio_config.bytes_per_channel = audio_params.bits_per_sample() / 8; audio_config.channel_number = audio_params.channels(); audio_config.samples_per_second = audio_params.sample_rate(); - audio_config.extra_data = nullptr; - audio_config.extra_data_size = 0; audio_config.is_encrypted = false; if (!decoder->SetConfig(audio_config)) diff --git a/chromecast/media/cma/base/decoder_config_adapter.cc b/chromecast/media/cma/base/decoder_config_adapter.cc index b75d5f2..248edd0 100644 --- a/chromecast/media/cma/base/decoder_config_adapter.cc +++ b/chromecast/media/cma/base/decoder_config_adapter.cc @@ -182,8 +182,8 @@ AudioConfig DecoderConfigAdapter::ToCastAudioConfig( ::media::ChannelLayoutToChannelCount(config.channel_layout()), audio_config.samples_per_second = config.samples_per_second(); audio_config.extra_data = - (config.extra_data_size() > 0) ? config.extra_data() : nullptr; - audio_config.extra_data_size = config.extra_data_size(); + (config.extra_data().empty()) ? nullptr : &config.extra_data()[0]; + audio_config.extra_data_size = config.extra_data().size(); audio_config.is_encrypted = config.is_encrypted(); return audio_config; } @@ -191,11 +191,16 @@ AudioConfig DecoderConfigAdapter::ToCastAudioConfig( // static ::media::AudioDecoderConfig DecoderConfigAdapter::ToMediaAudioDecoderConfig( const AudioConfig& config) { + std::vector<uint8_t> extra_data; + if (config.extra_data_size > 0) + extra_data.assign(config.extra_data, + config.extra_data + config.extra_data_size); + return ::media::AudioDecoderConfig( ToMediaAudioCodec(config.codec), ToMediaSampleFormat(config.sample_format), ToMediaChannelLayout(config.channel_number), config.samples_per_second, - config.extra_data, config.extra_data_size, config.is_encrypted); + extra_data, config.is_encrypted); } // static @@ -210,9 +215,9 @@ VideoConfig DecoderConfigAdapter::ToCastVideoConfig( video_config.id = id; video_config.codec = ToVideoCodec(config.codec()); video_config.profile = ToVideoProfile(config.profile()); - video_config.extra_data = (config.extra_data_size() > 0) ? - config.extra_data() : nullptr; - video_config.extra_data_size = config.extra_data_size(); + video_config.extra_data = (config.extra_data().empty()) ? + nullptr : &config.extra_data()[0]; + video_config.extra_data_size = config.extra_data().size(); video_config.is_encrypted = config.is_encrypted(); return video_config; } diff --git a/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.cc b/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.cc index d595bda..2bc40f4 100644 --- a/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.cc +++ b/chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.cc @@ -4,6 +4,8 @@ #include "chromecast/media/cma/ipc_streamer/audio_decoder_config_marshaller.h" +#include <vector> + #include "base/basictypes.h" #include "base/logging.h" #include "chromecast/media/cma/ipc/media_message.h" @@ -24,9 +26,10 @@ void AudioDecoderConfigMarshaller::Write( CHECK(msg->WritePod(config.samples_per_second())); CHECK(msg->WritePod(config.sample_format())); CHECK(msg->WritePod(config.is_encrypted())); - CHECK(msg->WritePod(config.extra_data_size())); - if (config.extra_data_size() > 0) - CHECK(msg->WriteBuffer(config.extra_data(), config.extra_data_size())); + CHECK(msg->WritePod(config.extra_data().size())); + if (!config.extra_data().empty()) + CHECK(msg->WriteBuffer(&config.extra_data()[0], + config.extra_data().size())); } // static @@ -38,7 +41,7 @@ void AudioDecoderConfigMarshaller::Write( int samples_per_second; bool is_encrypted; size_t extra_data_size; - scoped_ptr<uint8[]> extra_data; + std::vector<uint8_t> extra_data; CHECK(msg->ReadPod(&codec)); CHECK(msg->ReadPod(&channel_layout)); @@ -55,15 +58,14 @@ void AudioDecoderConfigMarshaller::Write( CHECK_LE(sample_format, ::media::kSampleFormatMax); CHECK_LT(extra_data_size, kMaxExtraDataSize); if (extra_data_size > 0) { - extra_data.reset(new uint8[extra_data_size]); - CHECK(msg->ReadBuffer(extra_data.get(), extra_data_size)); + extra_data.resize(extra_data_size); + CHECK(msg->ReadBuffer(&extra_data[0], extra_data.size())); } return ::media::AudioDecoderConfig( codec, sample_format, channel_layout, samples_per_second, - extra_data.get(), extra_data_size, - is_encrypted); + extra_data, is_encrypted); } } // namespace media diff --git a/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc b/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc index 1c07352..8cd28ae 100644 --- a/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc +++ b/chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.cc @@ -4,6 +4,8 @@ #include "chromecast/media/cma/ipc_streamer/video_decoder_config_marshaller.h" +#include <vector> + #include "base/basictypes.h" #include "base/logging.h" #include "chromecast/media/cma/ipc/media_message.h" @@ -64,9 +66,10 @@ void VideoDecoderConfigMarshaller::Write( RectMarshaller::Write(config.visible_rect(), msg); SizeMarshaller::Write(config.natural_size(), msg); CHECK(msg->WritePod(config.is_encrypted())); - CHECK(msg->WritePod(config.extra_data_size())); - if (config.extra_data_size() > 0) - CHECK(msg->WriteBuffer(config.extra_data(), config.extra_data_size())); + CHECK(msg->WritePod(config.extra_data().size())); + if (!config.extra_data().empty()) + CHECK(msg->WriteBuffer(&config.extra_data()[0], + config.extra_data().size())); } // static @@ -81,7 +84,7 @@ void VideoDecoderConfigMarshaller::Write( gfx::Size natural_size; bool is_encrypted; size_t extra_data_size; - scoped_ptr<uint8[]> extra_data; + std::vector<uint8_t> extra_data; CHECK(msg->ReadPod(&codec)); CHECK(msg->ReadPod(&profile)); @@ -103,15 +106,14 @@ void VideoDecoderConfigMarshaller::Write( CHECK_LE(color_space, ::media::COLOR_SPACE_MAX); CHECK_LT(extra_data_size, kMaxExtraDataSize); if (extra_data_size > 0) { - extra_data.reset(new uint8[extra_data_size]); - CHECK(msg->ReadBuffer(extra_data.get(), extra_data_size)); + extra_data.resize(extra_data_size); + CHECK(msg->ReadBuffer(&extra_data[0], extra_data.size())); } return ::media::VideoDecoderConfig( codec, profile, format, color_space, coded_size, visible_rect, natural_size, - extra_data.get(), extra_data_size, - is_encrypted); + extra_data, is_encrypted); } } // namespace media diff --git a/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc b/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc index 040f96e..981247c 100644 --- a/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc +++ b/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc @@ -25,6 +25,7 @@ #include "chromecast/media/cma/test/mock_frame_provider.h" #include "media/base/audio_decoder_config.h" #include "media/base/decoder_buffer.h" +#include "media/base/media_util.h" #include "media/base/video_decoder_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -80,13 +81,13 @@ void AudioVideoPipelineImplTest::Initialize( ::media::kSampleFormatS16, ::media::CHANNEL_LAYOUT_STEREO, 44100, - NULL, 0, false); + ::media::EmptyExtraData(), false); std::vector<::media::VideoDecoderConfig> video_configs; video_configs.push_back(::media::VideoDecoderConfig( ::media::kCodecH264, ::media::H264PROFILE_MAIN, ::media::PIXEL_FORMAT_I420, ::media::COLOR_SPACE_UNSPECIFIED, - gfx::Size(640, 480), gfx::Rect(0, 0, 640, 480), gfx::Size(640, 480), NULL, - 0, false)); + gfx::Size(640, 480), gfx::Rect(0, 0, 640, 480), gfx::Size(640, 480), + ::media::EmptyExtraData(), false)); // Frame generation on the producer side. std::vector<FrameGeneratorForTest::FrameSpec> frame_specs; diff --git a/chromecast/media/cma/test/mock_frame_provider.cc b/chromecast/media/cma/test/mock_frame_provider.cc index 27649a7..fdab889 100644 --- a/chromecast/media/cma/test/mock_frame_provider.cc +++ b/chromecast/media/cma/test/mock_frame_provider.cc @@ -13,6 +13,7 @@ #include "chromecast/media/cma/test/frame_generator_for_test.h" #include "media/base/audio_decoder_config.h" #include "media/base/decoder_buffer.h" +#include "media/base/media_util.h" #include "media/base/video_decoder_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect.h" @@ -80,14 +81,15 @@ void MockFrameProvider::DoRead(const ReadCB& read_cb) { video_config = ::media::VideoDecoderConfig( ::media::kCodecH264, ::media::VIDEO_CODEC_PROFILE_UNKNOWN, ::media::PIXEL_FORMAT_YV12, ::media::COLOR_SPACE_UNSPECIFIED, - coded_size, visible_rect, natural_size, NULL, 0, false); + coded_size, visible_rect, natural_size, ::media::EmptyExtraData(), + false); audio_config = ::media::AudioDecoderConfig( ::media::kCodecAAC, ::media::kSampleFormatS16, ::media::CHANNEL_LAYOUT_STEREO, 44100, - NULL, 0, + ::media::EmptyExtraData(), false); } diff --git a/chromecast/renderer/media/demuxer_stream_for_test.cc b/chromecast/renderer/media/demuxer_stream_for_test.cc index 228f1de..89b45ea 100644 --- a/chromecast/renderer/media/demuxer_stream_for_test.cc +++ b/chromecast/renderer/media/demuxer_stream_for_test.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/threading/thread.h" #include "chromecast/renderer/media/demuxer_stream_for_test.h" +#include "base/threading/thread.h" +#include "media/base/media_util.h" + namespace chromecast { namespace media { @@ -60,8 +62,7 @@ void DemuxerStreamForTest::Read(const ReadCB& read_cb) { coded_size, visible_rect, natural_size, - NULL, - 0, + ::media::EmptyExtraData(), false); } diff --git a/content/common/gpu/media/video_encode_accelerator_unittest.cc b/content/common/gpu/media/video_encode_accelerator_unittest.cc index b612bd8..e22bd42 100644 --- a/content/common/gpu/media/video_encode_accelerator_unittest.cc +++ b/content/common/gpu/media/video_encode_accelerator_unittest.cc @@ -28,6 +28,7 @@ #include "media/base/bind_to_current_loop.h" #include "media/base/bitstream_buffer.h" #include "media/base/decoder_buffer.h" +#include "media/base/media_util.h" #include "media/base/test_data_util.h" #include "media/base/video_decoder.h" #include "media/base/video_frame.h" @@ -659,11 +660,11 @@ void VideoFrameQualityValidator::Initialize(const gfx::Size& coded_size, if (IsVP8(profile_)) config.Initialize(media::kCodecVP8, media::VP8PROFILE_ANY, kInputFormat, media::COLOR_SPACE_UNSPECIFIED, coded_size, visible_size, - natural_size, NULL, 0, false); + natural_size, media::EmptyExtraData(), false); else if (IsH264(profile_)) config.Initialize(media::kCodecH264, media::H264PROFILE_MAIN, kInputFormat, media::COLOR_SPACE_UNSPECIFIED, coded_size, visible_size, - natural_size, NULL, 0, false); + natural_size, media::EmptyExtraData(), false); else LOG_ASSERT(0) << "Invalid profile " << profile_; diff --git a/content/renderer/media/android/media_source_delegate.cc b/content/renderer/media/android/media_source_delegate.cc index 139be4a..4c0477b 100644 --- a/content/renderer/media/android/media_source_delegate.cc +++ b/content/renderer/media/android/media_source_delegate.cc @@ -736,8 +736,7 @@ bool MediaSourceDelegate::GetDemuxerConfigFromStream( media::ChannelLayoutToChannelCount(config.channel_layout()); configs->audio_sampling_rate = config.samples_per_second(); configs->is_audio_encrypted = config.is_encrypted(); - configs->audio_extra_data = std::vector<uint8>( - config.extra_data(), config.extra_data() + config.extra_data_size()); + configs->audio_extra_data = config.extra_data(); configs->audio_codec_delay_ns = static_cast<int64_t>( config.codec_delay() * (static_cast<double>(base::Time::kNanosecondsPerSecond) / @@ -752,8 +751,7 @@ bool MediaSourceDelegate::GetDemuxerConfigFromStream( configs->video_codec = config.codec(); configs->video_size = config.natural_size(); configs->is_video_encrypted = config.is_encrypted(); - configs->video_extra_data = std::vector<uint8>( - config.extra_data(), config.extra_data() + config.extra_data_size()); + configs->video_extra_data = config.extra_data(); return true; } return false; diff --git a/content/renderer/pepper/content_decryptor_delegate.cc b/content/renderer/pepper/content_decryptor_delegate.cc index a3596c8..2efb849 100644 --- a/content/renderer/pepper/content_decryptor_delegate.cc +++ b/content/renderer/pepper/content_decryptor_delegate.cc @@ -4,6 +4,8 @@ #include "content/renderer/pepper/content_decryptor_delegate.h" +#include <vector> + #include "base/callback_helpers.h" #include "base/metrics/sparse_histogram.h" #include "base/numerics/safe_conversions.h" @@ -53,27 +55,25 @@ namespace { // reference-count of 0. If |data| is NULL, sets |*resource| to NULL. Returns // true upon success and false if any error happened. bool MakeBufferResource(PP_Instance instance, - const uint8_t* data, - uint32_t size, + const std::vector<uint8_t>& data, scoped_refptr<PPB_Buffer_Impl>* resource) { TRACE_EVENT0("media", "ContentDecryptorDelegate - MakeBufferResource"); DCHECK(resource); - if (!data || !size) { - DCHECK(!data && !size); + if (data.empty()) { resource = NULL; return true; } scoped_refptr<PPB_Buffer_Impl> buffer( - PPB_Buffer_Impl::CreateResource(instance, size)); + PPB_Buffer_Impl::CreateResource(instance, data.size())); if (!buffer.get()) return false; BufferAutoMapper mapper(buffer.get()); - if (!mapper.data() || mapper.size() < size) + if (!mapper.data() || mapper.size() < data.size()) return false; - memcpy(mapper.data(), data, size); + memcpy(mapper.data(), &data[0], data.size()); *resource = buffer; return true; @@ -592,7 +592,6 @@ bool ContentDecryptorDelegate::InitializeAudioDecoder( scoped_refptr<PPB_Buffer_Impl> extra_data_resource; if (!MakeBufferResource(pp_instance_, decoder_config.extra_data(), - decoder_config.extra_data_size(), &extra_data_resource)) { return false; } @@ -621,7 +620,6 @@ bool ContentDecryptorDelegate::InitializeVideoDecoder( scoped_refptr<PPB_Buffer_Impl> extra_data_resource; if (!MakeBufferResource(pp_instance_, decoder_config.extra_data(), - decoder_config.extra_data_size(), &extra_data_resource)) { return false; } diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc index d84ac81..d3c3d1b 100644 --- a/content/renderer/pepper/video_decoder_shim.cc +++ b/content/renderer/pepper/video_decoder_shim.cc @@ -23,6 +23,7 @@ #include "gpu/command_buffer/client/gles2_implementation.h" #include "media/base/decoder_buffer.h" #include "media/base/limits.h" +#include "media/base/media_util.h" #include "media/base/video_decoder.h" #include "media/blink/skcanvas_video_renderer.h" #include "media/filters/ffmpeg_video_decoder.h" @@ -882,8 +883,8 @@ bool VideoDecoderShim::Initialize( codec, profile, media::PIXEL_FORMAT_YV12, media::COLOR_SPACE_UNSPECIFIED, gfx::Size(32, 24), // Small sizes that won't fail. gfx::Rect(32, 24), gfx::Size(32, 24), - NULL /* extra_data */, // TODO(bbudge) Verify this isn't needed. - 0 /* extra_data_size */, false /* decryption */); + // TODO(bbudge): Verify extra data isn't needed. + media::EmptyExtraData(), false /* decryption */); media_task_runner_->PostTask( FROM_HERE, diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn index 0bab261..0e9ec454 100644 --- a/media/base/BUILD.gn +++ b/media/base/BUILD.gn @@ -122,6 +122,8 @@ source_set("base") { "media_resources.h", "media_switches.cc", "media_switches.h", + "media_util.cc", + "media_util.h", "mime_util.cc", "mime_util.h", "moving_average.cc", diff --git a/media/base/audio_decoder_config.cc b/media/base/audio_decoder_config.cc index 6dc5b2f..213cb10 100644 --- a/media/base/audio_decoder_config.cc +++ b/media/base/audio_decoder_config.cc @@ -24,30 +24,26 @@ AudioDecoderConfig::AudioDecoderConfig(AudioCodec codec, SampleFormat sample_format, ChannelLayout channel_layout, int samples_per_second, - const uint8* extra_data, - size_t extra_data_size, + const std::vector<uint8_t>& extra_data, bool is_encrypted) { Initialize(codec, sample_format, channel_layout, samples_per_second, - extra_data, extra_data_size, is_encrypted, base::TimeDelta(), 0); + extra_data, is_encrypted, base::TimeDelta(), 0); } void AudioDecoderConfig::Initialize(AudioCodec codec, SampleFormat sample_format, ChannelLayout channel_layout, int samples_per_second, - const uint8* extra_data, - size_t extra_data_size, + const std::vector<uint8_t>& extra_data, bool is_encrypted, base::TimeDelta seek_preroll, int codec_delay) { - CHECK((extra_data_size != 0) == (extra_data != NULL)); - codec_ = codec; channel_layout_ = channel_layout; samples_per_second_ = samples_per_second; sample_format_ = sample_format; bytes_per_channel_ = SampleFormatToBytesPerChannel(sample_format); - extra_data_.assign(extra_data, extra_data + extra_data_size); + extra_data_ = extra_data; is_encrypted_ = is_encrypted; seek_preroll_ = seek_preroll; codec_delay_ = codec_delay; @@ -75,9 +71,7 @@ bool AudioDecoderConfig::Matches(const AudioDecoderConfig& config) const { (bytes_per_channel() == config.bytes_per_channel()) && (channel_layout() == config.channel_layout()) && (samples_per_second() == config.samples_per_second()) && - (extra_data_size() == config.extra_data_size()) && - (!extra_data() || !memcmp(extra_data(), config.extra_data(), - extra_data_size())) && + (extra_data() == config.extra_data()) && (is_encrypted() == config.is_encrypted()) && (sample_format() == config.sample_format()) && (seek_preroll() == config.seek_preroll()) && @@ -94,7 +88,7 @@ std::string AudioDecoderConfig::AsHumanReadableString() const { << " bytes_per_frame: " << bytes_per_frame() << " seek_preroll: " << seek_preroll().InMilliseconds() << "ms" << " codec_delay: " << codec_delay() - << " has extra data? " << (extra_data() ? "true" : "false") + << " has extra data? " << (extra_data().empty() ? "false" : "true") << " encrypted? " << (is_encrypted() ? "true" : "false"); return s.str(); } diff --git a/media/base/audio_decoder_config.h b/media/base/audio_decoder_config.h index c9c8593..ae72383 100644 --- a/media/base/audio_decoder_config.h +++ b/media/base/audio_decoder_config.h @@ -55,22 +55,22 @@ class MEDIA_EXPORT AudioDecoderConfig { // appropriate values before using. AudioDecoderConfig(); - // Constructs an initialized object. It is acceptable to pass in NULL for - // |extra_data|, otherwise the memory is copied. - AudioDecoderConfig(AudioCodec codec, SampleFormat sample_format, - ChannelLayout channel_layout, int samples_per_second, - const uint8* extra_data, size_t extra_data_size, + // Constructs an initialized object. + AudioDecoderConfig(AudioCodec codec, + SampleFormat sample_format, + ChannelLayout channel_layout, + int samples_per_second, + const std::vector<uint8_t>& extra_data, bool is_encrypted); ~AudioDecoderConfig(); - // Resets the internal state of this object. |codec_delay| is in frames. + // Resets the internal state of this object. |codec_delay| is in frames. void Initialize(AudioCodec codec, SampleFormat sample_format, ChannelLayout channel_layout, int samples_per_second, - const uint8* extra_data, - size_t extra_data_size, + const std::vector<uint8>& extra_data, bool is_encrypted, base::TimeDelta seek_preroll, int codec_delay); @@ -101,10 +101,7 @@ class MEDIA_EXPORT AudioDecoderConfig { // Optional byte data required to initialize audio decoders such as Vorbis // codebooks. - const uint8* extra_data() const { - return extra_data_.empty() ? NULL : &extra_data_[0]; - } - size_t extra_data_size() const { return extra_data_.size(); } + const std::vector<uint8_t>& extra_data() const { return extra_data_; } // Whether the audio stream is potentially encrypted. // Note that in a potentially encrypted audio stream, individual buffers @@ -118,7 +115,7 @@ class MEDIA_EXPORT AudioDecoderConfig { ChannelLayout channel_layout_; int samples_per_second_; int bytes_per_frame_; - std::vector<uint8> extra_data_; + std::vector<uint8_t> extra_data_; bool is_encrypted_; // |seek_preroll_| is the duration of the data that the decoder must decode diff --git a/media/base/fake_demuxer_stream.cc b/media/base/fake_demuxer_stream.cc index 30ff406..fab0396 100644 --- a/media/base/fake_demuxer_stream.cc +++ b/media/base/fake_demuxer_stream.cc @@ -4,6 +4,8 @@ #include "media/base/fake_demuxer_stream.h" +#include <vector> + #include "base/bind.h" #include "base/callback_helpers.h" #include "base/location.h" @@ -12,6 +14,7 @@ #include "base/thread_task_runner_handle.h" #include "media/base/bind_to_current_loop.h" #include "media/base/decoder_buffer.h" +#include "media/base/media_util.h" #include "media/base/test_helpers.h" #include "media/base/timestamp_constants.h" #include "media/base/video_frame.h" @@ -146,7 +149,8 @@ void FakeDemuxerStream::UpdateVideoDecoderConfig() { video_decoder_config_.Initialize(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_YV12, COLOR_SPACE_UNSPECIFIED, next_coded_size_, kVisibleRect, - next_coded_size_, NULL, 0, is_encrypted_); + next_coded_size_, EmptyExtraData(), + is_encrypted_); next_coded_size_.Enlarge(kWidthDelta, kHeightDelta); } diff --git a/media/base/media_util.cc b/media/base/media_util.cc new file mode 100644 index 0000000..bd7929f --- /dev/null +++ b/media/base/media_util.cc @@ -0,0 +1,13 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/media_util.h" + +namespace media { + +std::vector<uint8_t> EmptyExtraData() { + return std::vector<uint8_t>(); +} + +} // namespace media diff --git a/media/base/media_util.h b/media/base/media_util.h new file mode 100644 index 0000000..4e53c9a --- /dev/null +++ b/media/base/media_util.h @@ -0,0 +1,21 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_UTIL_H_ +#define MEDIA_BASE_UTIL_H_ + +#include <stdint.h> +#include <vector> + +#include "media/base/media_export.h" + +namespace media { + +// Simply returns an empty vector. {Audio|Video}DecoderConfig are often +// constructed with empty extra data. +MEDIA_EXPORT std::vector<uint8_t> EmptyExtraData(); + +} // namespace media + +#endif // MEDIA_BASE_UTIL_H_ diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc index 5da16ad..84074bf 100644 --- a/media/base/test_helpers.cc +++ b/media/base/test_helpers.cc @@ -15,6 +15,7 @@ #include "media/base/audio_buffer.h" #include "media/base/bind_to_current_loop.h" #include "media/base/decoder_buffer.h" +#include "media/base/media_util.h" #include "ui/gfx/geometry/rect.h" using ::testing::_; @@ -127,8 +128,8 @@ static VideoDecoderConfig GetTestConfig(VideoCodec codec, return VideoDecoderConfig(codec, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_YV12, COLOR_SPACE_UNSPECIFIED, - coded_size, visible_rect, natural_size, NULL, 0, - is_encrypted); + coded_size, visible_rect, natural_size, + EmptyExtraData(), is_encrypted); } static const gfx::Size kNormalSize(320, 240); diff --git a/media/base/video_decoder_config.cc b/media/base/video_decoder_config.cc index f867ad1..84058e4 100644 --- a/media/base/video_decoder_config.cc +++ b/media/base/video_decoder_config.cc @@ -4,6 +4,8 @@ #include "media/base/video_decoder_config.h" +#include <vector> + #include "base/logging.h" #include "media/base/video_frame.h" @@ -34,13 +36,11 @@ VideoCodec VideoCodecProfileToVideoCodec(VideoCodecProfile profile) { return kUnknownVideoCodec; } - VideoDecoderConfig::VideoDecoderConfig() : codec_(kUnknownVideoCodec), profile_(VIDEO_CODEC_PROFILE_UNKNOWN), format_(PIXEL_FORMAT_UNKNOWN), - is_encrypted_(false) { -} + is_encrypted_(false) {} VideoDecoderConfig::VideoDecoderConfig(VideoCodec codec, VideoCodecProfile profile, @@ -49,11 +49,10 @@ VideoDecoderConfig::VideoDecoderConfig(VideoCodec codec, const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, - const uint8* extra_data, - size_t extra_data_size, + const std::vector<uint8_t>& extra_data, bool is_encrypted) { Initialize(codec, profile, format, color_space, coded_size, visible_rect, - natural_size, extra_data, extra_data_size, is_encrypted); + natural_size, extra_data, is_encrypted); } VideoDecoderConfig::~VideoDecoderConfig() {} @@ -65,11 +64,8 @@ void VideoDecoderConfig::Initialize(VideoCodec codec, const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, - const uint8* extra_data, - size_t extra_data_size, + const std::vector<uint8_t>& extra_data, bool is_encrypted) { - CHECK((extra_data_size != 0) == (extra_data != NULL)); - codec_ = codec; profile_ = profile; format_ = format; @@ -77,7 +73,7 @@ void VideoDecoderConfig::Initialize(VideoCodec codec, coded_size_ = coded_size; visible_rect_ = visible_rect; natural_size_ = natural_size; - extra_data_.assign(extra_data, extra_data + extra_data_size); + extra_data_ = extra_data; is_encrypted_ = is_encrypted; } @@ -96,9 +92,7 @@ bool VideoDecoderConfig::Matches(const VideoDecoderConfig& config) const { (coded_size() == config.coded_size()) && (visible_rect() == config.visible_rect()) && (natural_size() == config.natural_size()) && - (extra_data_size() == config.extra_data_size()) && - (!extra_data() || !memcmp(extra_data(), config.extra_data(), - extra_data_size())) && + (extra_data() == config.extra_data()) && (is_encrypted() == config.is_encrypted())); } @@ -115,7 +109,7 @@ std::string VideoDecoderConfig::AsHumanReadableString() const { << "," << visible_rect().height() << "]" << " natural size: [" << natural_size().width() << "," << natural_size().height() << "]" - << " has extra data? " << (extra_data() ? "true" : "false") + << " has extra data? " << (extra_data().empty() ? "false" : "true") << " encrypted? " << (is_encrypted() ? "true" : "false"); return s.str(); } @@ -146,10 +140,4 @@ std::string VideoDecoderConfig::GetHumanReadableCodecName() const { return ""; } -const uint8* VideoDecoderConfig::extra_data() const { - if (extra_data_.empty()) - return NULL; - return &extra_data_[0]; -} - } // namespace media diff --git a/media/base/video_decoder_config.h b/media/base/video_decoder_config.h index 0dae77c8..3de0d2f 100644 --- a/media/base/video_decoder_config.h +++ b/media/base/video_decoder_config.h @@ -35,8 +35,7 @@ class MEDIA_EXPORT VideoDecoderConfig { const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, - const uint8* extra_data, - size_t extra_data_size, + const std::vector<uint8_t>& extra_data, bool is_encrypted); ~VideoDecoderConfig(); @@ -49,8 +48,7 @@ class MEDIA_EXPORT VideoDecoderConfig { const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, - const uint8* extra_data, - size_t extra_data_size, + const std::vector<uint8_t>& extra_data, bool is_encrypted); // Returns true if this object has appropriate configuration values, false @@ -91,8 +89,7 @@ class MEDIA_EXPORT VideoDecoderConfig { // Optional byte data required to initialize video decoders, such as H.264 // AAVC data. - const uint8* extra_data() const; - size_t extra_data_size() const { return extra_data_.size(); } + const std::vector<uint8_t>& extra_data() const { return extra_data_; } // Whether the video stream is potentially encrypted. // Note that in a potentially encrypted video stream, individual buffers @@ -110,7 +107,7 @@ class MEDIA_EXPORT VideoDecoderConfig { gfx::Rect visible_rect_; gfx::Size natural_size_; - std::vector<uint8> extra_data_; + std::vector<uint8_t> extra_data_; bool is_encrypted_; diff --git a/media/base/video_decoder_config_unittest.cc b/media/base/video_decoder_config_unittest.cc index d3787d2..7885c8f 100644 --- a/media/base/video_decoder_config_unittest.cc +++ b/media/base/video_decoder_config_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "media/base/limits.h" +#include "media/base/media_util.h" #include "media/base/video_decoder_config.h" #include "media/base/video_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -17,8 +18,8 @@ static const gfx::Size kNaturalSize(320, 240); TEST(VideoDecoderConfigTest, Invalid_UnsupportedPixelFormat) { VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_UNKNOWN, COLOR_SPACE_UNSPECIFIED, - kCodedSize, kVisibleRect, kNaturalSize, NULL, 0, - false); + kCodedSize, kVisibleRect, kNaturalSize, + EmptyExtraData(), false); EXPECT_FALSE(config.IsValidConfig()); } @@ -26,7 +27,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorZero) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect, - natural_size, NULL, 0, false); + natural_size, EmptyExtraData(), false); EXPECT_FALSE(config.IsValidConfig()); } @@ -34,7 +35,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorZero) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect, - natural_size, NULL, 0, false); + natural_size, EmptyExtraData(), false); EXPECT_FALSE(config.IsValidConfig()); } @@ -42,7 +43,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorNegative) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect, - natural_size, NULL, 0, false); + natural_size, EmptyExtraData(), false); EXPECT_FALSE(config.IsValidConfig()); } @@ -50,7 +51,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorNegative) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect, - natural_size, NULL, 0, false); + natural_size, EmptyExtraData(), false); EXPECT_FALSE(config.IsValidConfig()); } @@ -60,7 +61,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorTooLarge) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect, - natural_size, NULL, 0, false); + natural_size, EmptyExtraData(), false); EXPECT_FALSE(config.IsValidConfig()); } @@ -71,7 +72,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorTooLarge) { EXPECT_EQ(0, natural_size.width()); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect, - natural_size, NULL, 0, false); + natural_size, EmptyExtraData(), false); EXPECT_FALSE(config.IsValidConfig()); } diff --git a/media/cast/sender/h264_vt_encoder_unittest.cc b/media/cast/sender/h264_vt_encoder_unittest.cc index 7ef7eef..e44d9f8 100644 --- a/media/cast/sender/h264_vt_encoder_unittest.cc +++ b/media/cast/sender/h264_vt_encoder_unittest.cc @@ -15,6 +15,7 @@ #include "media/base/decoder_buffer.h" #include "media/base/media.h" #include "media/base/media_switches.h" +#include "media/base/media_util.h" #include "media/cast/sender/h264_vt_encoder.h" #include "media/cast/sender/video_frame_factory.h" #include "media/cast/test/utility/default_config.h" @@ -308,7 +309,7 @@ TEST_F(H264VideoToolboxEncoderTest, CheckFramesAreDecodable) { VideoDecoderConfig config(kCodecH264, H264PROFILE_MAIN, frame_->format(), COLOR_SPACE_UNSPECIFIED, frame_->coded_size(), frame_->visible_rect(), frame_->natural_size(), - nullptr, 0, false); + EmptyExtraData(), false); scoped_refptr<EndToEndFrameChecker> checker(new EndToEndFrameChecker(config)); VideoEncoder::FrameEncodedCallback cb = diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc index e6d5e91..c9d8669 100644 --- a/media/ffmpeg/ffmpeg_common.cc +++ b/media/ffmpeg/ffmpeg_common.cc @@ -6,6 +6,7 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "base/sha1.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" @@ -285,7 +286,7 @@ static AVSampleFormat SampleFormatToAVSampleFormat(SampleFormat sample_format) { return AV_SAMPLE_FMT_NONE; } -void AVCodecContextToAudioDecoderConfig(const AVCodecContext* codec_context, +bool AVCodecContextToAudioDecoderConfig(const AVCodecContext* codec_context, bool is_encrypted, AudioDecoderConfig* config) { DCHECK_EQ(codec_context->codec_type, AVMEDIA_TYPE_AUDIO); @@ -319,12 +320,26 @@ void AVCodecContextToAudioDecoderConfig(const AVCodecContext* codec_context, codec_context->seek_preroll * 1000000.0 / codec_context->sample_rate); } + // AVStream occasionally has invalid extra data. See http://crbug.com/517163 + if ((codec_context->extradata_size == 0) != + (codec_context->extradata == nullptr)) { + LOG(ERROR) << __FUNCTION__ + << (codec_context->extradata == nullptr ? " NULL" : " Non-NULL") + << " extra data cannot have size of " + << codec_context->extradata_size << "."; + return false; + } + + std::vector<uint8_t> extra_data; + if (codec_context->extradata_size > 0) { + extra_data.assign(codec_context->extradata, + codec_context->extradata + codec_context->extradata_size); + } config->Initialize(codec, sample_format, channel_layout, sample_rate, - codec_context->extradata, - codec_context->extradata_size, + extra_data, is_encrypted, seek_preroll, codec_context->delay); @@ -333,15 +348,19 @@ void AVCodecContextToAudioDecoderConfig(const AVCodecContext* codec_context, DCHECK_EQ(av_get_bytes_per_sample(codec_context->sample_fmt) * 8, config->bits_per_channel()); } + + return true; } -void AVStreamToAudioDecoderConfig(const AVStream* stream, +bool AVStreamToAudioDecoderConfig(const AVStream* stream, AudioDecoderConfig* config) { bool is_encrypted = false; - AVDictionaryEntry* key = av_dict_get(stream->metadata, "enc_key_id", NULL, 0); + AVDictionaryEntry* key = + av_dict_get(stream->metadata, "enc_key_id", nullptr, 0); if (key) is_encrypted = true; - AVCodecContextToAudioDecoderConfig(stream->codec, is_encrypted, config); + return AVCodecContextToAudioDecoderConfig(stream->codec, is_encrypted, + config); } void AudioDecoderConfigToAVCodecContext(const AudioDecoderConfig& config, @@ -358,21 +377,21 @@ void AudioDecoderConfigToAVCodecContext(const AudioDecoderConfig& config, ChannelLayoutToChannelCount(config.channel_layout()); codec_context->sample_rate = config.samples_per_second(); - if (config.extra_data()) { - codec_context->extradata_size = config.extra_data_size(); + if (config.extra_data().empty()) { + codec_context->extradata = nullptr; + codec_context->extradata_size = 0; + } else { + codec_context->extradata_size = config.extra_data().size(); codec_context->extradata = reinterpret_cast<uint8_t*>( - av_malloc(config.extra_data_size() + FF_INPUT_BUFFER_PADDING_SIZE)); - memcpy(codec_context->extradata, config.extra_data(), - config.extra_data_size()); - memset(codec_context->extradata + config.extra_data_size(), '\0', + av_malloc(config.extra_data().size() + FF_INPUT_BUFFER_PADDING_SIZE)); + memcpy(codec_context->extradata, &config.extra_data()[0], + config.extra_data().size()); + memset(codec_context->extradata + config.extra_data().size(), '\0', FF_INPUT_BUFFER_PADDING_SIZE); - } else { - codec_context->extradata = NULL; - codec_context->extradata_size = 0; } } -void AVStreamToVideoDecoderConfig(const AVStream* stream, +bool AVStreamToVideoDecoderConfig(const AVStream* stream, VideoDecoderConfig* config) { gfx::Size coded_size(stream->codec->coded_width, stream->codec->coded_height); @@ -433,12 +452,13 @@ void AVStreamToVideoDecoderConfig(const AVStream* stream, } bool is_encrypted = false; - AVDictionaryEntry* key = av_dict_get(stream->metadata, "enc_key_id", NULL, 0); + AVDictionaryEntry* key = + av_dict_get(stream->metadata, "enc_key_id", nullptr, 0); if (key) is_encrypted = true; AVDictionaryEntry* webm_alpha = - av_dict_get(stream->metadata, "alpha_mode", NULL, 0); + av_dict_get(stream->metadata, "alpha_mode", nullptr, 0); if (webm_alpha && !strcmp(webm_alpha->value, "1")) { format = PIXEL_FORMAT_YV12A; } @@ -453,9 +473,24 @@ void AVStreamToVideoDecoderConfig(const AVStream* stream, : COLOR_SPACE_HD_REC709; } + // AVStream occasionally has invalid extra data. See http://crbug.com/517163 + if ((stream->codec->extradata_size == 0) != + (stream->codec->extradata == nullptr)) { + LOG(ERROR) << __FUNCTION__ + << (stream->codec->extradata == nullptr ? " NULL" : " Non-Null") + << " extra data cannot have size of " + << stream->codec->extradata_size << "."; + return false; + } + + std::vector<uint8_t> extra_data; + if (stream->codec->extradata_size > 0) { + extra_data.assign(stream->codec->extradata, + stream->codec->extradata + stream->codec->extradata_size); + } config->Initialize(codec, profile, format, color_space, coded_size, - visible_rect, natural_size, stream->codec->extradata, - stream->codec->extradata_size, is_encrypted); + visible_rect, natural_size, extra_data, is_encrypted); + return true; } void VideoDecoderConfigToAVCodecContext( @@ -470,17 +505,17 @@ void VideoDecoderConfigToAVCodecContext( if (config.color_space() == COLOR_SPACE_JPEG) codec_context->color_range = AVCOL_RANGE_JPEG; - if (config.extra_data()) { - codec_context->extradata_size = config.extra_data_size(); + if (config.extra_data().empty()) { + codec_context->extradata = nullptr; + codec_context->extradata_size = 0; + } else { + codec_context->extradata_size = config.extra_data().size(); codec_context->extradata = reinterpret_cast<uint8_t*>( - av_malloc(config.extra_data_size() + FF_INPUT_BUFFER_PADDING_SIZE)); - memcpy(codec_context->extradata, config.extra_data(), - config.extra_data_size()); - memset(codec_context->extradata + config.extra_data_size(), '\0', + av_malloc(config.extra_data().size() + FF_INPUT_BUFFER_PADDING_SIZE)); + memcpy(codec_context->extradata, &config.extra_data()[0], + config.extra_data().size()); + memset(codec_context->extradata + config.extra_data().size(), '\0', FF_INPUT_BUFFER_PADDING_SIZE); - } else { - codec_context->extradata = NULL; - codec_context->extradata_size = 0; } } diff --git a/media/ffmpeg/ffmpeg_common.h b/media/ffmpeg/ffmpeg_common.h index 677bd76..f4a8237 100644 --- a/media/ffmpeg/ffmpeg_common.h +++ b/media/ffmpeg/ffmpeg_common.h @@ -85,19 +85,26 @@ MEDIA_EXPORT base::TimeDelta ConvertFromTimeBase(const AVRational& time_base, MEDIA_EXPORT int64 ConvertToTimeBase(const AVRational& time_base, const base::TimeDelta& timestamp); -void AVStreamToAudioDecoderConfig(const AVStream* stream, - AudioDecoderConfig* config); +// Returns true if AVStream is successfully converted to a AudioDecoderConfig. +// Returns false if conversion fails, in which case |config| is not modified. +MEDIA_EXPORT bool AVStreamToAudioDecoderConfig(const AVStream* stream, + AudioDecoderConfig* config); void AudioDecoderConfigToAVCodecContext( const AudioDecoderConfig& config, AVCodecContext* codec_context); -void AVStreamToVideoDecoderConfig(const AVStream* stream, - VideoDecoderConfig* config); +// Returns true if AVStream is successfully converted to a VideoDecoderConfig. +// Returns false if conversion fails, in which case |config| is not modified. +MEDIA_EXPORT bool AVStreamToVideoDecoderConfig(const AVStream* stream, + VideoDecoderConfig* config); void VideoDecoderConfigToAVCodecContext( const VideoDecoderConfig& config, AVCodecContext* codec_context); -MEDIA_EXPORT void AVCodecContextToAudioDecoderConfig( +// Returns true if AVCodecContext is successfully converted to an +// AudioDecoderConfig. Returns false if conversion fails, in which case |config| +// is not modified. +MEDIA_EXPORT bool AVCodecContextToAudioDecoderConfig( const AVCodecContext* codec_context, bool is_encrypted, AudioDecoderConfig* config); diff --git a/media/ffmpeg/ffmpeg_common_unittest.cc b/media/ffmpeg/ffmpeg_common_unittest.cc index ca68fa7..aa8a06d 100644 --- a/media/ffmpeg/ffmpeg_common_unittest.cc +++ b/media/ffmpeg/ffmpeg_common_unittest.cc @@ -2,11 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <cstring> + +#include "base/bind.h" +#include "base/files/memory_mapped_file.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "media/base/audio_decoder_config.h" #include "media/base/media.h" +#include "media/base/test_data_util.h" +#include "media/base/video_decoder_config.h" #include "media/ffmpeg/ffmpeg_common.h" #include "media/filters/ffmpeg_glue.h" +#include "media/filters/in_memory_url_protocol.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { @@ -19,6 +27,97 @@ class FFmpegCommonTest : public testing::Test { ~FFmpegCommonTest() override{}; }; +uint8_t kExtraData[5] = {0x00, 0x01, 0x02, 0x03, 0x04}; + +template <typename T> +void TestConfigConvertExtraData( + AVStream* stream, + T* decoder_config, + const base::Callback<bool(const AVStream*, T*)>& converter_fn) { + // Should initially convert. + EXPECT_TRUE(converter_fn.Run(stream, decoder_config)); + + // Store orig to let FFmpeg free whatever it allocated. + AVCodecContext* codec_context = stream->codec; + uint8_t* orig_extradata = codec_context->extradata; + int orig_extradata_size = codec_context->extradata_size; + + // Valid combination: extra_data = NULL && size = 0. + codec_context->extradata = NULL; + codec_context->extradata_size = 0; + EXPECT_TRUE(converter_fn.Run(stream, decoder_config)); + EXPECT_EQ(static_cast<size_t>(codec_context->extradata_size), + decoder_config->extra_data().size()); + + // Valid combination: extra_data = non-NULL && size > 0. + codec_context->extradata = &kExtraData[0]; + codec_context->extradata_size = arraysize(kExtraData); + EXPECT_TRUE(converter_fn.Run(stream, decoder_config)); + EXPECT_EQ(static_cast<size_t>(codec_context->extradata_size), + decoder_config->extra_data().size()); + EXPECT_EQ(0, memcmp(codec_context->extradata, + &decoder_config->extra_data()[0], + decoder_config->extra_data().size())); + + // Invalid combination: extra_data = NULL && size != 0. + codec_context->extradata = NULL; + codec_context->extradata_size = 10; + EXPECT_FALSE(converter_fn.Run(stream, decoder_config)); + + // Invalid combination: extra_data = non-NULL && size = 0. + codec_context->extradata = &kExtraData[0]; + codec_context->extradata_size = 0; + EXPECT_FALSE(converter_fn.Run(stream, decoder_config)); + + // Restore orig values for sane cleanup. + codec_context->extradata = orig_extradata; + codec_context->extradata_size = orig_extradata_size; +} + +TEST_F(FFmpegCommonTest, AVStreamToDecoderConfig) { + // Open a file to get a real AVStreams from FFmpeg. + base::MemoryMappedFile file; + file.Initialize(GetTestDataFilePath("bear-320x240.webm")); + InMemoryUrlProtocol protocol(file.data(), file.length(), false); + FFmpegGlue glue(&protocol); + ASSERT_TRUE(glue.OpenContext()); + AVFormatContext* format_context = glue.format_context(); + + // Find the audio and video streams and test valid and invalid combinations + // for extradata and extradata_size. + bool found_audio = false; + bool found_video = false; + for (size_t i = 0; + i < format_context->nb_streams && (!found_audio || !found_video); + ++i) { + AVStream* stream = format_context->streams[i]; + AVCodecContext* codec_context = stream->codec; + AVMediaType codec_type = codec_context->codec_type; + + if (codec_type == AVMEDIA_TYPE_AUDIO) { + if (found_audio) + continue; + found_audio = true; + AudioDecoderConfig audio_config; + TestConfigConvertExtraData(stream, &audio_config, + base::Bind(&AVStreamToAudioDecoderConfig)); + } else if (codec_type == AVMEDIA_TYPE_VIDEO) { + if (found_video) + continue; + found_video = true; + VideoDecoderConfig video_config; + TestConfigConvertExtraData(stream, &video_config, + base::Bind(&AVStreamToVideoDecoderConfig)); + } else { + // Only process audio/video. + continue; + } + } + + ASSERT_TRUE(found_audio); + ASSERT_TRUE(found_video); +} + TEST_F(FFmpegCommonTest, OpusAudioDecoderConfig) { AVCodecContext context = {0}; context.codec_type = AVMEDIA_TYPE_AUDIO; @@ -31,7 +130,8 @@ TEST_F(FFmpegCommonTest, OpusAudioDecoderConfig) { context.sample_rate = 44100; AudioDecoderConfig decoder_config; - AVCodecContextToAudioDecoderConfig(&context, false, &decoder_config); + ASSERT_TRUE(AVCodecContextToAudioDecoderConfig(&context, false, + &decoder_config)); EXPECT_EQ(48000, decoder_config.samples_per_second()); } diff --git a/media/filters/audio_decoder_selector_unittest.cc b/media/filters/audio_decoder_selector_unittest.cc index 1932be3..6e248fe 100644 --- a/media/filters/audio_decoder_selector_unittest.cc +++ b/media/filters/audio_decoder_selector_unittest.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "media/base/gmock_callback_support.h" +#include "media/base/media_util.h" #include "media/base/mock_filters.h" #include "media/base/test_helpers.h" #include "media/filters/decoder_selector.h" @@ -84,16 +85,16 @@ class AudioDecoderSelectorTest : public ::testing::Test { } void UseClearStream() { - AudioDecoderConfig clear_audio_config( - kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, - NULL, 0, false); + AudioDecoderConfig clear_audio_config(kCodecVorbis, kSampleFormatPlanarF32, + CHANNEL_LAYOUT_STEREO, 44100, + EmptyExtraData(), false); demuxer_stream_->set_audio_decoder_config(clear_audio_config); } void UseEncryptedStream() { AudioDecoderConfig encrypted_audio_config( kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, - NULL, 0, true); + EmptyExtraData(), true); demuxer_stream_->set_audio_decoder_config(encrypted_audio_config); } diff --git a/media/filters/audio_decoder_unittest.cc b/media/filters/audio_decoder_unittest.cc index 65db8f3..cfe6a1c 100644 --- a/media/filters/audio_decoder_unittest.cc +++ b/media/filters/audio_decoder_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <deque> +#include <vector> #include "base/bind.h" #include "base/format_macros.h" @@ -16,6 +17,7 @@ #include "media/base/audio_bus.h" #include "media/base/audio_hash.h" #include "media/base/decoder_buffer.h" +#include "media/base/media_util.h" #include "media/base/test_data_util.h" #include "media/base/test_helpers.h" #include "media/base/timestamp_constants.h" @@ -151,8 +153,8 @@ class AudioDecoderTest : public testing::TestWithParam<DecoderTestData> { ASSERT_TRUE(reader_->SeekForTesting(start_timestamp_)); AudioDecoderConfig config; - AVCodecContextToAudioDecoderConfig(reader_->codec_context_for_testing(), - false, &config); + ASSERT_TRUE(AVCodecContextToAudioDecoderConfig( + reader_->codec_context_for_testing(), false, &config)); EXPECT_EQ(GetParam().codec, config.codec()); EXPECT_EQ(GetParam().samples_per_second, config.samples_per_second()); @@ -374,13 +376,15 @@ TEST_P(AudioDecoderTest, NoTimestamp) { TEST_P(OpusAudioDecoderBehavioralTest, InitializeWithNoCodecDelay) { ASSERT_EQ(GetParam().decoder_type, OPUS); + std::vector<uint8_t> extra_data( + kOpusExtraData, + kOpusExtraData + arraysize(kOpusExtraData)); AudioDecoderConfig decoder_config; decoder_config.Initialize(kCodecOpus, kSampleFormatF32, CHANNEL_LAYOUT_STEREO, 48000, - kOpusExtraData, - arraysize(kOpusExtraData), + extra_data, false, base::TimeDelta::FromMilliseconds(80), 0); @@ -389,14 +393,16 @@ TEST_P(OpusAudioDecoderBehavioralTest, InitializeWithNoCodecDelay) { TEST_P(OpusAudioDecoderBehavioralTest, InitializeWithBadCodecDelay) { ASSERT_EQ(GetParam().decoder_type, OPUS); + std::vector<uint8_t> extra_data( + kOpusExtraData, + kOpusExtraData + arraysize(kOpusExtraData)); AudioDecoderConfig decoder_config; decoder_config.Initialize( kCodecOpus, kSampleFormatF32, CHANNEL_LAYOUT_STEREO, 48000, - kOpusExtraData, - arraysize(kOpusExtraData), + extra_data, false, base::TimeDelta::FromMilliseconds(80), // Use a different codec delay than in the extradata. diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 747e512..1b6b894 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -736,8 +736,8 @@ class ChunkDemuxerTest : public ::testing::Test { // bear-320x240.webm : [0-501) [801-2736) // bear-640x360.webm : [527-793) // - // bear-320x240.webm AudioDecoderConfig returns 3863 for its extra_data_size() - // bear-640x360.webm AudioDecoderConfig returns 3935 for its extra_data_size() + // bear-320x240.webm AudioDecoderConfig returns 3863 for its extra_data size. + // bear-640x360.webm AudioDecoderConfig returns 3935 for its extra_data size. // The resulting audio stream returns data from each file for the following // time ranges. // bear-320x240.webm : [0-524) [779-2736) @@ -1247,8 +1247,7 @@ TEST_F(ChunkDemuxerTest, Init) { EXPECT_EQ(32, config.bits_per_channel()); EXPECT_EQ(CHANNEL_LAYOUT_STEREO, config.channel_layout()); EXPECT_EQ(44100, config.samples_per_second()); - EXPECT_TRUE(config.extra_data()); - EXPECT_GT(config.extra_data_size(), 0u); + 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()); @@ -1317,8 +1316,7 @@ TEST_F(ChunkDemuxerTest, InitText) { EXPECT_EQ(32, config.bits_per_channel()); EXPECT_EQ(CHANNEL_LAYOUT_STEREO, config.channel_layout()); EXPECT_EQ(44100, config.samples_per_second()); - EXPECT_TRUE(config.extra_data()); - EXPECT_GT(config.extra_data_size(), 0u); + 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()); @@ -2887,7 +2885,7 @@ TEST_F(ChunkDemuxerTest, ConfigChange_Audio) { const AudioDecoderConfig& audio_config_1 = audio->audio_decoder_config(); ASSERT_TRUE(audio_config_1.IsValidConfig()); EXPECT_EQ(audio_config_1.samples_per_second(), 44100); - EXPECT_EQ(audio_config_1.extra_data_size(), 3863u); + EXPECT_EQ(audio_config_1.extra_data().size(), 3863u); ExpectRead(DemuxerStream::AUDIO, 0); @@ -2901,7 +2899,7 @@ TEST_F(ChunkDemuxerTest, ConfigChange_Audio) { const AudioDecoderConfig& audio_config_2 = audio->audio_decoder_config(); ASSERT_TRUE(audio_config_2.IsValidConfig()); EXPECT_EQ(audio_config_2.samples_per_second(), 44100); - EXPECT_EQ(audio_config_2.extra_data_size(), 3935u); + EXPECT_EQ(audio_config_2.extra_data().size(), 3935u); // The next config change is from a splice frame representing an overlap of // buffers from config 2 by buffers from config 1. diff --git a/media/filters/decrypting_audio_decoder_unittest.cc b/media/filters/decrypting_audio_decoder_unittest.cc index d6ef2b9..4f749ce 100644 --- a/media/filters/decrypting_audio_decoder_unittest.cc +++ b/media/filters/decrypting_audio_decoder_unittest.cc @@ -12,6 +12,7 @@ #include "media/base/decoder_buffer.h" #include "media/base/decrypt_config.h" #include "media/base/gmock_callback_support.h" +#include "media/base/media_util.h" #include "media/base/mock_filters.h" #include "media/base/test_helpers.h" #include "media/base/timestamp_constants.h" @@ -118,8 +119,8 @@ class DecryptingAudioDecoderTest : public testing::Test { .WillOnce(SaveArg<1>(&key_added_cb_)); config_.Initialize(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, kSampleRate, NULL, 0, true, - base::TimeDelta(), 0); + CHANNEL_LAYOUT_STEREO, kSampleRate, EmptyExtraData(), + true, base::TimeDelta(), 0); InitializeAndExpectResult(config_, true); } @@ -287,7 +288,8 @@ TEST_F(DecryptingAudioDecoderTest, Initialize_Normal) { // Ensure that DecryptingAudioDecoder only accepts encrypted audio. TEST_F(DecryptingAudioDecoderTest, Initialize_UnencryptedAudioConfig) { AudioDecoderConfig config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, kSampleRate, NULL, 0, false); + CHANNEL_LAYOUT_STEREO, kSampleRate, + EmptyExtraData(), false); InitializeAndExpectResult(config, false); } @@ -295,7 +297,7 @@ TEST_F(DecryptingAudioDecoderTest, Initialize_UnencryptedAudioConfig) { // Ensure decoder handles invalid audio configs without crashing. TEST_F(DecryptingAudioDecoderTest, Initialize_InvalidAudioConfig) { AudioDecoderConfig config(kUnknownAudioCodec, kUnknownSampleFormat, - CHANNEL_LAYOUT_STEREO, 0, NULL, 0, true); + CHANNEL_LAYOUT_STEREO, 0, EmptyExtraData(), true); InitializeAndExpectResult(config, false); } @@ -307,14 +309,16 @@ TEST_F(DecryptingAudioDecoderTest, Initialize_UnsupportedAudioConfig) { ExpectDecryptorNotification(decryptor_.get(), true); AudioDecoderConfig config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, kSampleRate, NULL, 0, true); + CHANNEL_LAYOUT_STEREO, kSampleRate, + EmptyExtraData(), true); InitializeAndExpectResult(config, false); } TEST_F(DecryptingAudioDecoderTest, Initialize_NullDecryptor) { ExpectDecryptorNotification(NULL, false); AudioDecoderConfig config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, kSampleRate, NULL, 0, true); + CHANNEL_LAYOUT_STEREO, kSampleRate, + EmptyExtraData(), true); InitializeAndExpectResult(config, false); } @@ -382,7 +386,8 @@ TEST_F(DecryptingAudioDecoderTest, Reinitialize_ConfigChange) { // The new config is different from the initial config in bits-per-channel, // channel layout and samples_per_second. AudioDecoderConfig new_config(kCodecVorbis, kSampleFormatPlanarS16, - CHANNEL_LAYOUT_5_1, 88200, NULL, 0, true); + CHANNEL_LAYOUT_5_1, 88200, EmptyExtraData(), + true); EXPECT_NE(new_config.bits_per_channel(), config_.bits_per_channel()); EXPECT_NE(new_config.channel_layout(), config_.channel_layout()); EXPECT_NE(new_config.samples_per_second(), config_.samples_per_second()); diff --git a/media/filters/decrypting_demuxer_stream.cc b/media/filters/decrypting_demuxer_stream.cc index ff98122..3e67a29 100644 --- a/media/filters/decrypting_demuxer_stream.cc +++ b/media/filters/decrypting_demuxer_stream.cc @@ -385,7 +385,6 @@ void DecryptingDemuxerStream::InitializeDecoderConfig() { input_audio_config.channel_layout(), input_audio_config.samples_per_second(), input_audio_config.extra_data(), - input_audio_config.extra_data_size(), false, // Output audio is not encrypted. input_audio_config.seek_preroll(), input_audio_config.codec_delay()); @@ -400,7 +399,6 @@ void DecryptingDemuxerStream::InitializeDecoderConfig() { input_video_config.format(), input_video_config.color_space(), input_video_config.coded_size(), input_video_config.visible_rect(), input_video_config.natural_size(), input_video_config.extra_data(), - input_video_config.extra_data_size(), false); // Output video is not encrypted. break; } diff --git a/media/filters/decrypting_demuxer_stream_unittest.cc b/media/filters/decrypting_demuxer_stream_unittest.cc index e5a8f4b..c679b56 100644 --- a/media/filters/decrypting_demuxer_stream_unittest.cc +++ b/media/filters/decrypting_demuxer_stream_unittest.cc @@ -11,6 +11,7 @@ #include "media/base/decoder_buffer.h" #include "media/base/decrypt_config.h" #include "media/base/gmock_callback_support.h" +#include "media/base/media_util.h" #include "media/base/mock_filters.h" #include "media/base/test_helpers.h" #include "media/filters/decrypting_demuxer_stream.h" @@ -134,9 +135,9 @@ class DecryptingDemuxerStreamTest : public testing::Test { EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kAudio, _)) .WillOnce(SaveArg<1>(&key_added_cb_)); - AudioDecoderConfig input_config( - kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, - NULL, 0, true); + AudioDecoderConfig input_config(kCodecVorbis, kSampleFormatPlanarF32, + CHANNEL_LAYOUT_STEREO, 44100, + EmptyExtraData(), true); InitializeAudioAndExpectStatus(input_config, PIPELINE_OK); const AudioDecoderConfig& output_config = @@ -309,18 +310,14 @@ TEST_F(DecryptingDemuxerStreamTest, Initialize_NormalVideo) { EXPECT_EQ(input_config.coded_size(), output_config.coded_size()); EXPECT_EQ(input_config.visible_rect(), output_config.visible_rect()); EXPECT_EQ(input_config.natural_size(), output_config.natural_size()); - ASSERT_EQ(input_config.extra_data_size(), output_config.extra_data_size()); - if (input_config.extra_data_size() > 0) { - EXPECT_FALSE(output_config.extra_data()); - EXPECT_EQ(0, memcmp(output_config.extra_data(), input_config.extra_data(), - input_config.extra_data_size())); - } + ASSERT_EQ(input_config.extra_data(), output_config.extra_data()); } TEST_F(DecryptingDemuxerStreamTest, Initialize_NullDecryptor) { ExpectDecryptorNotification(NULL, false); AudioDecoderConfig input_config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, 44100, NULL, 0, true); + CHANNEL_LAYOUT_STEREO, 44100, + EmptyExtraData(), true); InitializeAudioAndExpectStatus(input_config, DECODER_ERROR_NOT_SUPPORTED); } @@ -394,9 +391,9 @@ TEST_F(DecryptingDemuxerStreamTest, Reset_DuringDecryptorRequested) { // One for decryptor request, one for canceling request during Reset(). EXPECT_CALL(*this, RequestDecryptorNotification(_)) .Times(2); - AudioDecoderConfig input_config( - kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, - NULL, 0, true); + AudioDecoderConfig input_config(kCodecVorbis, kSampleFormatPlanarF32, + CHANNEL_LAYOUT_STEREO, 44100, + EmptyExtraData(), true); InitializeAudioAndExpectStatus(input_config, PIPELINE_ERROR_ABORT); Reset(); } @@ -482,9 +479,9 @@ TEST_F(DecryptingDemuxerStreamTest, Reset_DuringAbortedDemuxerRead) { TEST_F(DecryptingDemuxerStreamTest, DemuxerRead_ConfigChanged) { Initialize(); - AudioDecoderConfig new_config( - kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 88200, NULL, - 0, true); + AudioDecoderConfig new_config(kCodecVorbis, kSampleFormatPlanarF32, + CHANNEL_LAYOUT_STEREO, 88200, EmptyExtraData(), + true); input_audio_stream_->set_audio_decoder_config(new_config); EXPECT_CALL(*input_audio_stream_, Read(_)) @@ -515,9 +512,9 @@ TEST_F(DecryptingDemuxerStreamTest, Destroy_DuringDecryptorRequested) { // One for decryptor request, one for canceling request during Reset(). EXPECT_CALL(*this, RequestDecryptorNotification(_)) .Times(2); - AudioDecoderConfig input_config( - kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, - NULL, 0, true); + AudioDecoderConfig input_config(kCodecVorbis, kSampleFormatPlanarF32, + CHANNEL_LAYOUT_STEREO, 44100, + EmptyExtraData(), true); InitializeAudioAndExpectStatus(input_config, PIPELINE_ERROR_ABORT); } diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc index 155e980..c06dd29 100644 --- a/media/filters/ffmpeg_demuxer.cc +++ b/media/filters/ffmpeg_demuxer.cc @@ -172,14 +172,58 @@ static int32_t GetCodecHash(const AVCodecContext* context) { return HashCodecName("none"); } +scoped_ptr<FFmpegDemuxerStream> FFmpegDemuxerStream::Create( + FFmpegDemuxer* demuxer, + AVStream* stream) { + if (!demuxer || !stream) + return nullptr; + + scoped_ptr<FFmpegDemuxerStream> demuxer_stream; + scoped_ptr<AudioDecoderConfig> audio_config; + scoped_ptr<VideoDecoderConfig> video_config; + + if (stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + audio_config.reset(new AudioDecoderConfig()); + + // IsValidConfig() checks that the codec is supported and that the channel + // layout and sample format are valid. + // + // TODO(chcunningham): Change AVStreamToAudioDecoderConfig to check + // IsValidConfig internally and return a null scoped_ptr if not valid. + if (!AVStreamToAudioDecoderConfig(stream, audio_config.get()) || + !audio_config->IsValidConfig()) + return nullptr; + + } else if (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + video_config.reset(new VideoDecoderConfig()); + + // IsValidConfig() checks that the codec is supported and that the channel + // layout and sample format are valid. + // + // TODO(chcunningham): Change AVStreamToVideoDecoderConfig to check + // IsValidConfig internally and return a null scoped_ptr if not valid. + if (!AVStreamToVideoDecoderConfig(stream, video_config.get()) || + !video_config->IsValidConfig()) + return nullptr; + } + + return make_scoped_ptr(new FFmpegDemuxerStream( + demuxer, stream, audio_config.Pass(), video_config.Pass())); +} + // // FFmpegDemuxerStream // -FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer, - AVStream* stream) +FFmpegDemuxerStream::FFmpegDemuxerStream( + FFmpegDemuxer* demuxer, + AVStream* stream, + scoped_ptr<AudioDecoderConfig> audio_config, + scoped_ptr<VideoDecoderConfig> video_config) : demuxer_(demuxer), task_runner_(base::ThreadTaskRunnerHandle::Get()), stream_(stream), + audio_config_(audio_config.release()), + video_config_(video_config.release()), type_(UNKNOWN), liveness_(LIVENESS_UNKNOWN), end_of_stream_(false), @@ -196,14 +240,14 @@ FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer, // Determine our media format. switch (stream->codec->codec_type) { case AVMEDIA_TYPE_AUDIO: + DCHECK(audio_config_.get() && !video_config_.get()); type_ = AUDIO; - AVStreamToAudioDecoderConfig(stream, &audio_config_); - is_encrypted = audio_config_.is_encrypted(); + is_encrypted = audio_config_->is_encrypted(); break; case AVMEDIA_TYPE_VIDEO: + DCHECK(video_config_.get() && !audio_config_.get()); type_ = VIDEO; - AVStreamToVideoDecoderConfig(stream, &video_config_); - is_encrypted = video_config_.is_encrypted(); + is_encrypted = video_config_->is_encrypted(); rotation_entry = av_dict_get(stream->metadata, "rotate", NULL, 0); if (rotation_entry && rotation_entry->value && rotation_entry->value[0]) @@ -228,6 +272,7 @@ FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer, break; case AVMEDIA_TYPE_SUBTITLE: + DCHECK(!video_config_.get() && !audio_config_.get()); type_ = TEXT; break; default: @@ -315,8 +360,8 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { scoped_ptr<DecryptConfig> decrypt_config; int data_offset = 0; - if ((type() == DemuxerStream::AUDIO && audio_config_.is_encrypted()) || - (type() == DemuxerStream::VIDEO && video_config_.is_encrypted())) { + if ((type() == DemuxerStream::AUDIO && audio_config_->is_encrypted()) || + (type() == DemuxerStream::VIDEO && video_config_->is_encrypted())) { if (!WebMCreateDecryptConfig( packet->data, packet->size, reinterpret_cast<const uint8*>(encryption_key_id_.data()), @@ -580,14 +625,16 @@ bool FFmpegDemuxerStream::SupportsConfigChanges() { return false; } AudioDecoderConfig FFmpegDemuxerStream::audio_decoder_config() { DCHECK(task_runner_->BelongsToCurrentThread()); - CHECK_EQ(type_, AUDIO); - return audio_config_; + DCHECK_EQ(type_, AUDIO); + DCHECK(audio_config_.get()); + return *audio_config_; } VideoDecoderConfig FFmpegDemuxerStream::video_decoder_config() { DCHECK(task_runner_->BelongsToCurrentThread()); - CHECK_EQ(type_, VIDEO); - return video_config_; + DCHECK_EQ(type_, VIDEO); + DCHECK(video_config_.get()); + return *video_config_; } VideoRotation FFmpegDemuxerStream::video_rotation() { @@ -987,7 +1034,6 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, AVStream* audio_stream = NULL; AudioDecoderConfig audio_config; - AVStream* video_stream = NULL; VideoDecoderConfig video_config; @@ -1007,13 +1053,6 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, // Log the codec detected, whether it is supported or not. UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodecHash", GetCodecHash(codec_context)); - - // Ensure the codec is supported. IsValidConfig() also checks that the - // channel layout and sample format are valid. - AVStreamToAudioDecoderConfig(stream, &audio_config); - if (!audio_config.IsValidConfig()) - continue; - audio_stream = stream; } else if (codec_type == AVMEDIA_TYPE_VIDEO) { if (video_stream) continue; @@ -1042,14 +1081,6 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, // Log the codec detected, whether it is supported or not. UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodecHash", GetCodecHash(codec_context)); - - // Ensure the codec is supported. IsValidConfig() also checks that the - // frame size and visible size are valid. - AVStreamToVideoDecoderConfig(stream, &video_config); - - if (!video_config.IsValidConfig()) - continue; - video_stream = stream; } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { continue; @@ -1058,15 +1089,30 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, continue; } - streams_[i] = new FFmpegDemuxerStream(this, stream); + // Attempt to create a FFmpegDemuxerStream from the AVStream. This will + // return nullptr if the AVStream is invalid. Validity checks will verify + // things like: codec, channel layout, sample/pixel format, etc... + scoped_ptr<FFmpegDemuxerStream> demuxer_stream = + FFmpegDemuxerStream::Create(this, stream); + if (demuxer_stream.get()) { + streams_[i] = demuxer_stream.release(); + } else { + // This AVStream does not successfully convert. + continue; + } - // Record audio or video src= playback UMA stats for the stream's decoder - // config. + // Note when we find our audio/video stream (we only want one of each) and + // record src= playback UMA stats for the stream's decoder config. if (codec_type == AVMEDIA_TYPE_AUDIO) { - RecordAudioCodecStats(streams_[i]->audio_decoder_config()); + CHECK(!audio_stream); + audio_stream = stream; + audio_config = streams_[i]->audio_decoder_config(); + RecordAudioCodecStats(audio_config); } else if (codec_type == AVMEDIA_TYPE_VIDEO) { - RecordVideoCodecStats(streams_[i]->video_decoder_config(), - stream->codec->color_range); + CHECK(!video_stream); + video_stream = stream; + video_config = streams_[i]->video_decoder_config(); + RecordVideoCodecStats(video_config, stream->codec->color_range); } max_duration = std::max(max_duration, streams_[i]->duration()); diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h index 9c20e4a..96c21d5 100644 --- a/media/filters/ffmpeg_demuxer.h +++ b/media/filters/ffmpeg_demuxer.h @@ -27,6 +27,7 @@ #include <vector> #include "base/callback.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/threading/thread.h" #include "media/base/audio_decoder_config.h" @@ -55,9 +56,14 @@ typedef scoped_ptr<AVPacket, ScopedPtrAVFreePacket> ScopedAVPacket; class FFmpegDemuxerStream : public DemuxerStream { public: - // Keeps a copy of |demuxer| and initializes itself using information inside - // |stream|. Both parameters must outlive |this|. - FFmpegDemuxerStream(FFmpegDemuxer* demuxer, AVStream* stream); + // Attempts to create FFmpegDemuxerStream form the given AVStream. Will return + // null if the AVStream cannot be translated into a valid decoder config. + // + // FFmpegDemuxerStream keeps a copy of |demuxer| and initializes itself using + // information inside |stream|. Both parameters must outlive |this|. + static scoped_ptr<FFmpegDemuxerStream> Create(FFmpegDemuxer* demuxer, + AVStream* stream); + ~FFmpegDemuxerStream() override; // Enqueues the given AVPacket. It is invalid to queue a |packet| after @@ -123,6 +129,15 @@ class FFmpegDemuxerStream : public DemuxerStream { private: friend class FFmpegDemuxerTest; + // Use FFmpegDemuxerStream::Create to construct. + // Audio/Video streams must include their respective DecoderConfig. At most + // one DecoderConfig should be provided (leaving the other nullptr). Both + // configs should be null for text streams. + FFmpegDemuxerStream(FFmpegDemuxer* demuxer, + AVStream* stream, + scoped_ptr<AudioDecoderConfig> audio_config, + scoped_ptr<VideoDecoderConfig> video_config); + // Runs |read_cb_| if present with the front of |buffer_queue_|, calling // NotifyCapacityAvailable() if capacity is still available. void SatisfyPendingRead(); @@ -140,8 +155,8 @@ class FFmpegDemuxerStream : public DemuxerStream { FFmpegDemuxer* demuxer_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; AVStream* stream_; - AudioDecoderConfig audio_config_; - VideoDecoderConfig video_config_; + scoped_ptr<AudioDecoderConfig> audio_config_; + scoped_ptr<VideoDecoderConfig> video_config_; Type type_; Liveness liveness_; base::TimeDelta duration_; diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index e75a67f..2e988d2 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc @@ -296,8 +296,7 @@ TEST_F(FFmpegDemuxerTest, Initialize_Successful) { EXPECT_EQ(240, video_config.visible_rect().height()); EXPECT_EQ(320, video_config.natural_size().width()); EXPECT_EQ(240, video_config.natural_size().height()); - EXPECT_FALSE(video_config.extra_data()); - EXPECT_EQ(0u, video_config.extra_data_size()); + EXPECT_TRUE(video_config.extra_data().empty()); // Audio stream should be present. stream = demuxer_->GetStream(DemuxerStream::AUDIO); @@ -310,8 +309,7 @@ TEST_F(FFmpegDemuxerTest, Initialize_Successful) { EXPECT_EQ(CHANNEL_LAYOUT_STEREO, audio_config.channel_layout()); EXPECT_EQ(44100, audio_config.samples_per_second()); EXPECT_EQ(kSampleFormatPlanarF32, audio_config.sample_format()); - EXPECT_TRUE(audio_config.extra_data()); - EXPECT_GT(audio_config.extra_data_size(), 0u); + EXPECT_FALSE(audio_config.extra_data().empty()); // Unknown stream should never be present. EXPECT_FALSE(demuxer_->GetStream(DemuxerStream::UNKNOWN)); diff --git a/media/filters/ffmpeg_glue.cc b/media/filters/ffmpeg_glue.cc index 31c6828..bfeced0 100644 --- a/media/filters/ffmpeg_glue.cc +++ b/media/filters/ffmpeg_glue.cc @@ -153,7 +153,7 @@ FFmpegGlue::FFmpegGlue(FFmpegURLProtocol* protocol) } bool FFmpegGlue::OpenContext() { - DCHECK(!open_called_) << "OpenContext() should't be called twice."; + DCHECK(!open_called_) << "OpenContext() shouldn't be called twice."; // If avformat_open_input() is called we have to take a slightly different // destruction path to avoid double frees. diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc index fb35538..44f6852 100644 --- a/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/media/filters/ffmpeg_video_decoder_unittest.cc @@ -14,6 +14,7 @@ #include "media/base/decoder_buffer.h" #include "media/base/gmock_callback_support.h" #include "media/base/limits.h" +#include "media/base/media_util.h" #include "media/base/mock_filters.h" #include "media/base/test_data_util.h" #include "media/base/test_helpers.h" @@ -215,16 +216,12 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_Normal) { Initialize(); } -TEST_F(FFmpegVideoDecoderTest, Initialize_EncryptedConfig) { - InitializeWithConfigWithResult(TestVideoConfig::NormalEncrypted(), false); -} - TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) { // Specify Theora w/o extra data so that avcodec_open2() fails. VideoDecoderConfig config(kCodecTheora, VIDEO_CODEC_PROFILE_UNKNOWN, - kVideoFormat, COLOR_SPACE_UNSPECIFIED, - kCodedSize, kVisibleRect, kNaturalSize, - NULL, 0, false); + kVideoFormat, COLOR_SPACE_UNSPECIFIED, kCodedSize, + kVisibleRect, kNaturalSize, EmptyExtraData(), + false); InitializeWithConfigWithResult(config, false); } diff --git a/media/filters/frame_processor_unittest.cc b/media/filters/frame_processor_unittest.cc index ed4e715..4440de0 100644 --- a/media/filters/frame_processor_unittest.cc +++ b/media/filters/frame_processor_unittest.cc @@ -4,6 +4,7 @@ #include <map> #include <string> +#include <vector> #include "base/bind.h" #include "base/message_loop/message_loop.h" @@ -12,6 +13,7 @@ #include "base/strings/string_util.h" #include "base/time/time.h" #include "media/base/media_log.h" +#include "media/base/media_util.h" #include "media/base/mock_filters.h" #include "media/base/test_helpers.h" #include "media/base/timestamp_constants.h" @@ -282,13 +284,9 @@ class FrameProcessorTest : public testing::TestWithParam<bool> { case DemuxerStream::AUDIO: { ASSERT_FALSE(audio_); audio_.reset(new ChunkDemuxerStream(DemuxerStream::AUDIO, true)); - AudioDecoderConfig decoder_config(kCodecVorbis, - kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, - 1000, - NULL, - 0, - false); + AudioDecoderConfig decoder_config(kCodecVorbis, kSampleFormatPlanarF32, + CHANNEL_LAYOUT_STEREO, 1000, + EmptyExtraData(), false); frame_processor_->OnPossibleAudioConfigUpdate(decoder_config); ASSERT_TRUE(audio_->UpdateAudioConfig(decoder_config, new MediaLog())); break; diff --git a/media/filters/opus_audio_decoder.cc b/media/filters/opus_audio_decoder.cc index 57d173e..4095c4d 100644 --- a/media/filters/opus_audio_decoder.cc +++ b/media/filters/opus_audio_decoder.cc @@ -363,7 +363,9 @@ bool OpusAudioDecoder::ConfigureDecoder() { // Parse the Opus Extra Data. OpusExtraData opus_extra_data; - if (!ParseOpusExtraData(config_.extra_data(), config_.extra_data_size(), + if (!ParseOpusExtraData(config_.extra_data().empty() ? nullptr : + &config_.extra_data()[0], + config_.extra_data().size(), config_, &opus_extra_data)) return false; diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc index bf15f32..5d8dfd5 100644 --- a/media/filters/source_buffer_stream_unittest.cc +++ b/media/filters/source_buffer_stream_unittest.cc @@ -6,6 +6,7 @@ #include <stdint.h> #include <string> +#include <vector> #include "base/bind.h" #include "base/bind_helpers.h" @@ -15,6 +16,7 @@ #include "base/strings/string_util.h" #include "media/base/data_buffer.h" #include "media/base/media_log.h" +#include "media/base/media_util.h" #include "media/base/mock_media_log.h" #include "media/base/test_helpers.h" #include "media/base/text_track_config.h" @@ -101,8 +103,7 @@ class SourceBufferStreamTest : public testing::Test { kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 1000, - NULL, - 0, + EmptyExtraData(), false, base::TimeDelta(), 0); @@ -3603,7 +3604,7 @@ TEST_F(SourceBufferStreamTest, SameTimestamp_Video_Overlap_3) { // Test all the valid same timestamp cases for audio. TEST_F(SourceBufferStreamTest, SameTimestamp_Audio) { AudioDecoderConfig config(kCodecMP3, kSampleFormatF32, CHANNEL_LAYOUT_STEREO, - 44100, NULL, 0, false); + 44100, EmptyExtraData(), false); stream_.reset(new SourceBufferStream(config, media_log_, true)); Seek(0); NewSegmentAppend("0K 0K 30K 30 60 60"); @@ -3614,7 +3615,7 @@ TEST_F(SourceBufferStreamTest, SameTimestamp_Audio_Invalid_1) { EXPECT_MEDIA_LOG(ContainsSameTimestampAt30MillisecondsLog()); AudioDecoderConfig config(kCodecMP3, kSampleFormatF32, CHANNEL_LAYOUT_STEREO, - 44100, NULL, 0, false); + 44100, EmptyExtraData(), false); stream_.reset(new SourceBufferStream(config, media_log_, true)); Seek(0); NewSegmentAppend_ExpectFailure("0K 30 30K 60"); @@ -4173,12 +4174,8 @@ TEST_F(SourceBufferStreamTest, Audio_SpliceFrame_ConfigChange) { SetAudioStream(); - AudioDecoderConfig new_config(kCodecVorbis, - kSampleFormatPlanarF32, - CHANNEL_LAYOUT_MONO, - 1000, - NULL, - 0, + AudioDecoderConfig new_config(kCodecVorbis, kSampleFormatPlanarF32, + CHANNEL_LAYOUT_MONO, 1000, EmptyExtraData(), false); ASSERT_NE(new_config.channel_layout(), audio_config_.channel_layout()); @@ -4220,7 +4217,7 @@ TEST_F(SourceBufferStreamTest, Audio_SpliceFrame_NoMillisecondSplices) { video_config_ = TestVideoConfig::Invalid(); audio_config_.Initialize(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, 4000, NULL, 0, false, + CHANNEL_LAYOUT_STEREO, 4000, EmptyExtraData(), false, base::TimeDelta(), 0); stream_.reset(new SourceBufferStream(audio_config_, media_log_, true)); // Equivalent to 0.5ms per frame. diff --git a/media/formats/mp2t/es_adapter_video_unittest.cc b/media/formats/mp2t/es_adapter_video_unittest.cc index 396b655..24feb85 100644 --- a/media/formats/mp2t/es_adapter_video_unittest.cc +++ b/media/formats/mp2t/es_adapter_video_unittest.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/strings/string_util.h" #include "base/time/time.h" +#include "media/base/media_util.h" #include "media/base/stream_parser_buffer.h" #include "media/base/timestamp_constants.h" #include "media/base/video_decoder_config.h" @@ -27,7 +28,7 @@ VideoDecoderConfig CreateFakeVideoConfig() { gfx::Size natural_size(320, 240); return VideoDecoderConfig(kCodecH264, H264PROFILE_MAIN, PIXEL_FORMAT_I420, COLOR_SPACE_UNSPECIFIED, coded_size, visible_rect, - natural_size, NULL, 0, false); + natural_size, EmptyExtraData(), false); } StreamParserBuffer::BufferQueue diff --git a/media/formats/mp2t/es_parser_adts.cc b/media/formats/mp2t/es_parser_adts.cc index 9c57de1..adf1dbe 100644 --- a/media/formats/mp2t/es_parser_adts.cc +++ b/media/formats/mp2t/es_parser_adts.cc @@ -4,6 +4,8 @@ #include "media/formats/mp2t/es_parser_adts.h" +#include <vector> + #include "base/basictypes.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" @@ -219,10 +221,9 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) { (frequency_index << 7) + // channel_configuration is [0..7], per early out above. (channel_configuration << 3)); - uint8 extra_data[2] = { - static_cast<uint8>(extra_data_int >> 8), - static_cast<uint8>(extra_data_int & 0xff) - }; + std::vector<uint8_t> extra_data; + extra_data.push_back(static_cast<uint8>(extra_data_int >> 8)); + extra_data.push_back(static_cast<uint8>(extra_data_int & 0xff)); AudioDecoderConfig audio_decoder_config( kCodecAAC, @@ -230,7 +231,6 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) { kADTSChannelLayoutTable[channel_configuration], extended_samples_per_second, extra_data, - arraysize(extra_data), false); if (!audio_decoder_config.Matches(last_audio_decoder_config_)) { diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc index cbdec71..8c59112 100644 --- a/media/formats/mp2t/es_parser_h264.cc +++ b/media/formats/mp2t/es_parser_h264.cc @@ -280,8 +280,8 @@ bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps) { VideoDecoderConfig video_decoder_config( kCodecH264, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_YV12, - COLOR_SPACE_HD_REC709, coded_size, visible_rect, natural_size, NULL, 0, - false); + COLOR_SPACE_HD_REC709, coded_size, visible_rect, natural_size, + std::vector<uint8_t>(), false); if (!video_decoder_config.Matches(last_video_decoder_config_)) { DVLOG(1) << "Profile IDC: " << sps->profile_idc; diff --git a/media/formats/mp2t/es_parser_mpeg1audio.cc b/media/formats/mp2t/es_parser_mpeg1audio.cc index aee4cba..81abc2b 100644 --- a/media/formats/mp2t/es_parser_mpeg1audio.cc +++ b/media/formats/mp2t/es_parser_mpeg1audio.cc @@ -4,6 +4,8 @@ #include "media/formats/mp2t/es_parser_mpeg1audio.h" +#include <vector> + #include "base/basictypes.h" #include "base/bind.h" #include "base/logging.h" @@ -173,7 +175,7 @@ bool EsParserMpeg1Audio::UpdateAudioConfiguration( kSampleFormatS16, header.channel_layout, header.sample_rate, - NULL, 0, + std::vector<uint8_t>(), false); if (!audio_decoder_config.Matches(last_audio_decoder_config_)) { diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc index 18698b3..78b7dcd 100644 --- a/media/formats/mp4/mp4_stream_parser.cc +++ b/media/formats/mp4/mp4_stream_parser.cc @@ -4,6 +4,8 @@ #include "media/formats/mp4/mp4_stream_parser.h" +#include <vector> + #include "base/callback_helpers.h" #include "base/logging.h" #include "base/time/time.h" @@ -266,10 +268,9 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { is_audio_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted; DVLOG(1) << "is_audio_track_encrypted_: " << is_audio_track_encrypted_; - 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_, base::TimeDelta(), 0); + audio_config.Initialize(codec, sample_format, channel_layout, + sample_per_second, extra_data, + is_audio_track_encrypted_, base::TimeDelta(), 0); has_audio_ = true; audio_track_id_ = track->header.track_id; } @@ -305,12 +306,12 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { is_video_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted; DVLOG(1) << "is_video_track_encrypted_: " << is_video_track_encrypted_; - video_config.Initialize(entry.video_codec, entry.video_codec_profile, - PIXEL_FORMAT_YV12, COLOR_SPACE_HD_REC709, - coded_size, visible_rect, natural_size, - // No decoder-specific buffer needed for AVC; - // SPS/PPS are embedded in the video stream - NULL, 0, is_video_track_encrypted_); + video_config.Initialize( + entry.video_codec, entry.video_codec_profile, PIXEL_FORMAT_YV12, + COLOR_SPACE_HD_REC709, coded_size, visible_rect, natural_size, + // No decoder-specific buffer needed for AVC; + // SPS/PPS are embedded in the video stream + std::vector<uint8_t>(), is_video_track_encrypted_); has_video_ = true; video_track_id_ = track->header.track_id; } diff --git a/media/formats/mpeg/mpeg_audio_stream_parser_base.cc b/media/formats/mpeg/mpeg_audio_stream_parser_base.cc index d9bf2b5..23032fa 100644 --- a/media/formats/mpeg/mpeg_audio_stream_parser_base.cc +++ b/media/formats/mpeg/mpeg_audio_stream_parser_base.cc @@ -207,8 +207,7 @@ int MPEGAudioStreamParserBase::ParseFrame(const uint8* data, kSampleFormatF32, channel_layout, sample_rate, - NULL, - 0, + std::vector<uint8_t>(), false, base::TimeDelta(), codec_delay_); diff --git a/media/formats/webm/webm_audio_client.cc b/media/formats/webm/webm_audio_client.cc index bde7233..995746a4 100644 --- a/media/formats/webm/webm_audio_client.cc +++ b/media/formats/webm/webm_audio_client.cc @@ -66,13 +66,6 @@ bool WebMAudioClient::InitializeConfig( sample_format = kSampleFormatF32; } - const uint8* extra_data = NULL; - size_t extra_data_size = 0; - if (codec_private.size() > 0) { - extra_data = &codec_private[0]; - extra_data_size = codec_private.size(); - } - // Convert |codec_delay| from nanoseconds into frames. int codec_delay_in_frames = 0; if (codec_delay != -1) { @@ -87,8 +80,7 @@ bool WebMAudioClient::InitializeConfig( sample_format, channel_layout, samples_per_second, - extra_data, - extra_data_size, + codec_private, is_encrypted, base::TimeDelta::FromMicroseconds( (seek_preroll != -1 ? seek_preroll : 0) / 1000), diff --git a/media/formats/webm/webm_video_client.cc b/media/formats/webm/webm_video_client.cc index 2d3f43b..29b7dca 100644 --- a/media/formats/webm/webm_video_client.cc +++ b/media/formats/webm/webm_video_client.cc @@ -88,16 +88,10 @@ bool WebMVideoClient::InitializeConfig( return false; } gfx::Size natural_size = gfx::Size(display_width_, display_height_); - const uint8* extra_data = NULL; - size_t extra_data_size = 0; - if (codec_private.size() > 0) { - extra_data = &codec_private[0]; - extra_data_size = codec_private.size(); - } config->Initialize(video_codec, profile, format, COLOR_SPACE_HD_REC709, - coded_size, visible_rect, natural_size, extra_data, - extra_data_size, is_encrypted); + coded_size, visible_rect, natural_size, codec_private, + is_encrypted); return config->IsValidConfig(); } diff --git a/media/media.gyp b/media/media.gyp index 4f1ac5c..4430344 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -328,6 +328,8 @@ 'base/media_resources.h', 'base/media_switches.cc', 'base/media_switches.h', + 'base/media_util.cc', + 'base/media_util.h', 'base/mime_util.cc', 'base/mime_util.h', 'base/moving_average.cc', diff --git a/media/mojo/services/media_type_converters.cc b/media/mojo/services/media_type_converters.cc index 6dd84fb..bcfab49 100644 --- a/media/mojo/services/media_type_converters.cc +++ b/media/mojo/services/media_type_converters.cc @@ -419,10 +419,9 @@ media::interfaces::AudioDecoderConfigPtr TypeConverter< config->channel_layout = static_cast<media::interfaces::ChannelLayout>(input.channel_layout()); config->samples_per_second = input.samples_per_second(); - if (input.extra_data()) { - std::vector<uint8_t> data(input.extra_data(), - input.extra_data() + input.extra_data_size()); - config->extra_data.Swap(&data); + if (!input.extra_data().empty()) { + std::vector<uint8_t> extra_data = input.extra_data(); + config->extra_data.Swap(&extra_data); } config->seek_preroll_usec = input.seek_preroll().InMicroseconds(); config->codec_delay = input.codec_delay(); @@ -440,10 +439,7 @@ TypeConverter<media::AudioDecoderConfig, static_cast<media::AudioCodec>(input->codec), static_cast<media::SampleFormat>(input->sample_format), static_cast<media::ChannelLayout>(input->channel_layout), - input->samples_per_second, - input->extra_data.size() ? &input->extra_data.front() : NULL, - input->extra_data.size(), - input->is_encrypted, + input->samples_per_second, input->extra_data, input->is_encrypted, base::TimeDelta::FromMicroseconds(input->seek_preroll_usec), input->codec_delay); return config; @@ -465,11 +461,7 @@ media::interfaces::VideoDecoderConfigPtr TypeConverter< config->coded_size = Size::From(input.coded_size()); config->visible_rect = Rect::From(input.visible_rect()); config->natural_size = Size::From(input.natural_size()); - if (input.extra_data()) { - std::vector<uint8_t> data(input.extra_data(), - input.extra_data() + input.extra_data_size()); - config->extra_data.Swap(&data); - } + config->extra_data = mojo::Array<uint8>::From(input.extra_data()); config->is_encrypted = input.is_encrypted(); return config.Pass(); } @@ -486,9 +478,8 @@ TypeConverter<media::VideoDecoderConfig, static_cast<media::VideoPixelFormat>(input->format), static_cast<media::ColorSpace>(input->color_space), input->coded_size.To<gfx::Size>(), input->visible_rect.To<gfx::Rect>(), - input->natural_size.To<gfx::Size>(), - input->extra_data.size() ? &input->extra_data.front() : NULL, - input->extra_data.size(), input->is_encrypted); + input->natural_size.To<gfx::Size>(), input->extra_data, + input->is_encrypted); return config; } diff --git a/media/mojo/services/media_type_converters_unittest.cc b/media/mojo/services/media_type_converters_unittest.cc index 54cd665..38559f3 100644 --- a/media/mojo/services/media_type_converters_unittest.cc +++ b/media/mojo/services/media_type_converters_unittest.cc @@ -7,6 +7,7 @@ #include "media/base/audio_decoder_config.h" #include "media/base/cdm_config.h" #include "media/base/decoder_buffer.h" +#include "media/base/media_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { @@ -121,22 +122,23 @@ TEST(MediaTypeConvertersTest, ConvertDecoderBuffer_EncryptedBuffer) { // TODO(tim): Check other properties. TEST(MediaTypeConvertersTest, ConvertAudioDecoderConfig_Normal) { - const uint8 kExtraData[] = "config extra data"; - const int kExtraDataSize = arraysize(kExtraData); + const uint8_t kExtraData[] = "config extra data"; + const std::vector<uint8_t> kExtraDataVector( + &kExtraData[0], &kExtraData[0] + arraysize(kExtraData)); + AudioDecoderConfig config; config.Initialize(kCodecAAC, kSampleFormatU8, CHANNEL_LAYOUT_SURROUND, 48000, - reinterpret_cast<const uint8*>(&kExtraData), kExtraDataSize, - false, base::TimeDelta(), 0); + kExtraDataVector, false, base::TimeDelta(), 0); interfaces::AudioDecoderConfigPtr ptr( interfaces::AudioDecoderConfig::From(config)); AudioDecoderConfig result(ptr.To<AudioDecoderConfig>()); EXPECT_TRUE(result.Matches(config)); } -TEST(MediaTypeConvertersTest, ConvertAudioDecoderConfig_NullExtraData) { +TEST(MediaTypeConvertersTest, ConvertAudioDecoderConfig_EmptyExtraData) { AudioDecoderConfig config; config.Initialize(kCodecAAC, kSampleFormatU8, CHANNEL_LAYOUT_SURROUND, 48000, - NULL, 0, false, base::TimeDelta(), 0); + EmptyExtraData(), false, base::TimeDelta(), 0); interfaces::AudioDecoderConfigPtr ptr( interfaces::AudioDecoderConfig::From(config)); AudioDecoderConfig result(ptr.To<AudioDecoderConfig>()); @@ -146,7 +148,7 @@ TEST(MediaTypeConvertersTest, ConvertAudioDecoderConfig_NullExtraData) { TEST(MediaTypeConvertersTest, ConvertAudioDecoderConfig_Encrypted) { AudioDecoderConfig config; config.Initialize(kCodecAAC, kSampleFormatU8, CHANNEL_LAYOUT_SURROUND, 48000, - NULL, 0, + EmptyExtraData(), true, // Is encrypted. base::TimeDelta(), 0); interfaces::AudioDecoderConfigPtr ptr( diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc index 9e9bf87..2367c42 100644 --- a/media/renderers/audio_renderer_impl_unittest.cc +++ b/media/renderers/audio_renderer_impl_unittest.cc @@ -13,6 +13,7 @@ #include "media/base/audio_splicer.h" #include "media/base/fake_audio_renderer_sink.h" #include "media/base/gmock_callback_support.h" +#include "media/base/media_util.h" #include "media/base/mock_filters.h" #include "media/base/test_helpers.h" #include "media/renderers/audio_renderer_impl.h" @@ -69,8 +70,7 @@ class AudioRendererImplTest : public ::testing::Test { kSampleFormat, kChannelLayout, kInputSamplesPerSecond, - NULL, - 0, + EmptyExtraData(), false); demuxer_stream_.set_audio_decoder_config(audio_config); |