Re-add AC3/EAC3 audio demuxing support

Chrome used to have EAC3/DD+ support (crbug.com/215773),
but it was removed last year (crbug.com/334126). This CL
partially restores the old code and adds AC3 support.
This will allow AC3/EAC3 audio demuxing and can be used
for AC3/EAC3 pass-through support.

BUG=448878

Review URL: https://codereview.chromium.org/812643005

Cr-Original-Commit-Position: refs/heads/master@{#368476}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 063ce09474bc035a743aa565f87b7f506db8ff86
diff --git a/BUILD.gn b/BUILD.gn
index 7d03d4d..b356a33 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -14,8 +14,10 @@
 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/base/android/demuxer_stream_player_params.cc b/base/android/demuxer_stream_player_params.cc
index 2452711..3ce2a76 100644
--- a/base/android/demuxer_stream_player_params.cc
+++ b/base/android/demuxer_stream_player_params.cc
@@ -66,8 +66,10 @@
     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/base/audio_decoder_config.cc b/base/audio_decoder_config.cc
index 17f039f..91dc9a6 100644
--- a/base/audio_decoder_config.cc
+++ b/base/audio_decoder_config.cc
@@ -122,8 +122,12 @@
       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/base/audio_decoder_config.h b/base/audio_decoder_config.h
index e88c0eb..7205e56 100644
--- a/base/audio_decoder_config.h
+++ b/base/audio_decoder_config.h
@@ -36,16 +36,17 @@
   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/base/mime_util.cc b/base/mime_util.cc
index 01422b2..9abd15a 100644
--- a/base/mime_util.cc
+++ b/base/mime_util.cc
@@ -30,6 +30,8 @@
     INVALID_CODEC,
     PCM,
     MP3,
+    AC3,
+    EAC3,
     MPEG2_AAC_LC,
     MPEG2_AAC_MAIN,
     MPEG2_AAC_SSR,
@@ -152,6 +154,11 @@
     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 @@
 //   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 @@
     "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 @@
     {"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::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/ffmpeg/ffmpeg_common.cc b/ffmpeg/ffmpeg_common.cc
index 3d3a8b8..b65cedd 100644
--- a/ffmpeg/ffmpeg_common.cc
+++ b/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 @@
   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 @@
       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;
+  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;
+      // 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 @@
                      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/filters/ffmpeg_demuxer_unittest.cc b/filters/ffmpeg_demuxer_unittest.cc
index 1869b7a..2d1a868 100644
--- a/filters/ffmpeg_demuxer_unittest.cc
+++ b/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 @@
 }
 #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/filters/stream_parser_factory.cc b/filters/stream_parser_factory.cc
index db626c5..43e0079 100644
--- a/filters/stream_parser_factory.cc
+++ b/filters/stream_parser_factory.cc
@@ -58,7 +58,8 @@
     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 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 @@
   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 @@
         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/formats/mp4/es_descriptor.h b/formats/mp4/es_descriptor.h
index 26ca86d..9066291 100644
--- a/formats/mp4/es_descriptor.h
+++ b/formats/mp4/es_descriptor.h
@@ -21,8 +21,10 @@
 // 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/formats/mp4/fourccs.h b/formats/mp4/fourccs.h
index fd97797..38834fb 100644
--- a/formats/mp4/fourccs.h
+++ b/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/formats/mp4/mp4_stream_parser.cc b/formats/mp4/mp4_stream_parser.cc
index ee88ea1..966d250 100644
--- a/formats/mp4/mp4_stream_parser.cc
+++ b/formats/mp4/mp4_stream_parser.cc
@@ -220,9 +220,18 @@
       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 @@
       }
 
       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 @@
 #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/formats/mp4/mp4_stream_parser_unittest.cc b/formats/mp4/mp4_stream_parser_unittest.cc
index 79f68c1..eec9824 100644
--- a/formats/mp4/mp4_stream_parser_unittest.cc
+++ b/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 @@
   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.gyp b/media.gyp
index 620a77a..b8d7b9e 100644
--- a/media.gyp
+++ b/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_options.gni b/media_options.gni
index fa01d0f..635c4d2 100644
--- a/media_options.gni
+++ b/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 @@
   # 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/mojo/interfaces/media_types.mojom b/mojo/interfaces/media_types.mojom
index befd80a..3890b2a 100644
--- a/mojo/interfaces/media_types.mojom
+++ b/mojo/interfaces/media_types.mojom
@@ -29,10 +29,11 @@
   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/mojo/services/media_type_converters.cc b/mojo/services/media_type_converters.cc
index d3cc1e3..679accf 100644
--- a/mojo/services/media_type_converters.cc
+++ b/mojo/services/media_type_converters.cc
@@ -54,8 +54,10 @@
 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/test/data/README b/test/data/README
index 773ff84..8ab8273 100644
--- a/test/data/README
+++ b/test/data/README
@@ -196,4 +196,13 @@
 
 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/test/data/bear-ac3-only-frag.mp4 b/test/data/bear-ac3-only-frag.mp4
new file mode 100644
index 0000000..669c407
--- /dev/null
+++ b/test/data/bear-ac3-only-frag.mp4
Binary files differ
diff --git a/test/data/bear-eac3-only-frag.mp4 b/test/data/bear-eac3-only-frag.mp4
new file mode 100644
index 0000000..1530fd1
--- /dev/null
+++ b/test/data/bear-eac3-only-frag.mp4
Binary files differ