Merge pull request #335 from cedricxperi:dts-direct-passthrough-support

PiperOrigin-RevId: 535255453
(cherry picked from commit 6a9167097e78341b3bd3ad8309ee79ae35ecfd13)
parent 95fdd3f7
......@@ -189,6 +189,7 @@ public final class C {
ENCODING_DTS_HD,
ENCODING_DOLBY_TRUEHD,
ENCODING_OPUS,
ENCODING_DTS_UHD_P2,
})
public @interface Encoding {}
......@@ -252,6 +253,8 @@ public final class C {
public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS;
/** See {@link AudioFormat#ENCODING_DTS_HD}. */
public static final int ENCODING_DTS_HD = AudioFormat.ENCODING_DTS_HD;
// TODO(internal b/283949283): Use AudioFormat.ENCODING_DTS_UHD_P2 when Android 14 is released.
public static final int ENCODING_DTS_UHD_P2 = 0x0000001e;
/** See {@link AudioFormat#ENCODING_DOLBY_TRUEHD}. */
public static final int ENCODING_DOLBY_TRUEHD = AudioFormat.ENCODING_DOLBY_TRUEHD;
/** See {@link AudioFormat#ENCODING_OPUS}. */
......
......@@ -575,6 +575,10 @@ public final class MimeTypes {
return C.ENCODING_DTS;
case MimeTypes.AUDIO_DTS_HD:
return C.ENCODING_DTS_HD;
case MimeTypes.AUDIO_DTS_EXPRESS:
return C.ENCODING_DTS_HD;
case MimeTypes.AUDIO_DTS_X:
return C.ENCODING_DTS_UHD_P2;
case MimeTypes.AUDIO_TRUEHD:
return C.ENCODING_DOLBY_TRUEHD;
case MimeTypes.AUDIO_OPUS:
......
......@@ -1801,6 +1801,14 @@ public final class Util {
return AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
case 8:
return AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
case 10:
if (Util.SDK_INT >= 32) {
return AudioFormat.CHANNEL_OUT_5POINT1POINT4;
} else {
// Before API 32, height channel masks are not available. For those 10-channel streams
// supported on the audio output devices (e.g. DTS:X P2), we use 7.1-surround instead.
return AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
}
case 12:
return AudioFormat.CHANNEL_OUT_7POINT1POINT4;
default:
......
......@@ -46,21 +46,19 @@ import java.util.Arrays;
/** Represents the set of audio formats that a device is capable of playing. */
public final class AudioCapabilities {
private static final int DEFAULT_MAX_CHANNEL_COUNT = 8;
// TODO(internal b/283945513): Have separate default max channel counts in `AudioCapabilities`
// for PCM and compressed audio.
private static final int DEFAULT_MAX_CHANNEL_COUNT = 10;
@VisibleForTesting /* package */ static final int DEFAULT_SAMPLE_RATE_HZ = 48_000;
/** The minimum audio capabilities supported by all devices. */
public static final AudioCapabilities DEFAULT_AUDIO_CAPABILITIES =
new AudioCapabilities(new int[] {AudioFormat.ENCODING_PCM_16BIT}, DEFAULT_MAX_CHANNEL_COUNT);
/** Audio capabilities when the device specifies external surround sound. */
@SuppressWarnings("InlinedApi")
private static final AudioCapabilities EXTERNAL_SURROUND_SOUND_CAPABILITIES =
new AudioCapabilities(
new int[] {
AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_AC3, AudioFormat.ENCODING_E_AC3
},
DEFAULT_MAX_CHANNEL_COUNT);
/** Encodings supported when the device specifies external surround sound. */
private static final ImmutableList<Integer> EXTERNAL_SURROUND_SOUND_ENCODINGS =
ImmutableList.of(
AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_AC3, AudioFormat.ENCODING_E_AC3);
/**
* All surround sound encodings that a device may be capable of playing mapped to a maximum
......@@ -71,6 +69,7 @@ public final class AudioCapabilities {
.put(C.ENCODING_AC3, 6)
.put(C.ENCODING_AC4, 6)
.put(C.ENCODING_DTS, 6)
.put(C.ENCODING_DTS_UHD_P2, 10)
.put(C.ENCODING_E_AC3_JOC, 6)
.put(C.ENCODING_E_AC3, 8)
.put(C.ENCODING_DTS_HD, 8)
......@@ -101,25 +100,39 @@ public final class AudioCapabilities {
if (Util.SDK_INT >= 23 && Api23.isBluetoothConnected(context)) {
return DEFAULT_AUDIO_CAPABILITIES;
}
ImmutableSet.Builder<Integer> supportedEncodings = new ImmutableSet.Builder<>();
if (deviceMaySetExternalSurroundSoundGlobalSetting()
&& Global.getInt(context.getContentResolver(), EXTERNAL_SURROUND_SOUND_KEY, 0) == 1) {
return EXTERNAL_SURROUND_SOUND_CAPABILITIES;
supportedEncodings.addAll(EXTERNAL_SURROUND_SOUND_ENCODINGS);
}
// AudioTrack.isDirectPlaybackSupported returns true for encodings that are supported for audio
// offload, as well as for encodings we want to list for passthrough mode. Therefore we only use
// it on TV and automotive devices, which generally shouldn't support audio offload for surround
// encodings.
if (Util.SDK_INT >= 29 && (Util.isTv(context) || Util.isAutomotive(context))) {
supportedEncodings.addAll(Api29.getDirectPlaybackSupportedEncodings());
return new AudioCapabilities(
Api29.getDirectPlaybackSupportedEncodings(), DEFAULT_MAX_CHANNEL_COUNT);
Ints.toArray(supportedEncodings.build()), DEFAULT_MAX_CHANNEL_COUNT);
}
if (intent == null || intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) == 0) {
return DEFAULT_AUDIO_CAPABILITIES;
if (intent != null && intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) == 1) {
@Nullable int[] encodingsFromExtra = intent.getIntArrayExtra(AudioManager.EXTRA_ENCODINGS);
if (encodingsFromExtra != null) {
supportedEncodings.addAll(Ints.asList(encodingsFromExtra));
}
return new AudioCapabilities(
Ints.toArray(supportedEncodings.build()),
intent.getIntExtra(
AudioManager.EXTRA_MAX_CHANNEL_COUNT, /* defaultValue= */ DEFAULT_MAX_CHANNEL_COUNT));
}
return new AudioCapabilities(
intent.getIntArrayExtra(AudioManager.EXTRA_ENCODINGS),
intent.getIntExtra(
AudioManager.EXTRA_MAX_CHANNEL_COUNT, /* defaultValue= */ DEFAULT_MAX_CHANNEL_COUNT));
ImmutableSet<Integer> supportedEncodingsSet = supportedEncodings.build();
if (!supportedEncodingsSet.isEmpty()) {
return new AudioCapabilities(
Ints.toArray(supportedEncodingsSet), /* maxChannelCount= */ DEFAULT_MAX_CHANNEL_COUNT);
}
return DEFAULT_AUDIO_CAPABILITIES;
}
/**
......@@ -201,7 +214,8 @@ public final class AudioCapabilities {
if (encoding == C.ENCODING_E_AC3_JOC && !supportsEncoding(C.ENCODING_E_AC3_JOC)) {
// E-AC3 receivers support E-AC3 JOC streams (but decode only the base layer).
encoding = C.ENCODING_E_AC3;
} else if (encoding == C.ENCODING_DTS_HD && !supportsEncoding(C.ENCODING_DTS_HD)) {
} else if ((encoding == C.ENCODING_DTS_HD && !supportsEncoding(C.ENCODING_DTS_HD))
|| (encoding == C.ENCODING_DTS_UHD_P2 && !supportsEncoding(C.ENCODING_DTS_UHD_P2))) {
// DTS receivers support DTS-HD streams (but decode only the core layer).
encoding = C.ENCODING_DTS;
}
......@@ -218,7 +232,13 @@ public final class AudioCapabilities {
channelCount = getMaxSupportedChannelCountForPassthrough(encoding, sampleRate);
} else {
channelCount = format.channelCount;
if (channelCount > maxChannelCount) {
// Some DTS:X TVs reports ACTION_HDMI_AUDIO_PLUG.EXTRA_MAX_CHANNEL_COUNT as 8
// instead of 10. See https://github.com/androidx/media/issues/396
if (format.sampleMimeType.equals(MimeTypes.AUDIO_DTS_X)) {
if (channelCount > 10) {
return null;
}
} else if (channelCount > maxChannelCount) {
return null;
}
}
......@@ -353,9 +373,13 @@ public final class AudioCapabilities {
private Api29() {}
@DoNotInline
public static int[] getDirectPlaybackSupportedEncodings() {
public static ImmutableList<Integer> getDirectPlaybackSupportedEncodings() {
ImmutableList.Builder<Integer> supportedEncodingsListBuilder = ImmutableList.builder();
for (int encoding : ALL_SURROUND_ENCODINGS_AND_MAX_CHANNELS.keySet()) {
// AudioFormat.ENCODING_DTS_UHD_P2 is supported from API 34.
if (Util.SDK_INT < 34 && encoding == C.ENCODING_DTS_UHD_P2) {
continue;
}
if (AudioTrack.isDirectPlaybackSupported(
new AudioFormat.Builder()
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
......@@ -367,7 +391,7 @@ public final class AudioCapabilities {
}
}
supportedEncodingsListBuilder.add(AudioFormat.ENCODING_PCM_16BIT);
return Ints.toArray(supportedEncodingsListBuilder.build());
return supportedEncodingsListBuilder.build();
}
/**
......
......@@ -39,6 +39,17 @@ public final class DtsUtil {
private static final int SYNC_VALUE_14B_BE = 0x1FFFE800;
private static final int SYNC_VALUE_LE = 0xFE7F0180;
private static final int SYNC_VALUE_14B_LE = 0xFF1F00E8;
/**
* DTS Extension Substream Syncword (in different Endianness). See ETSI TS 102 114 (V1.6.1)
* Section 7.4.1.
*/
private static final int SYNC_EXT_SUB_LE = 0x25205864;
/**
* DTS FTOC Sync words (in different Endianness). See ETSI TS 103 491 (V1.2.1) Section 6.4.4.1.
*/
private static final int SYNC_FTOC_LE = 0xF21B4140;
private static final int SYNC_FTOC_NON_SYNC_LE = 0xE842C471;
private static final byte FIRST_BYTE_BE = (byte) (SYNC_VALUE_BE >>> 24);
private static final byte FIRST_BYTE_14B_BE = (byte) (SYNC_VALUE_14B_BE >>> 24);
private static final byte FIRST_BYTE_LE = (byte) (SYNC_VALUE_LE >>> 24);
......@@ -147,6 +158,16 @@ public final class DtsUtil {
* @return The number of audio samples represented by the syncframe.
*/
public static int parseDtsAudioSampleCount(ByteBuffer buffer) {
if ((buffer.getInt(0) == SYNC_FTOC_LE) || (buffer.getInt(0) == SYNC_FTOC_NON_SYNC_LE)) {
// Check for DTS:X Profile 2 sync or non sync word and return 1024 if found. This is the only
// audio sample count that is used by DTS:X Streaming Encoder.
return 1024;
} else if (buffer.getInt(0) == SYNC_EXT_SUB_LE) {
// Check for DTS Express sync word and return 4096 if found. This is the only audio sample
// count that is used by DTS Streaming Encoder.
return 4096;
}
// See ETSI TS 102 114 subsection 5.4.1.
int position = buffer.position();
int nblks;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment