0

Add passthrough support for DTS Audio codec on Android

DTS Audio within MP4 files will be played back using the passthrough interface.

Bug: 1270685
Change-Id: I3be2a1ffde093700870cdc8235b812b49104afd3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3423439
Reviewed-by: Mark Foltz <mfoltz@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Yuchen Liu <yucliu@chromium.org>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#969597}
This commit is contained in:
Roy Funderburk
2022-02-10 20:05:30 +00:00
committed by Chromium LUCI CQ
parent c742d2529d
commit 7868b7a3d2
16 changed files with 228 additions and 44 deletions

@ -36,10 +36,7 @@ bool IsChannelLayoutSupported(AudioConfig config) {
// Codecs that cannot be decoded on the device and must be passed through.
constexpr media::AudioCodec kPassthroughCodecs[] = {
kCodecEAC3,
kCodecAC3,
kCodecDTS,
kCodecMpegHAudio,
kCodecEAC3, kCodecAC3, kCodecDTS, kCodecDTSXP2, kCodecMpegHAudio,
};
} // namespace

@ -111,9 +111,6 @@ std::string FormatToString(media::AudioParameters::Format format) {
case media::AudioParameters::AUDIO_FAKE:
return "fake";
}
NOTREACHED();
return "unknown";
}
const char kAudioLogStatusKey[] = "status";

