diff options
21 files changed, 413 insertions, 40 deletions
diff --git a/content/browser/media/media_canplaytype_browsertest.cc b/content/browser/media/media_canplaytype_browsertest.cc index 58c4b6d..5b99d6f 100644 --- a/content/browser/media/media_canplaytype_browsertest.cc +++ b/content/browser/media/media_canplaytype_browsertest.cc @@ -56,6 +56,19 @@ const char* kMp2tsMaybe = kNot; const char* kMp2tsProbably = kNot; #endif +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) +const char* kAc3Eac3Probably = kPropProbably; +#else +const char* kAc3Eac3Probably = kNot; +#endif + +#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER) && \ + BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) +const char* kMp2tsAc3Eac3Probably = kPropProbably; +#else +const char* kMp2tsAc3Eac3Probably = kNot; +#endif + namespace content { class MediaCanPlayTypeTest : public MediaBrowserTest { @@ -220,6 +233,23 @@ class MediaCanPlayTypeTest : public MediaBrowserTest { EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"avc3x\"'")); EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4ax\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ac\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ec\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ac-\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ec-\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ac-2\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ec-2\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ac3\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ec3\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ac-4\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ec-4\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a4\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a7\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a5.\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a6.\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a5.1\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a6.1\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"unknown\"'")); // Don't allow incomplete/ambiguous codec ids for HEVC. @@ -300,6 +330,13 @@ class MediaCanPlayTypeTest : public MediaBrowserTest { EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"theora, mp4a.40.2\"'")); EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"theora, mp4a.40.02\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ac-3\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ec-3\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.A5\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.A6\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a5\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a6\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"1\"'")); EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"theora, 1\"'")); @@ -349,6 +386,13 @@ class MediaCanPlayTypeTest : public MediaBrowserTest { EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"vp8.0, mp4a.40\"'")); EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"vp9.0, mp4a.40\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ac-3\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ec-3\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.A5\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.A6\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a5\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a6\"'")); + // Codecs are case sensitive. EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"VP8, Vorbis\"'")); EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"VP8.0, Opus\"'")); @@ -388,6 +432,13 @@ class MediaCanPlayTypeTest : public MediaBrowserTest { EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"mp4a.40.2\"'")); EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"mp4a.40.02\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ac-3\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"ec-3\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.A5\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.A6\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a5\"'")); + EXPECT_EQ(kNot, CanPlay("'" + mime + "; codecs=\"mp4a.a6\"'")); + // Unknown codec. EXPECT_EQ(kNot, CanPlay("'" + mime +"; codecs=\"unknown\"'")); } @@ -596,6 +647,31 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) { EXPECT_EQ(kPropProbably, CanPlay("'video/mp4; codecs=\"avc3.42E01E, mp4a.40.29\"'")); + // AC3 and EAC3 (aka Dolby Digital Plus, DD+) audio codecs. + // TODO(servolk): Strictly speaking only mp4a.A5 and mp4a.A6 codec ids are + // valid according to RFC 6381 section 3.3, 3.4. Lower-case oti (mp4a.a5 and + // mp4a.a6) should be rejected. But we used to allow those in older versions + // of Chromecast firmware and some apps (notably MPL) depend on those codec + // types being supported, so they should be allowed for now (crbug.com/564960) + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/mp4; codecs=\"ac-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/mp4; codecs=\"mp4a.a5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/mp4; codecs=\"mp4a.A5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/mp4; codecs=\"ec-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/mp4; codecs=\"mp4a.a6\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/mp4; codecs=\"mp4a.A6\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/mp4; codecs=\"avc1.640028,ac-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/mp4; codecs=\"avc1.640028,mp4a.a5\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/mp4; codecs=\"avc1.640028,mp4a.A5\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/mp4; codecs=\"avc1.640028,ec-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/mp4; codecs=\"avc1.640028,mp4a.a6\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/mp4; codecs=\"avc1.640028,mp4a.A6\"'")); + EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1, mp4a.40.2\"'")); EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc1, mp4a.40.02\"'")); EXPECT_EQ(kPropMaybe, CanPlay("'video/mp4; codecs=\"avc3, mp4a.40.2\"'")); @@ -670,6 +746,25 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) { EXPECT_EQ(kHevcSupported, CanPlay("'video/x-m4v; codecs=\"hvc1.1.6.L93.B0, mp4a.40.5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/x-m4v; codecs=\"ac-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/x-m4v; codecs=\"mp4a.a5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/x-m4v; codecs=\"mp4a.A5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/x-m4v; codecs=\"ec-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/x-m4v; codecs=\"mp4a.a6\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'video/x-m4v; codecs=\"mp4a.A6\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/x-m4v; codecs=\"avc1.640028,ac-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/x-m4v; codecs=\"avc1.640028,mp4a.a5\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/x-m4v; codecs=\"avc1.640028,mp4a.A5\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/x-m4v; codecs=\"avc1.640028,ec-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/x-m4v; codecs=\"avc1.640028,mp4a.a6\"'")); + EXPECT_EQ(kAc3Eac3Probably, + CanPlay("'video/x-m4v; codecs=\"avc1.640028,mp4a.A6\"'")); + TestMPEGUnacceptableCombinations("video/x-m4v"); EXPECT_EQ(kPropMaybe, CanPlay("'audio/mp4'")); @@ -694,6 +789,13 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) { EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"hev1.1.6.L93.B0,mp4a.40.5\"'")); EXPECT_EQ(kNot, CanPlay("'audio/mp4; codecs=\"hvc1.1.6.L93.B0,mp4a.40.5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/mp4; codecs=\"ac-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/mp4; codecs=\"mp4a.a5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/mp4; codecs=\"mp4a.A5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/mp4; codecs=\"ec-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/mp4; codecs=\"mp4a.a6\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/mp4; codecs=\"mp4a.A6\"'")); + TestMPEGUnacceptableCombinations("audio/mp4"); EXPECT_EQ(kPropMaybe, CanPlay("'audio/x-m4a'")); @@ -720,6 +822,13 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) { EXPECT_EQ(kNot, CanPlay("'audio/x-m4a; codecs=\"hvc1.1.6.L93.B0, mp4a.40.5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/x-m4a; codecs=\"ac-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/x-m4a; codecs=\"mp4a.a5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/x-m4a; codecs=\"mp4a.A5\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/x-m4a; codecs=\"ec-3\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/x-m4a; codecs=\"mp4a.a6\"'")); + EXPECT_EQ(kAc3Eac3Probably, CanPlay("'audio/x-m4a; codecs=\"mp4a.A6\"'")); + TestMPEGUnacceptableCombinations("audio/x-m4a"); } @@ -1106,6 +1215,13 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_HLS) { EXPECT_EQ(kNot, CanPlay("'application/x-mpegurl; codecs=\"hvc1.1.6.L93.B0,mp4a.40.5\"'")); + EXPECT_EQ(kNot, CanPlay("'application/x-mpegurl; codecs=\"ac-3\"'")); + EXPECT_EQ(kNot, CanPlay("'application/x-mpegurl; codecs=\"ec-3\"'")); + EXPECT_EQ(kNot, CanPlay("'application/x-mpegurl; codecs=\"mp4a.A5\"'")); + EXPECT_EQ(kNot, CanPlay("'application/x-mpegurl; codecs=\"mp4a.A6\"'")); + EXPECT_EQ(kNot, CanPlay("'application/x-mpegurl; codecs=\"mp4a.a5\"'")); + EXPECT_EQ(kNot, CanPlay("'application/x-mpegurl; codecs=\"mp4a.a6\"'")); + EXPECT_EQ(maybeCanPlayHLS, CanPlay("'application/x-mpegurl; codecs=\"avc1, mp4a.40.2\"'")); EXPECT_EQ(maybeCanPlayHLS, @@ -1189,6 +1305,17 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_HLS) { CanPlay("'application/vnd.apple.mpegurl; " "codecs=\"hvc1.1.6.L93.B0,mp4a.40.5\"'")); + EXPECT_EQ(kNot, CanPlay("'application/vnd.apple.mpegurl; codecs=\"ac-3\"'")); + EXPECT_EQ(kNot, CanPlay("'application/vnd.apple.mpegurl; codecs=\"ec-3\"'")); + EXPECT_EQ(kNot, + CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.A5\"'")); + EXPECT_EQ(kNot, + CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.A6\"'")); + EXPECT_EQ(kNot, + CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.a5\"'")); + EXPECT_EQ(kNot, + CanPlay("'application/vnd.apple.mpegurl; codecs=\"mp4a.a6\"'")); + TestMPEGUnacceptableCombinations("application/vnd.apple.mpegurl"); } @@ -1218,6 +1345,19 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_Mpeg2Ts) { CanPlay("'video/mp2t; codecs=\"avc1.4D401E,mp4a.40.2\"'")); EXPECT_EQ(kMp2tsProbably, CanPlay("'video/mp2t; codecs=\"avc1.640028,mp4a.40.2\"'")); + // H.264 + AC3/EAC3 audio combinations + EXPECT_EQ(kMp2tsAc3Eac3Probably, + CanPlay("'video/mp2t; codecs=\"avc1.640028,ac-3\"'")); + EXPECT_EQ(kMp2tsAc3Eac3Probably, + CanPlay("'video/mp2t; codecs=\"avc1.640028,ec-3\"'")); + EXPECT_EQ(kMp2tsAc3Eac3Probably, + CanPlay("'video/mp2t; codecs=\"avc1.640028,mp4a.A5\"'")); + EXPECT_EQ(kMp2tsAc3Eac3Probably, + CanPlay("'video/mp2t; codecs=\"avc1.640028,mp4a.A6\"'")); + EXPECT_EQ(kMp2tsAc3Eac3Probably, + CanPlay("'video/mp2t; codecs=\"avc1.640028,mp4a.a5\"'")); + EXPECT_EQ(kMp2tsAc3Eac3Probably, + CanPlay("'video/mp2t; codecs=\"avc1.640028,mp4a.a6\"'")); TestMPEGUnacceptableCombinations("video/mp2t"); } diff --git a/media/BUILD.gn b/media/BUILD.gn index 7d03d4d..b356a33 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -14,8 +14,10 @@ import("//testing/test.gni") buildflag_header("media_features") { header = "media_features.h" - flags = - [ "ENABLE_MSE_MPEG2TS_STREAM_PARSER=$enable_mse_mpeg2ts_stream_parser" ] + flags = [ + "ENABLE_AC3_EAC3_AUDIO_DEMUXING=$enable_ac3_eac3_audio_demuxing", + "ENABLE_MSE_MPEG2TS_STREAM_PARSER=$enable_mse_mpeg2ts_stream_parser", + ] } # Common configuration for targets in the media directory. diff --git a/media/base/android/demuxer_stream_player_params.cc b/media/base/android/demuxer_stream_player_params.cc index 2452711..3ce2a76 100644 --- a/media/base/android/demuxer_stream_player_params.cc +++ b/media/base/android/demuxer_stream_player_params.cc @@ -66,8 +66,10 @@ const char* AsString(AudioCodec codec) { RETURN_STRING(kCodecPCM_S16BE); RETURN_STRING(kCodecPCM_S24BE); RETURN_STRING(kCodecOpus); + RETURN_STRING(kCodecEAC3); RETURN_STRING(kCodecPCM_ALAW); RETURN_STRING(kCodecALAC); + RETURN_STRING(kCodecAC3); } NOTREACHED(); return nullptr; // crash early diff --git a/media/base/audio_decoder_config.cc b/media/base/audio_decoder_config.cc index 17f039f..91dc9a6 100644 --- a/media/base/audio_decoder_config.cc +++ b/media/base/audio_decoder_config.cc @@ -122,8 +122,12 @@ std::string AudioDecoderConfig::GetHumanReadableCodecName() const { return "opus"; case kCodecPCM_ALAW: return "pcm_alaw"; + case kCodecEAC3: + return "eac3"; case kCodecALAC: return "alac"; + case kCodecAC3: + return "ac3"; } NOTREACHED(); return ""; diff --git a/media/base/audio_decoder_config.h b/media/base/audio_decoder_config.h index e88c0eb..7205e56 100644 --- a/media/base/audio_decoder_config.h +++ b/media/base/audio_decoder_config.h @@ -36,16 +36,17 @@ enum AudioCodec { kCodecPCM_S16BE = 10, kCodecPCM_S24BE = 11, kCodecOpus = 12, - // kCodecEAC3 = 13, + kCodecEAC3 = 13, kCodecPCM_ALAW = 14, kCodecALAC = 15, + kCodecAC3 = 16, // DO NOT ADD RANDOM AUDIO CODECS! // // The only acceptable time to add a new codec is if there is production code // that uses said codec in the same CL. // Must always be equal to the largest entry ever logged. - kAudioCodecMax = kCodecALAC, + kAudioCodecMax = kCodecAC3, }; // TODO(dalecurtis): FFmpeg API uses |bytes_per_channel| instead of diff --git a/media/base/mime_util.cc b/media/base/mime_util.cc index 01422b2..9abd15a 100644 --- a/media/base/mime_util.cc +++ b/media/base/mime_util.cc @@ -30,6 +30,8 @@ class MimeUtil { INVALID_CODEC, PCM, MP3, + AC3, + EAC3, MPEG2_AAC_LC, MPEG2_AAC_MAIN, MPEG2_AAC_SSR, @@ -152,6 +154,11 @@ static bool IsCodecSupportedOnAndroid(MimeUtil::Codec codec) { case MimeUtil::VP8: return true; + case MimeUtil::AC3: + case MimeUtil::EAC3: + // TODO(servolk): Revisit this for AC3/EAC3 support on AndroidTV + return false; + case MimeUtil::MPEG2_AAC_LC: case MimeUtil::MPEG2_AAC_MAIN: case MimeUtil::MPEG2_AAC_SSR: @@ -210,6 +217,11 @@ struct MediaFormat { // avc1.6400xx - H.264 High static const char kMP4AudioCodecsExpression[] = "mp4a.66,mp4a.67,mp4a.68,mp4a.69,mp4a.6B,mp4a.40.2,mp4a.40.02,mp4a.40.5," +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + // Only one variant each of ac3 and eac3 codec string is sufficient here, + // since these strings are parsed and mapped to MimeUtil::Codec enum values. + "ac-3,ec-3," +#endif "mp4a.40.05,mp4a.40.29"; static const char kMP4VideoCodecsExpression[] = // This is not a complete list of supported avc1 codecs. It is simply used @@ -225,6 +237,11 @@ static const char kMP4VideoCodecsExpression[] = "hev1.1.6.L93.B0," #endif "mp4a.66,mp4a.67,mp4a.68,mp4a.69,mp4a.6B,mp4a.40.2,mp4a.40.02,mp4a.40.5," +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + // Only one variant each of ac3 and eac3 codec string is sufficient here, + // since these strings are parsed and mapped to MimeUtil::Codec enum values. + "ac-3,ec-3," +#endif "mp4a.40.05,mp4a.40.29"; #endif // USE_PROPRIETARY_CODECS @@ -291,6 +308,20 @@ static const CodecIDMappings kUnambiguousCodecStringMap[] = { {"mp4a.40.5", MimeUtil::MPEG4_AAC_SBR_v1}, {"mp4a.40.05", MimeUtil::MPEG4_AAC_SBR_v1}, {"mp4a.40.29", MimeUtil::MPEG4_AAC_SBR_PS_v2}, +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + // TODO(servolk): Strictly speaking only mp4a.A5 and mp4a.A6 codec ids are + // valid according to RFC 6381 section 3.3, 3.4. Lower-case oti (mp4a.a5 and + // mp4a.a6) should be rejected. But we used to allow those in older versions + // of Chromecast firmware and some apps (notably MPL) depend on those codec + // types being supported, so they should be allowed for now + // (crbug.com/564960). + {"ac-3", MimeUtil::AC3}, + {"mp4a.a5", MimeUtil::AC3}, + {"mp4a.A5", MimeUtil::AC3}, + {"ec-3", MimeUtil::EAC3}, + {"mp4a.a6", MimeUtil::EAC3}, + {"mp4a.A6", MimeUtil::EAC3}, +#endif {"vorbis", MimeUtil::VORBIS}, {"opus", MimeUtil::OPUS}, {"vp8", MimeUtil::VP8}, @@ -638,6 +669,8 @@ bool MimeUtil::IsCodecSupported(Codec codec) const { bool MimeUtil::IsCodecProprietary(Codec codec) const { switch (codec) { case INVALID_CODEC: + case AC3: + case EAC3: case MP3: case MPEG2_AAC_LC: case MPEG2_AAC_MAIN: diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc index 3d3a8b8..b65cedd 100644 --- a/media/ffmpeg/ffmpeg_common.cc +++ b/media/ffmpeg/ffmpeg_common.cc @@ -14,6 +14,7 @@ #include "media/base/decoder_buffer.h" #include "media/base/video_decoder_config.h" #include "media/base/video_util.h" +#include "media/media_features.h" namespace media { @@ -67,6 +68,12 @@ static AudioCodec CodecIDToAudioCodec(AVCodecID codec_id) { switch (codec_id) { case AV_CODEC_ID_AAC: return kCodecAAC; +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + case AV_CODEC_ID_AC3: + return kCodecAC3; + case AV_CODEC_ID_EAC3: + return kCodecEAC3; +#endif case AV_CODEC_ID_MP3: return kCodecMP3; case AV_CODEC_ID_VORBIS: @@ -309,18 +316,37 @@ bool AVCodecContextToAudioDecoderConfig(const AVCodecContext* codec_context, codec_context->channel_layout, codec_context->channels); int sample_rate = codec_context->sample_rate; - if (codec == kCodecOpus) { - // |codec_context->sample_fmt| is not set by FFmpeg because Opus decoding is - // not enabled in FFmpeg. It doesn't matter what value is set here, so long - // as it's valid, the true sample format is selected inside the decoder. - sample_format = kSampleFormatF32; - - // Always use 48kHz for OPUS. Technically we should match to the highest - // supported hardware sample rate among [8, 12, 16, 24, 48] kHz, but we - // don't know the hardware sample rate at this point and those rates are - // rarely used for output. See the "Input Sample Rate" section of the spec: - // http://tools.ietf.org/html/draft-terriberry-oggopus-01#page-11 - sample_rate = 48000; + switch (codec) { + case kCodecOpus: + // |codec_context->sample_fmt| is not set by FFmpeg because Opus decoding + // is not enabled in FFmpeg. It doesn't matter what value is set here, so + // long as it's valid, the true sample format is selected inside the + // decoder. + sample_format = kSampleFormatF32; + + // Always use 48kHz for OPUS. Technically we should match to the highest + // supported hardware sample rate among [8, 12, 16, 24, 48] kHz, but we + // don't know the hardware sample rate at this point and those rates are + // rarely used for output. See the "Input Sample Rate" section of the + // spec: http://tools.ietf.org/html/draft-terriberry-oggopus-01#page-11 + sample_rate = 48000; + break; + + // For AC3/EAC3 we enable only demuxing, but not decoding, so FFmpeg does + // not fill |sample_fmt|. + case kCodecAC3: + case kCodecEAC3: +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + // The spec for AC3/EAC3 audio is ETSI TS 102 366. According to sections + // F.3.1 and F.5.1 in that spec the sample_format for AC3/EAC3 must be 16. + sample_format = kSampleFormatS16; +#else + NOTREACHED(); +#endif + break; + + default: + break; } base::TimeDelta seek_preroll; @@ -353,9 +379,19 @@ bool AVCodecContextToAudioDecoderConfig(const AVCodecContext* codec_context, seek_preroll, codec_context->delay); - if (codec != kCodecOpus) { - DCHECK_EQ(av_get_bytes_per_sample(codec_context->sample_fmt) * 8, - config->bits_per_channel()); + // Verify that AudioConfig.bits_per_channel was calculated correctly for + // codecs that have |sample_fmt| set by FFmpeg. + switch (codec) { + case kCodecOpus: +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + case kCodecAC3: + case kCodecEAC3: +#endif + break; + default: + DCHECK_EQ(av_get_bytes_per_sample(codec_context->sample_fmt) * 8, + config->bits_per_channel()); + break; } return true; diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index 1869b7a..2d1a868 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc @@ -24,6 +24,7 @@ #include "media/filters/ffmpeg_demuxer.h" #include "media/filters/file_data_source.h" #include "media/formats/mp4/avc.h" +#include "media/media_features.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::AnyNumber; @@ -1132,4 +1133,36 @@ TEST_F(FFmpegDemuxerTest, HEVC_in_MP4_container) { } #endif +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) +TEST_F(FFmpegDemuxerTest, Read_AC3_Audio) { + CreateDemuxer("bear-ac3-only-frag.mp4"); + InitializeDemuxer(); + + // Attempt a read from the audio stream and run the message loop until done. + DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO); + + // Read the first two frames and check that we are getting expected data + audio->Read(NewReadCB(FROM_HERE, 834, 0, true)); + message_loop_.Run(); + + audio->Read(NewReadCB(FROM_HERE, 836, 34830, true)); + message_loop_.Run(); +} + +TEST_F(FFmpegDemuxerTest, Read_EAC3_Audio) { + CreateDemuxer("bear-eac3-only-frag.mp4"); + InitializeDemuxer(); + + // Attempt a read from the audio stream and run the message loop until done. + DemuxerStream* audio = demuxer_->GetStream(DemuxerStream::AUDIO); + + // Read the first two frames and check that we are getting expected data + audio->Read(NewReadCB(FROM_HERE, 870, 0, true)); + message_loop_.Run(); + + audio->Read(NewReadCB(FROM_HERE, 872, 34830, true)); + message_loop_.Run(); +} +#endif // ENABLE_AC3_EAC3_AUDIO_DEMUXING + } // namespace media diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc index db626c5..43e0079 100644 --- a/media/filters/stream_parser_factory.cc +++ b/media/filters/stream_parser_factory.cc @@ -58,7 +58,8 @@ struct CodecInfo { HISTOGRAM_MP3, HISTOGRAM_OPUS, HISTOGRAM_HEVC, - HISTOGRAM_MAX = HISTOGRAM_HEVC // Must be equal to largest logged entry. + HISTOGRAM_AC3, + HISTOGRAM_MAX = HISTOGRAM_AC3 // Must be equal to largest logged entry. }; const char* pattern; @@ -166,6 +167,26 @@ static const CodecInfo kMPEG4AACCodecInfo = { "mp4a.40.*", CodecInfo::AUDIO, static const CodecInfo kMPEG2AACLCCodecInfo = { "mp4a.67", CodecInfo::AUDIO, NULL, CodecInfo::HISTOGRAM_MPEG2AAC }; +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) +// The 'ac-3' and 'ec-3' are mime codec ids for AC3 and EAC3 according to +// http://www.mp4ra.org/codecs.html +// The object types for AC3 and EAC3 in MP4 container are 0xa5 and 0xa6, so +// according to RFC 6381 this corresponds to codec ids 'mp4a.A5' and 'mp4a.A6'. +// Codec ids with lower case oti (mp4a.a5 and mp4a.a6) are supported for +// backward compatibility. +static const CodecInfo kAC3CodecInfo1 = {"ac-3", CodecInfo::AUDIO, NULL, + CodecInfo::HISTOGRAM_AC3}; +static const CodecInfo kAC3CodecInfo2 = {"mp4a.a5", CodecInfo::AUDIO, NULL, + CodecInfo::HISTOGRAM_AC3}; +static const CodecInfo kAC3CodecInfo3 = {"mp4a.A5", CodecInfo::AUDIO, NULL, + CodecInfo::HISTOGRAM_AC3}; +static const CodecInfo kEAC3CodecInfo1 = {"ec-3", CodecInfo::AUDIO, NULL, + CodecInfo::HISTOGRAM_EAC3}; +static const CodecInfo kEAC3CodecInfo2 = {"mp4a.a6", CodecInfo::AUDIO, NULL, + CodecInfo::HISTOGRAM_EAC3}; +static const CodecInfo kEAC3CodecInfo3 = {"mp4a.A6", CodecInfo::AUDIO, NULL, + CodecInfo::HISTOGRAM_EAC3}; +#endif static const CodecInfo* kVideoMP4Codecs[] = { &kH264AVC1CodecInfo, @@ -179,11 +200,17 @@ static const CodecInfo* kVideoMP4Codecs[] = { NULL }; -static const CodecInfo* kAudioMP4Codecs[] = { - &kMPEG4AACCodecInfo, - &kMPEG2AACLCCodecInfo, - NULL -}; +static const CodecInfo* kAudioMP4Codecs[] = {&kMPEG4AACCodecInfo, + &kMPEG2AACLCCodecInfo, +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + &kAC3CodecInfo1, + &kAC3CodecInfo2, + &kAC3CodecInfo3, + &kEAC3CodecInfo1, + &kEAC3CodecInfo2, + &kEAC3CodecInfo3, +#endif + NULL}; static StreamParser* BuildMP4Parser(const std::vector<std::string>& codecs, const scoped_refptr<MediaLog>& media_log) { @@ -205,6 +232,16 @@ static StreamParser* BuildMP4Parser(const std::vector<std::string>& codecs, has_sbr = true; break; } +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + } else if (base::MatchPattern(codec_id, kAC3CodecInfo1.pattern) || + base::MatchPattern(codec_id, kAC3CodecInfo2.pattern) || + base::MatchPattern(codec_id, kAC3CodecInfo3.pattern)) { + audio_object_types.insert(mp4::kAC3); + } else if (base::MatchPattern(codec_id, kEAC3CodecInfo1.pattern) || + base::MatchPattern(codec_id, kEAC3CodecInfo2.pattern) || + base::MatchPattern(codec_id, kEAC3CodecInfo3.pattern)) { + audio_object_types.insert(mp4::kEAC3); +#endif } } diff --git a/media/formats/mp4/es_descriptor.h b/media/formats/mp4/es_descriptor.h index 26ca86d..9066291 100644 --- a/media/formats/mp4/es_descriptor.h +++ b/media/formats/mp4/es_descriptor.h @@ -21,8 +21,10 @@ namespace mp4 { // objectTypeIndication Values. Only values currently in use are included. enum ObjectType { kForbidden = 0, - kISO_14496_3 = 0x40, // MPEG4 AAC - kISO_13818_7_AAC_LC = 0x67 // MPEG2 AAC-LC + kISO_14496_3 = 0x40, // MPEG4 AAC + kISO_13818_7_AAC_LC = 0x67, // MPEG2 AAC-LC + kAC3 = 0xa5, // AC3 + kEAC3 = 0xa6 // EAC3 / Dolby Digital Plus }; // This class parse object type and decoder specific information from an diff --git a/media/formats/mp4/fourccs.h b/media/formats/mp4/fourccs.h index fd97797..38834fb 100644 --- a/media/formats/mp4/fourccs.h +++ b/media/formats/mp4/fourccs.h @@ -7,11 +7,17 @@ #include <string> +#include "media/media_features.h" + namespace media { namespace mp4 { enum FourCC { FOURCC_NULL = 0, +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + FOURCC_AC3 = 0x61632d33, // "ac-3" + FOURCC_EAC3 = 0x65632d33, // "ec-3" +#endif FOURCC_AVC1 = 0x61766331, FOURCC_AVC3 = 0x61766333, FOURCC_AVCC = 0x61766343, diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc index ee88ea1..966d250 100644 --- a/media/formats/mp4/mp4_stream_parser.cc +++ b/media/formats/mp4/mp4_stream_parser.cc @@ -220,9 +220,18 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx]; const AAC& aac = entry.esds.aac; - if (!(entry.format == FOURCC_MP4A || - (entry.format == FOURCC_ENCA && - entry.sinf.format.format == FOURCC_MP4A))) { + // For encrypted audio streams entry.format is FOURCC_ENCA and actual + // format is in entry.sinf.format.format. + FourCC audio_format = (entry.format == FOURCC_ENCA) + ? entry.sinf.format.format + : entry.format; + +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + if (audio_format != FOURCC_MP4A && audio_format != FOURCC_AC3 && + audio_format != FOURCC_EAC3) { +#else + if (audio_format != FOURCC_MP4A) { +#endif MEDIA_LOG(ERROR, media_log_) << "Unsupported audio format 0x" << std::hex << entry.format << " in stsd box."; @@ -230,12 +239,20 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { } uint8_t audio_type = entry.esds.object_type; - DVLOG(1) << "audio_type " << std::hex << static_cast<int>(audio_type); +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + if (audio_type == kForbidden) { + if (audio_format == FOURCC_AC3) + audio_type = kAC3; + if (audio_format == FOURCC_EAC3) + audio_type = kEAC3; + } +#endif + DVLOG(1) << "audio_type 0x" << std::hex << static_cast<int>(audio_type); if (audio_object_types_.find(audio_type) == audio_object_types_.end()) { MEDIA_LOG(ERROR, media_log_) - << "audio object type 0x" << std::hex << audio_type - << " does not match what is specified in the" - << " mimetype."; + << "audio object type 0x" << std::hex + << static_cast<int>(audio_type) + << " does not match what is specified in the mimetype."; return false; } @@ -252,9 +269,20 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { #if defined(OS_ANDROID) extra_data = aac.codec_specific_data(); #endif +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) + } else if (audio_type == kAC3) { + codec = kCodecAC3; + channel_layout = GuessChannelLayout(entry.channelcount); + sample_per_second = entry.samplerate; + } else if (audio_type == kEAC3) { + codec = kCodecEAC3; + channel_layout = GuessChannelLayout(entry.channelcount); + sample_per_second = entry.samplerate; +#endif } else { MEDIA_LOG(ERROR, media_log_) << "Unsupported audio object type 0x" - << std::hex << audio_type << " in esds."; + << std::hex << static_cast<int>(audio_type) + << " in esds."; return false; } diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc index 79f68c1..eec9824 100644 --- a/media/formats/mp4/mp4_stream_parser_unittest.cc +++ b/media/formats/mp4/mp4_stream_parser_unittest.cc @@ -22,6 +22,7 @@ #include "media/base/video_decoder_config.h" #include "media/formats/mp4/es_descriptor.h" #include "media/formats/mp4/mp4_stream_parser.h" +#include "media/media_features.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -341,5 +342,27 @@ TEST_F(MP4StreamParserTest, NaturalSizeWithPASP) { EXPECT_EQ(gfx::Size(639, 360), video_decoder_config_.natural_size()); } +#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING) +TEST_F(MP4StreamParserTest, DemuxingAC3) { + std::set<int> audio_object_types; + audio_object_types.insert(kAC3); + parser_.reset(new MP4StreamParser(audio_object_types, false)); + InitializeParserAndExpectLiveness(DemuxerStream::LIVENESS_RECORDED); + scoped_refptr<DecoderBuffer> buffer = + ReadTestDataFile("bear-ac3-only-frag.mp4"); + EXPECT_TRUE(AppendDataInPieces(buffer->data(), buffer->data_size(), 512)); +} + +TEST_F(MP4StreamParserTest, DemuxingEAC3) { + std::set<int> audio_object_types; + audio_object_types.insert(kEAC3); + parser_.reset(new MP4StreamParser(audio_object_types, false)); + InitializeParserAndExpectLiveness(DemuxerStream::LIVENESS_RECORDED); + scoped_refptr<DecoderBuffer> buffer = + ReadTestDataFile("bear-eac3-only-frag.mp4"); + EXPECT_TRUE(AppendDataInPieces(buffer->data(), buffer->data_size(), 512)); +} +#endif + } // namespace mp4 } // namespace media diff --git a/media/media.gyp b/media/media.gyp index 620a77a..b8d7b9e 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -33,9 +33,13 @@ }, { 'use_low_memory_buffer%': 0, }], - ['chromecast == 1', { + ['proprietary_codecs==1 and chromecast==1', { + # Enable AC3/EAC3 audio demuxing. Actual decoding must be provided by + # the platform (or HDMI sink in Chromecast for audio pass-through case). + 'enable_ac3_eac3_audio_demuxing%': 1, 'enable_mse_mpeg2ts_stream_parser%': 1, }, { + 'enable_ac3_eac3_audio_demuxing%': 0, 'enable_mse_mpeg2ts_stream_parser%': 0, }], ['chromecast==1', { @@ -59,6 +63,7 @@ 'variables': { 'buildflag_header_path': 'media/media_features.h', 'buildflag_flags': [ + "ENABLE_AC3_EAC3_AUDIO_DEMUXING=<(enable_ac3_eac3_audio_demuxing)", "ENABLE_MSE_MPEG2TS_STREAM_PARSER=<(enable_mse_mpeg2ts_stream_parser)", ], }, diff --git a/media/media_options.gni b/media/media_options.gni index fa01d0f..635c4d2 100644 --- a/media/media_options.gni +++ b/media/media_options.gni @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/config/chromecast_build.gni") +import("//build/config/features.gni") import("//build/config/headless_build.gni") declare_args() { @@ -48,7 +49,12 @@ declare_args() { # Use low-memory buffers on non-Android builds of Chromecast. use_low_memory_buffer = is_chromecast && !is_android - enable_mse_mpeg2ts_stream_parser = is_chromecast + # Enables AC3/EAC3 audio demuxing. This is enabled only on Chromecast, since + # it only provides demuxing, and is only useful for AC3/EAC3 audio + # pass-through to HDMI sink on Chromecast. + enable_ac3_eac3_audio_demuxing = proprietary_codecs && is_chromecast + + enable_mse_mpeg2ts_stream_parser = proprietary_codecs && is_chromecast # Enable HEVC/H265 demuxing. Actual decoding must be provided by the # platform. Enable by default for Chromecast. diff --git a/media/mojo/interfaces/media_types.mojom b/media/mojo/interfaces/media_types.mojom index befd80a..3890b2a 100644 --- a/media/mojo/interfaces/media_types.mojom +++ b/media/mojo/interfaces/media_types.mojom @@ -29,10 +29,11 @@ enum AudioCodec { PCM_S16BE = 10, PCM_S24BE = 11, Opus = 12, - // EAC3 = 13, + EAC3 = 13, PCM_ALAW = 14, ALAC = 15, - MAX = ALAC, + AC3 = 16, + MAX = AC3, }; // See media/base/channel_layout.h for descriptions. diff --git a/media/mojo/services/media_type_converters.cc b/media/mojo/services/media_type_converters.cc index d3cc1e3..679accf 100644 --- a/media/mojo/services/media_type_converters.cc +++ b/media/mojo/services/media_type_converters.cc @@ -54,8 +54,10 @@ ASSERT_ENUM_EQ(AudioCodec, kCodec, AUDIO_CODEC_, GSM_MS); ASSERT_ENUM_EQ(AudioCodec, kCodec, AUDIO_CODEC_, PCM_S16BE); ASSERT_ENUM_EQ(AudioCodec, kCodec, AUDIO_CODEC_, PCM_S24BE); ASSERT_ENUM_EQ(AudioCodec, kCodec, AUDIO_CODEC_, Opus); +ASSERT_ENUM_EQ(AudioCodec, kCodec, AUDIO_CODEC_, EAC3); ASSERT_ENUM_EQ(AudioCodec, kCodec, AUDIO_CODEC_, PCM_ALAW); ASSERT_ENUM_EQ(AudioCodec, kCodec, AUDIO_CODEC_, ALAC); +ASSERT_ENUM_EQ(AudioCodec, kCodec, AUDIO_CODEC_, AC3); ASSERT_ENUM_EQ_RAW(AudioCodec, kAudioCodecMax, AUDIO_CODEC_MAX); // ChannelLayout. diff --git a/media/test/data/README b/media/test/data/README index 773ff84..8ab8273 100644 --- a/media/test/data/README +++ b/media/test/data/README @@ -196,4 +196,13 @@ media/test/data/bear-640x360-non_square_pixel-with_pasp.mp4 media/test/data/bear-640x360-non_square_pixel-without_pasp.mp4 Size in TKHD is (639.2x360) and size in STSD is (470x360). No PASP box is - present.
\ No newline at end of file + present. + +// MP4 files with AC3 and EAC3 audio +media/test/data/bear-ac3-only-frag.mp4 + AC3 audio in framented MP4, generated with + ffmpeg -i bear.ac3 -acodec copy -movflags frag_keyframe bear-ac3-only-frag.mp4 + +media/test/data/bear-eac3-only-frag.mp4 + EAC3 audio in framented MP4, generated with + ffmpeg -i bear.eac3 -acodec copy -movflags frag_keyframe bear-eac3-only-frag.mp4 diff --git a/media/test/data/bear-ac3-only-frag.mp4 b/media/test/data/bear-ac3-only-frag.mp4 Binary files differnew file mode 100644 index 0000000..669c407 --- /dev/null +++ b/media/test/data/bear-ac3-only-frag.mp4 diff --git a/media/test/data/bear-eac3-only-frag.mp4 b/media/test/data/bear-eac3-only-frag.mp4 Binary files differnew file mode 100644 index 0000000..1530fd1 --- /dev/null +++ b/media/test/data/bear-eac3-only-frag.mp4 diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 26ec39a..d19089e 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -56612,8 +56612,10 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. <int value="10" label="kCodecPCM_S16BE"/> <int value="11" label="kCodecPCM_S24BE"/> <int value="12" label="kCodecOpus"/> + <int value="13" label="kCodecEAC3"/> <int value="14" label="kCodecPCM_ALAW"/> <int value="15" label="kCodecALAC"/> + <int value="16" label="kCodecAC3"/> </enum> <enum name="AudioFramesPerBuffer" type="int"> @@ -70926,6 +70928,7 @@ To add a new entry, add it with any value and run test to compute valid value. <int value="8" label="MP3"/> <int value="9" label="OPUS"/> <int value="10" label="HEVC"/> + <int value="11" label="AC3"/> </enum> <enum name="MultiProfileSessionMode" type="int"> |