Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dts direct passthrough support #335

Merged
merged 33 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b73d2cc
Add direct passthrough support for DTS audio.
cedricxperi Apr 18, 2023
22b45b7
Use getMaxSupportedChannelCountForPassthrough to assign channel Count
cedricxperi May 3, 2023
6823a29
Add C.ENCODING_DTS_UHD_P2
cedricxperi May 4, 2023
c0e03f1
Remove skip channelCount check for ENCODING_DTS
cedricxperi May 4, 2023
3b9d680
Add comments for DTSUtil.java
cedricxperi May 5, 2023
d01a93b
Add getAllSurroundEncodingsMaybeSupported()
cedricxperi May 5, 2023
689451b
Set AudioCapabilities.DEFAULT_MAX_CHANNEL_COUNT to 10
cedricxperi May 5, 2023
b069fb1
Return Immutable List for getAllSurroundEncodingsMaybeSupported()
cedricxperi May 8, 2023
c984387
Re-word comments for new DTS Sync words in DtsUtil.java.
cedricxperi May 8, 2023
f47930e
Refactor getDirectPlaybackSupportedEncodings()
cedricxperi May 8, 2023
abc46d4
Rename some variables and change to use ImmutableList.Builder.
cedricxperi May 10, 2023
753257e
Refactor Audio Capabilities
cedricxperi May 10, 2023
b1ac768
Add distinct encodings check before returning AudioCapabilities.
cedricxperi May 10, 2023
924723d
Additional changes to AudioCapabilities.java and Util.java
cedricxperi May 10, 2023
53f35f4
Use ImmutableSet to store discovered encodings in AudioCapabilities.java
cedricxperi May 11, 2023
773d3c5
Swap empty line in AudioCapabilities.java
cedricxperi May 11, 2023
6b4cf4d
Swap HDMI case and non-HDMI case in AudioCapabilities.
cedricxperi May 12, 2023
905ad1c
Fixed bug in HDMI reporting logic.
cedricxperi May 12, 2023
cb29e8f
Construct AudioCapabilities with HDMI reported MaxChannelCount.
cedricxperi May 15, 2023
424b5d8
Add issue link for AudioCapabilities
cedricxperi May 16, 2023
47b0726
Format with google-java-format
tianyif May 16, 2023
5a6906a
Add release note
tianyif May 25, 2023
d0cd2f5
Refactor AudioCapabilities.getCapabilities
tianyif May 16, 2023
c61f8d3
Add C.ENCODING_DTS_UHD_P2 to C.Encoding
tianyif May 16, 2023
b9a53da
Refactor AudioCapabilities.getCapabilities
tianyif May 16, 2023
162f5e8
Add type argument when using ImmutableSet
tianyif May 17, 2023
f69718e
Fix the lint issues
tianyif May 17, 2023
9d147f2
Adjust the comment lines
tianyif May 17, 2023
ea32f11
Refactor the methods and reword the comments
tianyif May 19, 2023
48509df
Quick return the capabilities for API29 TV case
tianyif May 19, 2023
fb34983
Format with google-java-format
tianyif May 20, 2023
f301214
Refactor the comment
tianyif May 22, 2023
730cfec
Make the API 34 check inline and refactor the comment
tianyif May 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}

cedricxperi marked this conversation as resolved.
Show resolved Hide resolved
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)) {
Copy link
Contributor

@tianyif tianyif May 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cedricxperi We have two questions regarding this:

  1. I'm seeing in the referenced issue, Xiaomi TV V435H is the device for reproducing the bug. Are there any more devices can reproduce it?

  2. If DTS:X TV reporting ACTION_HDMI_AUDIO_PLUG.EXTRA_MAX_CHANNEL_COUNT as 8 instead of 10 #396 is a device problem, the issue can be fixed from their side?

The reason I'm asking above questions is - either this is a device or even platform issue, if it is able to be fixed, how do you think if we just don't attempt to play DTS:X for those devices (isPassthroughPlaybackSupported() will return false and DTS:X track won't be selected)? In that way this issue can be automatically solved by the device/platform without this workaround.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tianyif I do not have access to other device at the moment but it is reasonable to think that this would be reproduceable for all DTS:X TVs based on MTK platforms. It will take sometime for this issue to be fixed on the platform. Meanwhile, Xperi will have to go to each and every streaming service to tell them to add the above so that DTS:X can be played back. I think it is better to add this patch in the exoplayer code base so that everyone will be aware of it. When the issue get fixed on the platform (which could take a long time), we can remove this patch in another exoplayer contribution. Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will keep this patch. Thanks!

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;
cedricxperi marked this conversation as resolved.
Show resolved Hide resolved
/**
* 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