@ -45,17 +45,29 @@ static const base::FilePath::CharType kAaudioLib[] =
namespace media {
namespace {
void AddDefaultDevice(AudioDeviceNames* device_names) {
DCHECK(device_names->empty());
device_names->push_front(AudioDeviceName::CreateDefault());
}
// Maximum number of output streams that can be open simultaneously.
const int kMaxOutputStreams = 10;
const int kDefaultInputBufferSize = 1024;
const int kDefaultOutputBufferSize = 2048;
void AddDefaultDevice(AudioDeviceNames* device_names) {
DCHECK(device_names->empty());
device_names->push_front(AudioDeviceName::CreateDefault());
}
// Returns whether the currently connected device is an audio sink.
bool IsAudioSinkConnected() {
return Java_AudioManagerAndroid_isAudioSinkConnected(
base::android::AttachCurrentThread());
}
// Return a bit mask of AudioParameters::Format enum values sink device supports
int GetSinkAudioEncodingFormats() {
JNIEnv* env = AttachCurrentThread();
return Java_AudioManagerAndroid_getAudioEncodingFormatsSupported(env);
}
} // namespace
static bool InitAAudio() {
@ -387,6 +399,12 @@ AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters(
if (user_buffer_size)
buffer_size = user_buffer_size;
// Check if device supports additional audio encodings.
if (IsAudioSinkConnected()) {
return GetAudioFormatsSupportedBySinkDevice(
output_device_id, channel_layout, sample_rate, buffer_size);
}
return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
sample_rate, buffer_size);
}
@ -459,6 +477,22 @@ int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate,
sample_rate, channels));
}
// Returns encoding bitstream formats supported by Sink device. Returns
// AudioParameters structure.
AudioParameters AudioManagerAndroid::GetAudioFormatsSupportedBySinkDevice(
const std::string& output_device_id,
ChannelLayout channel_layout,
int sample_rate,
int buffer_size) {
int formats = GetSinkAudioEncodingFormats();
DVLOG(1) << __func__ << ": IsAudioSinkConnected()==true, output_device_id="
<< output_device_id << ", Supported Encodings=" << formats;
return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
sample_rate, buffer_size,
AudioParameters::HardwareCapabilities(formats));
}
void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) {
DCHECK(GetTaskRunner()->BelongsToCurrentThread());
for (OutputStreams::iterator it = streams_.begin();

@ -108,6 +108,11 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
bool IsAudioLowLatencySupported();
int GetAudioLowLatencyOutputFrameSize();
int GetOptimalOutputFrameSize(int sample_rate, int channels);
AudioParameters GetAudioFormatsSupportedBySinkDevice(
const std::string& output_device_id,
ChannelLayout channel_layout,
int sample_rate,
int buffer_size);
void DoSetMuteOnAudioThread(bool muted);
void DoSetVolumeOnAudioThread(double volume);

@ -24,9 +24,12 @@ namespace media {
// Android audio format. For more information, please see:
// https://developer.android.com/reference/android/media/AudioFormat.html
enum {
kEncodingPcm16bit = 2, // ENCODING_PCM_16BIT
kEncodingAc3 = 5, // ENCODING_AC3
kEncodingEac3 = 6, // ENCODING_E_AC3
kEncodingPcm16bit = 2, // ENCODING_PCM_16BIT
kEncodingAc3 = 5, // ENCODING_AC3
kEncodingEac3 = 6, // ENCODING_E_AC3
kEncodingDts = 7, // ENCODING_DTS
kEncodingDtshd = 8, // ENCODING_DTS_HD
kEncodingIec61937 = 13, // ENCODING_IEC61937
};
AudioTrackOutputStream::AudioTrackOutputStream(AudioManagerBase* manager,
@ -50,12 +53,27 @@ bool AudioTrackOutputStream::Open() {
int format = kEncodingPcm16bit;
if (params_.IsBitstreamFormat()) {
if (params_.format() == AudioParameters::AUDIO_BITSTREAM_AC3) {
format = kEncodingAc3;
} else if (params_.format() == AudioParameters::AUDIO_BITSTREAM_EAC3) {
format = kEncodingEac3;
} else {
NOTREACHED();
switch (params_.format()) {
case AudioParameters::AUDIO_BITSTREAM_AC3:
format = kEncodingAc3;
break;
case AudioParameters::AUDIO_BITSTREAM_EAC3:
format = kEncodingEac3;
break;
case AudioParameters::AUDIO_BITSTREAM_DTS:
format = kEncodingDts;
break;
case AudioParameters::AUDIO_BITSTREAM_DTS_HD:
format = kEncodingDtshd;
break;
case AudioParameters::AUDIO_BITSTREAM_IEC61937:
format = kEncodingIec61937;
break;
case AudioParameters::AUDIO_FAKE:
case AudioParameters::AUDIO_PCM_LINEAR:
case AudioParameters::AUDIO_PCM_LOW_LATENCY:
NOTREACHED();
break;
}
}

@ -477,6 +477,7 @@ source_set("video_facing") {
if (is_android) {
java_cpp_enum("java_enums") {
sources = [
"audio_parameters.h",
"container_names.h",
"encryption_scheme.h",
"video_codecs.h",

@ -17,6 +17,7 @@ import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
@ -1238,6 +1239,86 @@ class AudioManagerAndroid {
mUsbAudioReceiver = null;
}
/** Return the AudioDeviceInfo array as reported by the Android OS. */
private static AudioDeviceInfo[] getAudioDeviceInfo() {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
return new AudioDeviceInfo[0];
}
AudioManager audioManager =
(AudioManager) ContextUtils.getApplicationContext().getSystemService(
Context.AUDIO_SERVICE);
return audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
}
/** Returns whether an audio sink device is connected. */
@CalledByNative
private static boolean isAudioSinkConnected() {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
return false;
}
for (AudioDeviceInfo deviceInfo : getAudioDeviceInfo()) {
if (deviceInfo.isSink()) {
return true;
}
}
return false;
}
/**
* Returns a bit mask of Audio Formats (C++ AudioParameters::Format enum)
* supported by all of the sink devices.
*/
@CalledByNative
private static int getAudioEncodingFormatsSupported() {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
return 0;
}
int intersection_mask = 0; // intersection of multiple device encoding arrays
boolean first = true;
for (AudioDeviceInfo deviceInfo : getAudioDeviceInfo()) {
int[] encodings = deviceInfo.getEncodings();
if (encodings.length > 0) {
int mask = 0; // bit mask for a single device
// Map AudioFormat values to C++ media/base/audio_parameters.h Format enum
for (int i : encodings) {
switch (i) {
case AudioFormat.ENCODING_PCM_16BIT:
mask |= AudioEncodingFormat.PCM_LINEAR;
break;
case AudioFormat.ENCODING_AC3:
mask |= AudioEncodingFormat.BITSTREAM_AC3;
break;
case AudioFormat.ENCODING_E_AC3:
mask |= AudioEncodingFormat.BITSTREAM_EAC3;
break;
case AudioFormat.ENCODING_DTS:
mask |= AudioEncodingFormat.BITSTREAM_DTS;
break;
case AudioFormat.ENCODING_DTS_HD:
mask |= AudioEncodingFormat.BITSTREAM_DTS_HD;
break;
case AudioFormat.ENCODING_IEC61937:
mask |= AudioEncodingFormat.BITSTREAM_IEC61937;
break;
}
}
// Require all devices to support a format
if (first) {
first = false;
intersection_mask = mask;
} else {
intersection_mask &= mask;
}
}
}
return intersection_mask;
}
@NativeMethods
interface Natives {
void setMute(long nativeAudioManagerAndroid, AudioManagerAndroid caller, boolean muted);

@ -70,6 +70,10 @@ AudioCodec StringToAudioCodec(const std::string& codec_id) {
return AudioCodec::kAC3;
if (codec_id == "ec-3" || codec_id == "mp4a.A6" || codec_id == "mp4a.a6")
return AudioCodec::kEAC3;
if (codec_id == "dtsc")
return AudioCodec::kDTS;
if (codec_id == "dtsx")
return AudioCodec::kDTSXP2;
if (codec_id == "mp3" || codec_id == "mp4a.69" || codec_id == "mp4a.6B")
return AudioCodec::kMP3;
if (codec_id == "alac")

@ -29,7 +29,6 @@ const char* FormatToString(AudioParameters::Format format) {
case AudioParameters::AUDIO_FAKE:
return "FAKE";
}
return "INVALID";
}
base::CheckedNumeric<uint32_t> ComputeAudioInputBufferSizeChecked(
@ -146,11 +145,12 @@ std::string AudioParameters::AsHumanReadableString() const {
<< ", frames_per_buffer: " << frames_per_buffer()
<< ", effects: " << effects()
<< ", mic_positions: " << PointsToString(mic_positions_);
if (hardware_capabilities_) {
s << ", hw_cap.min_frames_per_buffer: "
if (hardware_capabilities_.has_value()) {
s << ", hw_capabilities: min_frames_per_buffer: "
<< hardware_capabilities_->min_frames_per_buffer
<< ", hw_cap.max_frames_per_buffer: "
<< hardware_capabilities_->max_frames_per_buffer;
<< ", max_frames_per_buffer: "
<< hardware_capabilities_->max_frames_per_buffer
<< ", bitstream_formats:" << hardware_capabilities_->bitstream_formats;
}
return s.str();
}

@ -126,16 +126,20 @@ class MEDIA_SHMEM_EXPORT AudioParameters {
public:
// TODO(miu): Rename this enum to something that correctly reflects its
// semantics, such as "TransportScheme."
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.media
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: AudioEncodingFormat
// GENERATED_JAVA_PREFIX_TO_STRIP: AUDIO_
enum Format {
AUDIO_PCM_LINEAR = 0, // PCM is 'raw' amplitude samples.
AUDIO_PCM_LOW_LATENCY, // Linear PCM, low latency requested.
AUDIO_BITSTREAM_AC3, // Compressed AC3 bitstream.
AUDIO_BITSTREAM_EAC3, // Compressed E-AC3 bitstream.
AUDIO_BITSTREAM_DTS, // Compressed DTS bitstream.
AUDIO_BITSTREAM_DTS_HD, // Compressed DTS-HD bitstream.
AUDIO_BITSTREAM_IEC61937, // Compressed IEC61937 bitstream.
AUDIO_FAKE, // Creates a fake AudioOutputStream object.
AUDIO_FORMAT_LAST = AUDIO_FAKE, // Only used for validation of format.
AUDIO_FAKE = 0x00, // Creates a fake AudioOutputStream object
AUDIO_PCM_LINEAR = 0x01, // PCM is 'raw' amplitude samples.
AUDIO_PCM_LOW_LATENCY = 0x02, // Linear PCM, low latency requested.
AUDIO_BITSTREAM_AC3 = 0x04, // Compressed AC3 bitstream.
AUDIO_BITSTREAM_EAC3 = 0x08, // Compressed E-AC3 bitstream.
AUDIO_BITSTREAM_DTS = 0x10, // Compressed DTS bitstream.
AUDIO_BITSTREAM_DTS_HD = 0x20, // Compressed DTS-HD bitstream.
AUDIO_BITSTREAM_IEC61937 = 0x40, // Compressed IEC61937 bitstream.
AUDIO_FORMAT_LAST =
AUDIO_BITSTREAM_IEC61937, // Only used for validation of format.
};
enum {
@ -171,9 +175,16 @@ class MEDIA_SHMEM_EXPORT AudioParameters {
struct HardwareCapabilities {
HardwareCapabilities(int min_frames_per_buffer, int max_frames_per_buffer)
: min_frames_per_buffer(min_frames_per_buffer),
max_frames_per_buffer(max_frames_per_buffer) {}
max_frames_per_buffer(max_frames_per_buffer),
bitstream_formats(0) {}
HardwareCapabilities(int bitstream_formats)
: min_frames_per_buffer(0),
max_frames_per_buffer(0),
bitstream_formats(bitstream_formats) {}
HardwareCapabilities()
: min_frames_per_buffer(0), max_frames_per_buffer(0) {}
: min_frames_per_buffer(0),
max_frames_per_buffer(0),
bitstream_formats(0) {}
// Minimum and maximum buffer sizes supported by the audio hardware. Opening
// a device with frames_per_buffer set to a value between min and max should
@ -182,6 +193,7 @@ class MEDIA_SHMEM_EXPORT AudioParameters {
// Either value can be 0 and means that the min or max is not known.
int min_frames_per_buffer;
int max_frames_per_buffer;
int bitstream_formats;
};
AudioParameters();
@ -261,6 +273,11 @@ class MEDIA_SHMEM_EXPORT AudioParameters {
return hardware_capabilities_;
}
void set_hardware_capabilities(
const absl::optional<HardwareCapabilities>& hwc) {
hardware_capabilities_ = hwc;
}
void set_effects(int effects) { effects_ = effects; }
int effects() const { return effects_; }

@ -80,19 +80,24 @@ void ParamTraits<AudioParameters::HardwareCapabilities>::Write(
const param_type& p) {
WriteParam(m, p.min_frames_per_buffer);
WriteParam(m, p.max_frames_per_buffer);
WriteParam(m, p.bitstream_formats);
}
bool ParamTraits<AudioParameters::HardwareCapabilities>::Read(
const base::Pickle* m,
base::PickleIterator* iter,
param_type* r) {
int max_frames_per_buffer, min_frames_per_buffer;
int bitstream_formats;
int max_frames_per_buffer;
int min_frames_per_buffer;
if (!ReadParam(m, iter, &min_frames_per_buffer) ||
!ReadParam(m, iter, &max_frames_per_buffer)) {
!ReadParam(m, iter, &max_frames_per_buffer) ||
!ReadParam(m, iter, &bitstream_formats)) {
return false;
}
r->min_frames_per_buffer = min_frames_per_buffer;
r->max_frames_per_buffer = max_frames_per_buffer;
r->bitstream_formats = bitstream_formats;
return true;
}

@ -20,6 +20,8 @@
#include "media/base/status.h"
#include "media/base/timestamp_constants.h"
#include "media/formats/ac3/ac3_util.h"
#include "media/formats/dts/dts_util.h"
#include "media/media_buildflags.h"
namespace media {
@ -77,6 +79,10 @@ void MediaCodecAudioDecoder::Initialize(const AudioDecoderConfig& config,
sample_format_ = kSampleFormatEac3;
else if (config.codec() == AudioCodec::kMpegHAudio)
sample_format_ = kSampleFormatMpegHAudio;
else if (config.codec() == AudioCodec::kDTS)
sample_format_ = kSampleFormatDts;
else if (config.codec() == AudioCodec::kDTSXP2)
sample_format_ = kSampleFormatDtsxP2;
if (state_ == STATE_ERROR) {
DVLOG(1) << "Decoder is in error state.";
@ -416,6 +422,10 @@ bool MediaCodecAudioDecoder::OnDecodedFrame(
} else if (config_.codec() == AudioCodec::kEAC3) {
frame_count = Ac3Util::ParseTotalEac3SampleCount(
audio_buffer->channel_data()[0], out.size);
} else if (config_.codec() == AudioCodec::kDTS) {
frame_count = media::dts::ParseTotalSampleCount(
audio_buffer->channel_data()[0], out.size, AudioCodec::kDTS);
DVLOG(2) << ": DTS Frame Count = " << frame_count;
} else {
NOTREACHED() << "Unsupported passthrough format.";
}

@ -115,6 +115,8 @@ class AudioRendererAlgorithmTest : public testing::Test {
format = media::AudioParameters::AUDIO_BITSTREAM_AC3;
else if (sample_format == kSampleFormatEac3)
format = media::AudioParameters::AUDIO_BITSTREAM_EAC3;
else if (sample_format == kSampleFormatDts)
format = media::AudioParameters::AUDIO_BITSTREAM_DTS;
AudioParameters params(format, channel_layout, samples_per_second,
frames_per_buffer);
@ -160,6 +162,13 @@ class AudioRendererAlgorithmTest : public testing::Test {
ChannelLayoutToChannelCount(channel_layout_), samples_per_second_,
1, 1, frame_size, kNoTimestamp);
break;
case kSampleFormatDts:
case kSampleFormatDtsxP2:
buffer = MakeBitstreamAudioBuffer(
sample_format_, channel_layout_,
ChannelLayoutToChannelCount(channel_layout_), samples_per_second_,
1, 1, frame_size, kFrameSize, kNoTimestamp);
break;
default:
NOTREACHED() << "Unrecognized format " << sample_format_;
}

@ -24,6 +24,10 @@ source_set("formats") {
"common/offset_byte_queue.h",
"common/opus_constants.cc",
"common/opus_constants.h",
"dts/dts_stream_parser.cc",
"dts/dts_stream_parser.h",
"dts/dts_util.cc",
"dts/dts_util.h",
"mp4/bitstream_converter.cc",
"mp4/bitstream_converter.h",
"mp4/box_definitions.cc",
@ -126,10 +130,6 @@ source_set("formats") {
if (proprietary_codecs && enable_platform_dts_audio) {
sources += [
"dts/dts_stream_parser.cc",
"dts/dts_stream_parser.h",
"dts/dts_util.cc",
"dts/dts_util.h",
"mp4/dts.cc",
"mp4/dts.h",
"mp4/dtsx.cc",
@ -240,6 +240,7 @@ source_set("unit_tests") {
sources = [
"ac3/ac3_util_unittest.cc",
"common/offset_byte_queue_unittest.cc",
"dts/dts_util_unittest.cc",
"mpeg/mpeg1_audio_stream_parser_unittest.cc",
"webm/webm_cluster_parser_unittest.cc",
"webm/webm_content_encodings_client_unittest.cc",
@ -276,7 +277,6 @@ source_set("unit_tests") {
if (enable_platform_dts_audio) {
sources += [
"dts/dts_util_unittest.cc",
"mp4/dts_unittest.cc",
"mp4/dtsx_unittest.cc",
]

@ -428,6 +428,8 @@ RemotingCompatibility RendererController::GetAudioCompatibility() const {
case AudioCodec::kPCM_ALAW:
case AudioCodec::kALAC:
case AudioCodec::kAC3:
case AudioCodec::kDTS:
case AudioCodec::kDTSXP2:
compatible =
HasAudioCapability(RemotingSinkAudioCapability::CODEC_BASELINE_SET);
break;
@ -666,6 +668,8 @@ bool RendererController::IsAudioRemotePlaybackSupported() const {
case AudioCodec::kPCM_ALAW:
case AudioCodec::kALAC:
case AudioCodec::kAC3:
case AudioCodec::kDTS:
case AudioCodec::kDTSXP2:
return true;
default:
return false;

@ -476,6 +476,8 @@ void AudioRendererImpl::OnDeviceInfoReceived(
format = AudioParameters::AUDIO_BITSTREAM_AC3;
} else if (codec == AudioCodec::kEAC3) {
format = AudioParameters::AUDIO_BITSTREAM_EAC3;
} else if (codec == AudioCodec::kDTS) {
format = AudioParameters::AUDIO_BITSTREAM_DTS;
} else {
NOTREACHED();
}