mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Merge pull request #335 from cedricxperi:dts-direct-passthrough-support
PiperOrigin-RevId: 535255453
This commit is contained in:
commit
c3dd88d715
@ -17,6 +17,8 @@
|
|||||||
* Ogg: Fix bug when seeking in files with a long duration
|
* Ogg: Fix bug when seeking in files with a long duration
|
||||||
([#391](https://github.com/androidx/media/issues/391)).
|
([#391](https://github.com/androidx/media/issues/391)).
|
||||||
* Audio:
|
* Audio:
|
||||||
|
* Add direct playback support for DTS Express and DTS:X
|
||||||
|
([#335](https://github.com/androidx/media/pull/335)).
|
||||||
* Audio Offload:
|
* Audio Offload:
|
||||||
* Add `AudioSink.getFormatOffloadSupport(Format)` that retrieves level of
|
* Add `AudioSink.getFormatOffloadSupport(Format)` that retrieves level of
|
||||||
offload support the sink can provide for the format through a
|
offload support the sink can provide for the format through a
|
||||||
|
@ -192,6 +192,7 @@ public final class C {
|
|||||||
ENCODING_DTS_HD,
|
ENCODING_DTS_HD,
|
||||||
ENCODING_DOLBY_TRUEHD,
|
ENCODING_DOLBY_TRUEHD,
|
||||||
ENCODING_OPUS,
|
ENCODING_OPUS,
|
||||||
|
ENCODING_DTS_UHD_P2,
|
||||||
})
|
})
|
||||||
public @interface Encoding {}
|
public @interface Encoding {}
|
||||||
|
|
||||||
@ -256,6 +257,8 @@ public final class C {
|
|||||||
@UnstableApi public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS;
|
@UnstableApi public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS;
|
||||||
/** See {@link AudioFormat#ENCODING_DTS_HD}. */
|
/** See {@link AudioFormat#ENCODING_DTS_HD}. */
|
||||||
@UnstableApi public static final int ENCODING_DTS_HD = 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}. */
|
/** See {@link AudioFormat#ENCODING_DOLBY_TRUEHD}. */
|
||||||
@UnstableApi public static final int ENCODING_DOLBY_TRUEHD = AudioFormat.ENCODING_DOLBY_TRUEHD;
|
@UnstableApi public static final int ENCODING_DOLBY_TRUEHD = AudioFormat.ENCODING_DOLBY_TRUEHD;
|
||||||
/** See {@link AudioFormat#ENCODING_OPUS}. */
|
/** See {@link AudioFormat#ENCODING_OPUS}. */
|
||||||
|
@ -595,6 +595,10 @@ public final class MimeTypes {
|
|||||||
return C.ENCODING_DTS;
|
return C.ENCODING_DTS;
|
||||||
case MimeTypes.AUDIO_DTS_HD:
|
case MimeTypes.AUDIO_DTS_HD:
|
||||||
return C.ENCODING_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:
|
case MimeTypes.AUDIO_TRUEHD:
|
||||||
return C.ENCODING_DOLBY_TRUEHD;
|
return C.ENCODING_DOLBY_TRUEHD;
|
||||||
case MimeTypes.AUDIO_OPUS:
|
case MimeTypes.AUDIO_OPUS:
|
||||||
|
@ -1884,6 +1884,14 @@ public final class Util {
|
|||||||
return AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
return AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
||||||
case 8:
|
case 8:
|
||||||
return AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
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:
|
case 12:
|
||||||
return AudioFormat.CHANNEL_OUT_7POINT1POINT4;
|
return AudioFormat.CHANNEL_OUT_7POINT1POINT4;
|
||||||
default:
|
default:
|
||||||
|
@ -48,21 +48,19 @@ import java.util.Arrays;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class AudioCapabilities {
|
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;
|
@VisibleForTesting /* package */ static final int DEFAULT_SAMPLE_RATE_HZ = 48_000;
|
||||||
|
|
||||||
/** The minimum audio capabilities supported by all devices. */
|
/** The minimum audio capabilities supported by all devices. */
|
||||||
public static final AudioCapabilities DEFAULT_AUDIO_CAPABILITIES =
|
public static final AudioCapabilities DEFAULT_AUDIO_CAPABILITIES =
|
||||||
new AudioCapabilities(new int[] {AudioFormat.ENCODING_PCM_16BIT}, DEFAULT_MAX_CHANNEL_COUNT);
|
new AudioCapabilities(new int[] {AudioFormat.ENCODING_PCM_16BIT}, DEFAULT_MAX_CHANNEL_COUNT);
|
||||||
|
|
||||||
/** Audio capabilities when the device specifies external surround sound. */
|
/** Encodings supported when the device specifies external surround sound. */
|
||||||
@SuppressWarnings("InlinedApi")
|
private static final ImmutableList<Integer> EXTERNAL_SURROUND_SOUND_ENCODINGS =
|
||||||
private static final AudioCapabilities EXTERNAL_SURROUND_SOUND_CAPABILITIES =
|
ImmutableList.of(
|
||||||
new AudioCapabilities(
|
AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_AC3, AudioFormat.ENCODING_E_AC3);
|
||||||
new int[] {
|
|
||||||
AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_AC3, AudioFormat.ENCODING_E_AC3
|
|
||||||
},
|
|
||||||
DEFAULT_MAX_CHANNEL_COUNT);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All surround sound encodings that a device may be capable of playing mapped to a maximum
|
* All surround sound encodings that a device may be capable of playing mapped to a maximum
|
||||||
@ -73,6 +71,7 @@ public final class AudioCapabilities {
|
|||||||
.put(C.ENCODING_AC3, 6)
|
.put(C.ENCODING_AC3, 6)
|
||||||
.put(C.ENCODING_AC4, 6)
|
.put(C.ENCODING_AC4, 6)
|
||||||
.put(C.ENCODING_DTS, 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_JOC, 6)
|
||||||
.put(C.ENCODING_E_AC3, 8)
|
.put(C.ENCODING_E_AC3, 8)
|
||||||
.put(C.ENCODING_DTS_HD, 8)
|
.put(C.ENCODING_DTS_HD, 8)
|
||||||
@ -103,25 +102,39 @@ public final class AudioCapabilities {
|
|||||||
if (Util.SDK_INT >= 23 && Api23.isBluetoothConnected(context)) {
|
if (Util.SDK_INT >= 23 && Api23.isBluetoothConnected(context)) {
|
||||||
return DEFAULT_AUDIO_CAPABILITIES;
|
return DEFAULT_AUDIO_CAPABILITIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImmutableSet.Builder<Integer> supportedEncodings = new ImmutableSet.Builder<>();
|
||||||
if (deviceMaySetExternalSurroundSoundGlobalSetting()
|
if (deviceMaySetExternalSurroundSoundGlobalSetting()
|
||||||
&& Global.getInt(context.getContentResolver(), EXTERNAL_SURROUND_SOUND_KEY, 0) == 1) {
|
&& 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
|
// 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
|
// 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
|
// it on TV and automotive devices, which generally shouldn't support audio offload for surround
|
||||||
// encodings.
|
// encodings.
|
||||||
if (Util.SDK_INT >= 29 && (Util.isTv(context) || Util.isAutomotive(context))) {
|
if (Util.SDK_INT >= 29 && (Util.isTv(context) || Util.isAutomotive(context))) {
|
||||||
|
supportedEncodings.addAll(Api29.getDirectPlaybackSupportedEncodings());
|
||||||
return new AudioCapabilities(
|
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),
|
ImmutableSet<Integer> supportedEncodingsSet = supportedEncodings.build();
|
||||||
intent.getIntExtra(
|
if (!supportedEncodingsSet.isEmpty()) {
|
||||||
AudioManager.EXTRA_MAX_CHANNEL_COUNT, /* defaultValue= */ DEFAULT_MAX_CHANNEL_COUNT));
|
return new AudioCapabilities(
|
||||||
|
Ints.toArray(supportedEncodingsSet), /* maxChannelCount= */ DEFAULT_MAX_CHANNEL_COUNT);
|
||||||
|
}
|
||||||
|
return DEFAULT_AUDIO_CAPABILITIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,7 +216,8 @@ public final class AudioCapabilities {
|
|||||||
if (encoding == C.ENCODING_E_AC3_JOC && !supportsEncoding(C.ENCODING_E_AC3_JOC)) {
|
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).
|
// E-AC3 receivers support E-AC3 JOC streams (but decode only the base layer).
|
||||||
encoding = C.ENCODING_E_AC3;
|
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).
|
// DTS receivers support DTS-HD streams (but decode only the core layer).
|
||||||
encoding = C.ENCODING_DTS;
|
encoding = C.ENCODING_DTS;
|
||||||
}
|
}
|
||||||
@ -220,7 +234,13 @@ public final class AudioCapabilities {
|
|||||||
channelCount = getMaxSupportedChannelCountForPassthrough(encoding, sampleRate);
|
channelCount = getMaxSupportedChannelCountForPassthrough(encoding, sampleRate);
|
||||||
} else {
|
} else {
|
||||||
channelCount = format.channelCount;
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,9 +375,13 @@ public final class AudioCapabilities {
|
|||||||
private Api29() {}
|
private Api29() {}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
public static int[] getDirectPlaybackSupportedEncodings() {
|
public static ImmutableList<Integer> getDirectPlaybackSupportedEncodings() {
|
||||||
ImmutableList.Builder<Integer> supportedEncodingsListBuilder = ImmutableList.builder();
|
ImmutableList.Builder<Integer> supportedEncodingsListBuilder = ImmutableList.builder();
|
||||||
for (int encoding : ALL_SURROUND_ENCODINGS_AND_MAX_CHANNELS.keySet()) {
|
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(
|
if (AudioTrack.isDirectPlaybackSupported(
|
||||||
new AudioFormat.Builder()
|
new AudioFormat.Builder()
|
||||||
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
|
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
|
||||||
@ -369,7 +393,7 @@ public final class AudioCapabilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
supportedEncodingsListBuilder.add(AudioFormat.ENCODING_PCM_16BIT);
|
supportedEncodingsListBuilder.add(AudioFormat.ENCODING_PCM_16BIT);
|
||||||
return Ints.toArray(supportedEncodingsListBuilder.build());
|
return supportedEncodingsListBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,6 +41,17 @@ public final class DtsUtil {
|
|||||||
private static final int SYNC_VALUE_14B_BE = 0x1FFFE800;
|
private static final int SYNC_VALUE_14B_BE = 0x1FFFE800;
|
||||||
private static final int SYNC_VALUE_LE = 0xFE7F0180;
|
private static final int SYNC_VALUE_LE = 0xFE7F0180;
|
||||||
private static final int SYNC_VALUE_14B_LE = 0xFF1F00E8;
|
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_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_14B_BE = (byte) (SYNC_VALUE_14B_BE >>> 24);
|
||||||
private static final byte FIRST_BYTE_LE = (byte) (SYNC_VALUE_LE >>> 24);
|
private static final byte FIRST_BYTE_LE = (byte) (SYNC_VALUE_LE >>> 24);
|
||||||
@ -149,6 +160,16 @@ public final class DtsUtil {
|
|||||||
* @return The number of audio samples represented by the syncframe.
|
* @return The number of audio samples represented by the syncframe.
|
||||||
*/
|
*/
|
||||||
public static int parseDtsAudioSampleCount(ByteBuffer buffer) {
|
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.
|
// See ETSI TS 102 114 subsection 5.4.1.
|
||||||
int position = buffer.position();
|
int position = buffer.position();
|
||||||
int nblks;
|
int nblks;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user