Skip to content

Commit 6895466

Browse files
nift4rohitjoins
authored andcommitted
Support DTS-HD MA profiles
Android supports a seperate AudioFormat.ENCODING_DTS_HD_MA and a MediaCodec mime type with ;profile=dtsma since SDK 34, but ExoPlayer wasn't using this even though the platform docs explicitly say to use the new encoding for DTS-HD MA. Implement detection of DTS-HD MA and add plumbing to use this constant where available, and fall back to previous behavior of using ENCODING_DTS_HD otherwise for backwards compatibility with devices that weren't updated to support the new constant/MIME type. DTS-HD MA exists in two versions, in multi-layer version where fallback to DTS decoders is valid and in a single-layer version where it is not. Add detection for this and change fallbacks accordingly. Issue: #2487
1 parent 8928f04 commit 6895466

38 files changed

Lines changed: 105 additions & 32 deletions

File tree

libraries/common/src/main/java/androidx/media3/common/C.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ private C() {}
185185
* #ENCODING_PCM_FLOAT_BIG_ENDIAN}, {@link #ENCODING_PCM_DOUBLE}, {@link
186186
* #ENCODING_PCM_DOUBLE_BIG_ENDIAN}, {@link #ENCODING_MP3}, {@link #ENCODING_AC3}, {@link
187187
* #ENCODING_E_AC3}, {@link #ENCODING_E_AC3_JOC}, {@link #ENCODING_AC4}, {@link #ENCODING_DTS},
188-
* {@link #ENCODING_DTS_HD}, {@link #ENCODING_DOLBY_TRUEHD}, {@link #ENCODING_OPUS} or {@link
189-
* #ENCODING_DSD}.
188+
* {@link #ENCODING_DTS_HD}, {@link #ENCODING_DTS_HD_MA}, {@link #ENCODING_DOLBY_TRUEHD}, {@link
189+
* #ENCODING_OPUS} or {@link #ENCODING_DSD}.
190190
*/
191191
@Documented
192192
@Retention(RetentionPolicy.SOURCE)
@@ -218,6 +218,7 @@ private C() {}
218218
ENCODING_AC4,
219219
ENCODING_DTS,
220220
ENCODING_DTS_HD,
221+
ENCODING_DTS_HD_MA,
221222
ENCODING_DOLBY_TRUEHD,
222223
ENCODING_OPUS,
223224
ENCODING_DTS_UHD_P2,
@@ -329,6 +330,9 @@ private C() {}
329330
/** See {@link AudioFormat#ENCODING_DTS_HD}. */
330331
public static final int ENCODING_DTS_HD = AudioFormat.ENCODING_DTS_HD;
331332

333+
/** See {@link AudioFormat#ENCODING_DTS_HD_MA}. */
334+
public static final int ENCODING_DTS_HD_MA = AudioFormat.ENCODING_DTS_HD_MA;
335+
332336
/** See {@link AudioFormat#ENCODING_DTS_UHD_P2}. */
333337
public static final int ENCODING_DTS_UHD_P2 = AudioFormat.ENCODING_DTS_UHD_P2;
334338

libraries/common/src/main/java/androidx/media3/common/MimeTypes.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,20 @@ public final class MimeTypes {
9292
@UnstableApi public static final String AUDIO_DSD = BASE_TYPE_AUDIO + "/dsd";
9393
public static final String AUDIO_DTS = BASE_TYPE_AUDIO + "/vnd.dts";
9494
public static final String AUDIO_DTS_HD = BASE_TYPE_AUDIO + "/vnd.dts.hd";
95+
public static final String AUDIO_DTS_HD_MA = BASE_TYPE_AUDIO + "/vnd.dts.hd;profile=dtsma";
9596
public static final String AUDIO_DTS_EXPRESS = BASE_TYPE_AUDIO + "/vnd.dts.hd;profile=lbr";
9697

98+
/**
99+
* DTS-HD MA can be encoded in a multi-layer setup with a core layer ({@link #AUDIO_DTS_HD_MA}),
100+
* where fallback to a DTS core decoder is possible, or in a single-layer setup with only a
101+
* lossless layer, where this is not. The same MIME type is normally used for both, but it's
102+
* important for ExoPlayer to differentiate this at extractor level to understand whether decoder
103+
* fallback is possible or not. Hence, use this dummy MIME type to signal lossless-only DTS-HD MA.
104+
*/
105+
@UnstableApi
106+
public static final String AUDIO_EXOPLAYER_DTS_HD_MA_CORELESS =
107+
BASE_TYPE_AUDIO + "/x-exoplayer-dtsma-coreless";
108+
97109
@UnstableApi
98110
public static final String AUDIO_DTS_UHD_P2 = BASE_TYPE_AUDIO + "/vnd.dts.uhd;profile=p2";
99111

@@ -509,8 +521,10 @@ public static String getMediaMimeType(@Nullable String codec) {
509521
return MimeTypes.AUDIO_DTS;
510522
} else if (codec.startsWith("dtse")) {
511523
return MimeTypes.AUDIO_DTS_EXPRESS;
512-
} else if (codec.startsWith("dtsh") || codec.startsWith("dtsl")) {
524+
} else if (codec.startsWith("dtsh")) {
513525
return MimeTypes.AUDIO_DTS_HD;
526+
} else if (codec.startsWith("dtsl")) {
527+
return MimeTypes.AUDIO_EXOPLAYER_DTS_HD_MA_CORELESS;
514528
} else if (codec.startsWith("dtsx")) {
515529
return MimeTypes.AUDIO_DTS_UHD_P2;
516530
} else if (codec.startsWith("opus")) {
@@ -716,6 +730,9 @@ public static boolean isDolbyVisionCodec(
716730
return C.ENCODING_DTS;
717731
case MimeTypes.AUDIO_DTS_HD:
718732
return C.ENCODING_DTS_HD;
733+
case MimeTypes.AUDIO_DTS_HD_MA:
734+
case MimeTypes.AUDIO_EXOPLAYER_DTS_HD_MA_CORELESS:
735+
return C.ENCODING_DTS_HD_MA;
719736
case MimeTypes.AUDIO_DTS_EXPRESS:
720737
return C.ENCODING_DTS_HD;
721738
case MimeTypes.AUDIO_DTS_UHD_P2:

libraries/common/src/main/java/androidx/media3/common/util/Util.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2711,6 +2711,7 @@ public static int getApiLevelThatAudioFormatIntroducedAudioEncoding(int encoding
27112711
case C.ENCODING_PCM_32BIT:
27122712
return 31;
27132713
case C.ENCODING_DTS_UHD_P2:
2714+
case C.ENCODING_DTS_HD_MA:
27142715
case C.ENCODING_DSD:
27152716
return 34;
27162717
default:

libraries/common/src/test/java/androidx/media3/common/MimeTypesTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ public void getMediaMimeType_fromValidCodecs_returnsCorrectMimeType() {
179179
assertThat(MimeTypes.getMediaMimeType("dtsc")).isEqualTo(MimeTypes.AUDIO_DTS);
180180
assertThat(MimeTypes.getMediaMimeType("dtse")).isEqualTo(MimeTypes.AUDIO_DTS_EXPRESS);
181181
assertThat(MimeTypes.getMediaMimeType("dtsh")).isEqualTo(MimeTypes.AUDIO_DTS_HD);
182-
assertThat(MimeTypes.getMediaMimeType("dtsl")).isEqualTo(MimeTypes.AUDIO_DTS_HD);
182+
assertThat(MimeTypes.getMediaMimeType("dtsl"))
183+
.isEqualTo(MimeTypes.AUDIO_EXOPLAYER_DTS_HD_MA_CORELESS);
183184
assertThat(MimeTypes.getMediaMimeType("dtsx")).isEqualTo(MimeTypes.AUDIO_DTS_UHD_P2);
184185
assertThat(MimeTypes.getMediaMimeType("opus")).isEqualTo(MimeTypes.AUDIO_OPUS);
185186
assertThat(MimeTypes.getMediaMimeType("vorbis")).isEqualTo(MimeTypes.AUDIO_VORBIS);

libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegLibrary.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ public static boolean supportsFormat(String mimeType) {
133133
case MimeTypes.AUDIO_DTS:
134134
case MimeTypes.AUDIO_DTS_EXPRESS:
135135
case MimeTypes.AUDIO_DTS_HD:
136+
case MimeTypes.AUDIO_DTS_HD_MA:
137+
case MimeTypes.AUDIO_EXOPLAYER_DTS_HD_MA_CORELESS:
136138
return "dca";
137139
case MimeTypes.AUDIO_VORBIS:
138140
return "vorbis";

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public final class AudioCapabilities {
9898
.put(C.ENCODING_E_AC3_JOC, 6)
9999
.put(C.ENCODING_E_AC3, 8)
100100
.put(C.ENCODING_DTS_HD, 8)
101+
.put(C.ENCODING_DTS_HD_MA, 8)
101102
.put(C.ENCODING_DOLBY_TRUEHD, 8)
102103
.buildOrThrow();
103104

@@ -398,6 +399,23 @@ public Pair<Integer, Integer> getEncodingAndChannelConfigForPassthrough(
398399
|| (encoding == C.ENCODING_DTS_UHD_P2 && !supportsEncoding(C.ENCODING_DTS_UHD_P2))) {
399400
// DTS receivers support DTS-HD streams (but decode only the core layer).
400401
encoding = C.ENCODING_DTS;
402+
} else if (MimeTypes.AUDIO_EXOPLAYER_DTS_HD_MA_CORELESS.equals(format.sampleMimeType)) {
403+
// DTS-HD MA without core layer can only be decoded by DTS-HD MA receivers. However, because
404+
// the constant for DTS-HD MA was only added in 2022 (https://r.android.com/1992892), we have
405+
// to try the older DTS-HD constant for backwards compatibility.
406+
if (!supportsEncoding(C.ENCODING_DTS_HD_MA)) {
407+
encoding = C.ENCODING_DTS_HD;
408+
}
409+
} else if (encoding == C.ENCODING_DTS_HD_MA && !supportsEncoding(C.ENCODING_DTS_HD_MA)) {
410+
// DTS-HD MA with core layer can be decoded by any DTS or DTS-HD MA receivers. Also, because
411+
// the constant for DTS-HD MA was only added in 2022 (https://r.android.com/1992892), we have
412+
// to try the older DTS-HD constant for backwards compatibility as well.
413+
if (supportsEncoding(C.ENCODING_DTS_HD)) {
414+
encoding = C.ENCODING_DTS_HD;
415+
} else {
416+
// DTS receivers support DTS-HD MA streams (but decode only the core layer).
417+
encoding = C.ENCODING_DTS;
418+
}
401419
}
402420
if (!supportsEncoding(encoding)) {
403421
return null;

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,7 @@ private FormatConfig getFormatConfig(Format format, int preferredBufferSize) {
18371837
return AacUtil.AAC_LD_AUDIO_SAMPLE_COUNT;
18381838
case C.ENCODING_DTS:
18391839
case C.ENCODING_DTS_HD:
1840+
case C.ENCODING_DTS_HD_MA:
18401841
case C.ENCODING_DTS_UHD_P2:
18411842
return DtsUtil.parseDtsAudioSampleCount(buffer);
18421843
case C.ENCODING_AC3:

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,18 @@ public static List<String> getAlternativeCodecMimeTypes(Format format) {
384384
// E-AC3 decoders can decode JOC streams, but in 2-D rather than 3-D.
385385
return Collections.singletonList(MimeTypes.AUDIO_E_AC3);
386386
}
387+
if (MimeTypes.AUDIO_EXOPLAYER_DTS_HD_MA_CORELESS.equals(format.sampleMimeType)) {
388+
// Coreless DTS-HD MA can only be decoded by a DTS-HD MA decoder. However, as DTS-HD MA was
389+
// added as separate MIME type in platform in 2022, we need to try the older DTS-HD mime type
390+
// for backwards compatibility. (https://r.android.com/2302237)
391+
return List.of(MimeTypes.AUDIO_DTS_HD_MA, MimeTypes.AUDIO_DTS_HD);
392+
}
393+
if (MimeTypes.AUDIO_DTS_HD_MA.equals(format.sampleMimeType)) {
394+
// DTS-HD MA was added as separate MIME type in platform in 2022, so we need to try the older
395+
// DTS-HD mime type for backwards compatibility. (https://r.android.com/2302237)
396+
// And because this is DTS-HD MA with core layer, even a DTS decoder can decode this stream.
397+
return List.of(MimeTypes.AUDIO_DTS_HD, MimeTypes.AUDIO_DTS);
398+
}
387399
if (MimeTypes.AUDIO_DTS_HD.equals(format.sampleMimeType)
388400
|| MimeTypes.AUDIO_DTS_UHD_P2.equals(format.sampleMimeType)) {
389401
// DTS decoders support DTS-HD streams (but decode only the core layer).

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/util/EventLogger.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,8 @@ private static String encodingAsString(@C.Encoding int encoding) {
805805
return "dts";
806806
case C.ENCODING_DTS_HD:
807807
return "dts-hd";
808+
case C.ENCODING_DTS_HD_MA:
809+
return "dts-hd-ma";
808810
case C.ENCODING_DTS_UHD_P2:
809811
return "dts-uhd-p2";
810812
case C.ENCODING_DSD:

libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,8 +795,10 @@ private static String fourCCToMimeType(String fourCC) {
795795
return MimeTypes.AUDIO_E_AC3;
796796
} else if (fourCC.equalsIgnoreCase("dtsc")) {
797797
return MimeTypes.AUDIO_DTS;
798-
} else if (fourCC.equalsIgnoreCase("dtsh") || fourCC.equalsIgnoreCase("dtsl")) {
798+
} else if (fourCC.equalsIgnoreCase("dtsh")) {
799799
return MimeTypes.AUDIO_DTS_HD;
800+
} else if (fourCC.equalsIgnoreCase("dtsl")) {
801+
return MimeTypes.AUDIO_EXOPLAYER_DTS_HD_MA_CORELESS;
800802
} else if (fourCC.equalsIgnoreCase("dtse")) {
801803
return MimeTypes.AUDIO_DTS_EXPRESS;
802804
} else if (fourCC.equalsIgnoreCase("opus")) {

0 commit comments

Comments
 (0)