Skip to content

Commit

Permalink
Merge pull request #335 from cedricxperi:dts-direct-passthrough-support
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 535255453
  • Loading branch information
tof-tof committed May 25, 2023
2 parents 41b19df + 730cfec commit c3dd88d
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 21 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* Ogg: Fix bug when seeking in files with a long duration
([#391](https://github.com/androidx/media/issues/391)).
* Audio:
* Add direct playback support for DTS Express and DTS:X
([#335](https://github.com/androidx/media/pull/335)).
* Audio Offload:
* Add `AudioSink.getFormatOffloadSupport(Format)` that retrieves level of
offload support the sink can provide for the format through a
Expand Down
3 changes: 3 additions & 0 deletions libraries/common/src/main/java/androidx/media3/common/C.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ private C() {}
ENCODING_DTS_HD,
ENCODING_DOLBY_TRUEHD,
ENCODING_OPUS,
ENCODING_DTS_UHD_P2,
})
public @interface Encoding {}

Expand Down Expand Up @@ -256,6 +257,8 @@ private C() {}
@UnstableApi public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS;
/** See {@link AudioFormat#ENCODING_DTS_HD}. */
@UnstableApi 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.
@UnstableApi public static final int ENCODING_DTS_UHD_P2 = 0x0000001e;
/** See {@link AudioFormat#ENCODING_DOLBY_TRUEHD}. */
@UnstableApi public static final int ENCODING_DOLBY_TRUEHD = AudioFormat.ENCODING_DOLBY_TRUEHD;
/** See {@link AudioFormat#ENCODING_OPUS}. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,10 @@ public static String getMimeTypeFromMp4ObjectType(int objectType) {
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1884,6 +1884,14 @@ public static int getAudioTrackChannelConfig(int channelCount) {
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,19 @@
@UnstableApi
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
Expand All @@ -73,6 +71,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)
Expand Down Expand Up @@ -103,25 +102,39 @@ public static AudioCapabilities getCapabilities(Context context) {
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;
}

/**
Expand Down Expand Up @@ -203,7 +216,8 @@ public Pair<Integer, Integer> getEncodingAndChannelConfigForPassthrough(Format f
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;
}
Expand All @@ -220,7 +234,13 @@ public Pair<Integer, Integer> getEncodingAndChannelConfigForPassthrough(Format f
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;
}
}
Expand Down Expand Up @@ -355,9 +375,13 @@ private static final class Api29 {
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)
Expand All @@ -369,7 +393,7 @@ public static int[] getDirectPlaybackSupportedEncodings() {
}
}
supportedEncodingsListBuilder.add(AudioFormat.ENCODING_PCM_16BIT);
return Ints.toArray(supportedEncodingsListBuilder.build());
return supportedEncodingsListBuilder.build();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,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);
Expand Down Expand Up @@ -149,6 +160,16 @@ public static int parseDtsAudioSampleCount(byte[] data) {
* @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;
Expand Down

0 comments on commit c3dd88d

Please sign in to comment.