Skip to content

Commit

Permalink
Add Dolby AC3, EC3 codec support on Windows Platform
Browse files Browse the repository at this point in the history
Report ac3,ec3 codec supported on Windows when query media type with API
HTMLMediaElement.canPlayType, MediaSource.isTypeSupported and Media
Capabilities API

Bug: 1402182
Change-Id: Ic1a59a9e930417c35f0f51150e4c502f76f7fc04
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4116077
Commit-Queue: 朱思达 <zhusida@bytedance.com>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1120993}
  • Loading branch information
Jiawei Chen authored and Chromium LUCI CQ committed Mar 23, 2023
1 parent b2ffe57 commit 1ec72b6
Show file tree
Hide file tree
Showing 21 changed files with 491 additions and 95 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@ Jiangzhen Hou <houjiangzhen@360.cn>
Jianjun Zhu <jianjun.zhu@intel.com>
Jianneng Zhong <muzuiget@gmail.com>
Jiawei Shao <jiawei.shao@intel.com>
Jiawei Chen <jiawei.chen@dolby.com>
Jiaxun Wei <leuisken@gmail.com>
Jiaxun Yang <jiaxun.yang@flygoat.com>
Jidong Qin <qinjidong@qianxin.com>
Expand Down
24 changes: 15 additions & 9 deletions content/browser/media/media_canplaytype_browsertest.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2014 The Chromium Authors
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

Expand Down Expand Up @@ -74,28 +74,34 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp3) {
IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_mp4) {
#if !BUILDFLAG(USE_PROPRIETARY_CODECS)
// The function signature for JS is:
// testMp4Variants(has_proprietary_codecs:bool, platform_guarantees_hevc:bool)
ExecuteTest("testMp4Variants(false, false)");
// testMp4Variants(has_proprietary_codecs:bool,
// platform_guarantees_hevc:bool,
// platform_guarantees_ac3_eac3:bool)
ExecuteTest("testMp4Variants(false, false, false)");
#elif BUILDFLAG(IS_ANDROID)
if (!base::FeatureList::IsEnabled(media::kPlatformHEVCDecoderSupport)) {
ExecuteTest("testMp4Variants(true, false)");
ExecuteTest("testMp4Variants(true, false, false)");
return;
}
ExecuteTest("testMp4Variants(true, true)");
ExecuteTest("testMp4Variants(true, true, false)");
#elif BUILDFLAG(IS_MAC)
if (!base::FeatureList::IsEnabled(media::kPlatformHEVCDecoderSupport)) {
ExecuteTest("testMp4Variants(true, false)");
ExecuteTest("testMp4Variants(true, false, false)");
} else if (__builtin_available(macOS 11.0, *)) {
// the Mac compiler freaks out if __builtin_available is not the _only_
// condition in the if statement, which is why it's written like this.
ExecuteTest("testMp4Variants(true, true)");
ExecuteTest("testMp4Variants(true, true, false)");
} else {
ExecuteTest("testMp4Variants(true, false)");
ExecuteTest("testMp4Variants(true, false, false)");
}
#else
// Other platforms query the gpu each time to find out, so it would be
// unreliable on the bots to test for this.
ExecuteTest("testMp4Variants(true, false)");
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
ExecuteTest("testMp4Variants(true, false, true)");
#else
ExecuteTest("testMp4Variants(true, false, false)");
#endif // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
#endif
}

Expand Down
68 changes: 31 additions & 37 deletions content/test/data/media/canplaytype_test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 The Chromium Authors
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

Expand Down Expand Up @@ -743,7 +743,9 @@ function testMp3Variants() {
testMimeCodecMap(MP3_CODEC_MAP, false);
}

function testMp4Variants(has_proprietary_codecs, platform_guarantees_hevc) {
function testMp4Variants(
has_proprietary_codecs, platform_guarantees_hevc,
platform_guarantees_ac3_eac3) {
const MP4_CODEC_MAP = {
'probably': [
'audio/mp4; codecs="flac"',
Expand Down Expand Up @@ -793,35 +795,6 @@ function testMp4Variants(has_proprietary_codecs, platform_guarantees_hevc) {
'video/x-m4v; codecs="hev1.1.6.L93.B0"',
'video/x-m4v; codecs="hvc1.1.6.L93.B0, mp4a.40.5"',
'video/x-m4v; codecs="hvc1.1.6.L93.B0"',

// AC3 and EAC3 (aka Dolby Digital Plus, DD+) audio codecs. These are not
// supported by Chrome by default. 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)
'video/mp4; codecs="ac-3"',
'video/mp4; codecs="mp4a.a5"',
'video/mp4; codecs="mp4a.A5"',
'video/mp4; codecs="ec-3"',
'video/mp4; codecs="mp4a.a6"',
'video/mp4; codecs="mp4a.A6"',
'video/mp4; codecs="avc1.640028,ac-3"',
'video/mp4; codecs="avc1.640028,mp4a.a5"',
'video/mp4; codecs="avc1.640028,mp4a.A5"',
'video/mp4; codecs="avc1.640028,ec-3"',
'video/mp4; codecs="avc1.640028,mp4a.a6"',
'video/mp4; codecs="avc1.640028,mp4a.A6"',
'video/mp4; codecs="dtsc"',
'video/mp4; codecs="mp4a.a9"',
'video/mp4; codecs="mp4a.A9"',
'video/mp4; codecs="dtse"',
'video/mp4; codecs="mp4a.ac"',
'video/mp4; codecs="mp4a.AC"',
'video/mp4; codecs="dtsx"',
'video/mp4; codecs="mp4a.b2"',
'video/mp4; codecs="mp4a.B2"',
],
};

Expand All @@ -841,9 +814,35 @@ function testMp4Variants(has_proprietary_codecs, platform_guarantees_hevc) {
])
}

// 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)
let ac3_eac3_codecs = [
'video/mp4; codecs="ac-3"',
'video/mp4; codecs="mp4a.a5"',
'video/mp4; codecs="mp4a.A5"',
'video/mp4; codecs="ec-3"',
'video/mp4; codecs="mp4a.a6"',
'video/mp4; codecs="mp4a.A6"',
'video/mp4; codecs="avc1.640028,ac-3"',
'video/mp4; codecs="avc1.640028,mp4a.a5"',
'video/mp4; codecs="avc1.640028,mp4a.A5"',
'video/mp4; codecs="avc1.640028,ec-3"',
'video/mp4; codecs="avc1.640028,mp4a.a6"',
'video/mp4; codecs="avc1.640028,mp4a.A6"',
];
if (platform_guarantees_ac3_eac3) {
MP4_CODEC_MAP['probably'] =
MP4_CODEC_MAP['probably'].concat(ac3_eac3_codecs);
} else {
MP4_CODEC_MAP['not'] = MP4_CODEC_MAP['not'].concat(ac3_eac3_codecs);
}

const MP4A_BAD_CODEC_LIST = [
'ac-3',
'avc1, mp4a.40',
'avc1, mp4a',
'avc1.4D401E',
Expand All @@ -852,15 +851,10 @@ function testMp4Variants(has_proprietary_codecs, platform_guarantees_hevc) {
'avc3, mp4a',
'avc3.64001F',
'avc3',
'ec-3',
'hev1.1.6.L93.B0,mp4a.40.5',
'hev1.1.6.L93.B0',
'hvc1.1.6.L93.B0,mp4a.40.5',
'hvc1.1.6.L93.B0',
'mp4a.A5',
'mp4a.A6',
'mp4a.a5',
'mp4a.a6',
'vp09.00.10.08',
];

Expand Down
9 changes: 7 additions & 2 deletions media/base/supported_types.cc
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,7 @@ bool IsDefaultSupportedAudioType(const AudioType& type) {
case AudioCodec::kAMR_NB:
case AudioCodec::kAMR_WB:
case AudioCodec::kGSM_MS:
case AudioCodec::kEAC3:
case AudioCodec::kALAC:
case AudioCodec::kAC3:
case AudioCodec::kMpegHAudio:
case AudioCodec::kUnknown:
return false;
Expand All @@ -383,6 +381,13 @@ bool IsDefaultSupportedAudioType(const AudioType& type) {
return true;
#else
return false;
#endif
case AudioCodec::kAC3:
case AudioCodec::kEAC3:
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
return true;
#else
return false;
#endif
}
}
Expand Down
4 changes: 3 additions & 1 deletion media/filters/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,13 @@ source_set("filters") {
"win/media_foundation_utils.h",
]
deps += [ "//media/base/win:media_foundation_util" ]
if (enable_platform_dts_audio) {
if (enable_platform_dts_audio || enable_platform_ac3_eac3_audio) {
sources += [
"win/media_foundation_audio_decoder.cc",
"win/media_foundation_audio_decoder.h",
]
}
if (enable_platform_dts_audio) {
ldflags = [
"/DELAYLOAD:packages/Microsoft.VCRTForwarders.140.1.0.6/runtimes/win10-x64/native/release/concrt140_app.dll",
"/DELAYLOAD:packages/Microsoft.VCRTForwarders.140.1.0.6/runtimes/win10-x64/native/release/msvcp140_1_app.dll",
Expand Down
103 changes: 78 additions & 25 deletions media/filters/win/media_foundation_audio_decoder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,24 @@ void MediaFoundationAudioDecoder::Initialize(const AudioDecoderConfig& config,
InitCB init_cb,
const OutputCB& output_cb,
const WaitingCB& waiting_cb) {
switch (config.codec()) {
#if BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
if (config.codec() != AudioCodec::kDTS &&
config.codec() != AudioCodec::kDTSE &&
config.codec() != AudioCodec::kDTSXP2) {
std::move(init_cb).Run(
DecoderStatus(DecoderStatus::Codes::kUnsupportedCodec,
"MFT Codec does not support DTS content"));
return;
}
#else // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
#error "MediaFoundationAudioDecoder requires proprietary codecs and DTS audio"
case AudioCodec::kDTS:
case AudioCodec::kDTSE:
case AudioCodec::kDTSXP2:
break;
#endif // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
case AudioCodec::kAC3:
case AudioCodec::kEAC3:
break;
#endif // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
default:
std::move(init_cb).Run(
DecoderStatus(DecoderStatus::Codes::kUnsupportedCodec,
"Codec is not supported by MFT"));
return;
}

// FIXME: MFT will need to be signed by a Microsoft Certificate
// to support a secured chain of custody on Windows.
Expand All @@ -118,17 +124,9 @@ void MediaFoundationAudioDecoder::Initialize(const AudioDecoderConfig& config,
config_ = config;
output_cb_ = output_cb;

#if BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
if (config.codec() == AudioCodec::kDTS ||
config.codec() == AudioCodec::kDTSE ||
config.codec() == AudioCodec::kDTSXP2) {
std::move(init_cb).Run(
CreateDecoder()
? DecoderStatus(OkStatus())
: DecoderStatus(DecoderStatus::Codes::kUnsupportedCodec,
"MFT Codec does not support DTS content"));
}
#endif // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
std::move(init_cb).Run(
CreateDecoder() ? DecoderStatus(OkStatus())
: DecoderStatus(DecoderStatus::Codes::kUnsupportedCodec));
}

void MediaFoundationAudioDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
Expand Down Expand Up @@ -242,8 +240,16 @@ bool MediaFoundationAudioDecoder::CreateDecoder() {
type_info = {MFMediaType_Audio, MFAudioFormat_DTS_RAW};
break;
#endif // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
case AudioCodec::kAC3:
type_info = {MFMediaType_Audio, MFAudioFormat_Dolby_AC3};
break;
case AudioCodec::kEAC3:
type_info = {MFMediaType_Audio, MFAudioFormat_Dolby_DDPlus};
break;
#endif // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
default:
type_info = {MFMediaType_Audio, MFAudioFormat_Base};
return false;
}

base::win::ScopedCoMem<IMFActivate*> acts;
Expand Down Expand Up @@ -317,6 +323,40 @@ bool MediaFoundationAudioDecoder::ConfigureOutput() {
}
}
#endif // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)

#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
if (config_.codec() == AudioCodec::kAC3 ||
config_.codec() == AudioCodec::kEAC3) {
if (out_subtype == MFAudioFormat_Float) {
WAVEFORMATEX* wave_format;
UINT32 wave_format_size;
RETURN_ON_HR_FAILURE(
MFCreateWaveFormatExFromMFMediaType(output_type.Get(), &wave_format,
&wave_format_size),
"Failed to get waveformat for media type", false);
if (config_.channels() == wave_format->nChannels &&
config_.samples_per_second() ==
static_cast<int>(wave_format->nSamplesPerSec) &&
wave_format->nBlockAlign ==
wave_format->nChannels * sizeof(float)) {
RETURN_ON_HR_FAILURE(decoder_->SetOutputType(0, output_type.Get(), 0),
"Failed to set output type IMFTransform", false);

MFT_OUTPUT_STREAM_INFO info = {0};
RETURN_ON_HR_FAILURE(decoder_->GetOutputStreamInfo(0, &info),
"Failed to get output stream info", false);

output_sample_ =
CreateEmptySampleWithBuffer(info.cbSize, info.cbAlignment);
RETURN_ON_FAILURE(!!output_sample_, "Failed to create staging sample",
false);

channel_count_ = wave_format->nChannels;
}
CoTaskMemFree(wave_format);
}
}
#endif // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
if (!output_sample_) {
output_type.Reset();
continue;
Expand Down Expand Up @@ -424,12 +464,13 @@ MediaFoundationAudioDecoder::PumpOutput(PumpState pump_state) {
if (!pool_)
pool_ = base::MakeRefCounted<AudioBufferMemoryPool>();

auto audio_buffer =
scoped_refptr<AudioBuffer> audio_buffer;

#if BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
audio_buffer =
AudioBuffer::CreateBuffer(kSampleFormatF32, channel_layout_,
channel_count_, sample_rate_, frames, pool_);
audio_buffer->set_timestamp(timestamp_helper_->GetTimestamp());

#if BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
// DTS Sound Unbound MFT v1.3.0 outputs 24-bit PCM samples, and will
// be converted to 32-bit float
if (config_.codec() == AudioCodec::kDTS ||
Expand All @@ -449,8 +490,20 @@ MediaFoundationAudioDecoder::PumpOutput(PumpState pump_state) {
}
#endif // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)

#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
// AC3,EAC3 MFT is configured to output 32-bit float always
if (config_.codec() == AudioCodec::kAC3 ||
config_.codec() == AudioCodec::kEAC3) {
audio_buffer = AudioBuffer::CopyFrom(
kSampleFormatF32, channel_layout_, channel_count_, sample_rate_, frames,
&destination, timestamp_helper_->GetTimestamp(), pool_);
}
#endif // BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)

timestamp_helper_->AddFrames(frames);

// Important to reset length to 0 since we reuse a same output buffer
output_buffer->SetCurrentLength(0);
output_buffer->Unlock();

output_cb_.Run(std::move(audio_buffer));
Expand Down
9 changes: 9 additions & 0 deletions media/formats/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,15 @@ source_set("formats") {
]
}

if (enable_platform_ac3_eac3_audio) {
sources += [
"mp4/ac3.cc",
"mp4/ac3.h",
"mp4/eac3.cc",
"mp4/eac3.h",
]
}

if (proprietary_codecs && enable_mse_mpeg2ts_stream_parser) {
deps += [ "//ui/gfx/geometry" ]
sources += [
Expand Down
Loading

0 comments on commit 1ec72b6

Please sign in to comment.