Skip to content

Commit 8928f04

Browse files
nift4rohitjoins
authored andcommitted
Support multiple alternative MediaCodec MIME types
DTS-HD MA with core maps to both DTS-HD and DTS. Add wiring to support more than one alternative MIME type.
1 parent 30d0770 commit 8928f04

7 files changed

Lines changed: 72 additions & 52 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ && isCodecProfileAndLevelSupported(
312312

313313
private boolean isSampleMimeTypeSupported(Format format) {
314314
return mimeType.equals(format.sampleMimeType)
315-
|| mimeType.equals(MediaCodecUtil.getAlternativeCodecMimeType(format));
315+
|| MediaCodecUtil.getAlternativeCodecMimeTypes(format).contains(mimeType);
316316
}
317317

318318
private boolean isCodecProfileAndLevelSupported(

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

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ public static synchronized List<MediaCodecInfo> getDecoderInfos(
193193
* decoders that fully support the format come first.
194194
*
195195
* <p>This list is more complete than {@link #getDecoderInfos}, as it also considers alternative
196-
* MIME types that are a close match using {@link #getAlternativeCodecMimeType}.
196+
* MIME types that are a close match using {@link #getAlternativeCodecMimeTypes}.
197197
*
198198
* @param mediaCodecSelector The decoder selector.
199199
* @param format The {@link Format} for which a decoder is required.
@@ -222,9 +222,9 @@ public static List<MediaCodecInfo> getDecoderInfosSoftMatch(
222222
}
223223

224224
/**
225-
* Returns a list of decoders for {@linkplain #getAlternativeCodecMimeType alternative MIME types}
226-
* that can decode samples of the provided {@link Format}, in the priority order specified by the
227-
* {@link MediaCodecSelector}.
225+
* Returns a list of decoders for {@linkplain #getAlternativeCodecMimeTypes alternative MIME
226+
* types} that can decode samples of the provided {@link Format}, in the priority order specified
227+
* by the {@link MediaCodecSelector}.
228228
*
229229
* <p>Since the {@link MediaCodecSelector} only has access to {@link Format#sampleMimeType}, the
230230
* list is not ordered to account for whether each decoder supports the details of the format
@@ -245,12 +245,17 @@ public static List<MediaCodecInfo> getAlternativeDecoderInfos(
245245
boolean requiresSecureDecoder,
246246
boolean requiresTunnelingDecoder)
247247
throws DecoderQueryException {
248-
@Nullable String alternativeMimeType = getAlternativeCodecMimeType(format);
249-
if (alternativeMimeType == null) {
248+
List<String> alternativeMimeTypes = getAlternativeCodecMimeTypes(format);
249+
if (alternativeMimeTypes.isEmpty()) {
250250
return ImmutableList.of();
251251
}
252-
return mediaCodecSelector.getDecoderInfos(
253-
alternativeMimeType, requiresSecureDecoder, requiresTunnelingDecoder);
252+
ImmutableList.Builder<MediaCodecInfo> listBuilder = new ImmutableList.Builder<>();
253+
for (String alternativeMimeType : alternativeMimeTypes) {
254+
listBuilder.addAll(
255+
mediaCodecSelector.getDecoderInfos(
256+
alternativeMimeType, requiresSecureDecoder, requiresTunnelingDecoder));
257+
}
258+
return listBuilder.build();
254259
}
255260

256261
/**
@@ -366,24 +371,23 @@ public static MediaCodecProfileAndLevel getHevcBaseLayerCodecProfileAndLevel(For
366371
}
367372

368373
/**
369-
* Returns an alternative codec MIME type (besides the default {@link Format#sampleMimeType}) that
370-
* can be used to decode samples of the provided {@link Format}.
374+
* Returns alternative codec MIME types (besides the default {@link Format#sampleMimeType}) that
375+
* can be used to decode samples of the provided {@link Format}, in order of preference.
371376
*
372377
* @param format The media format.
373-
* @return An alternative MIME type of a codec that be used decode samples of the provided {@code
374-
* Format} (besides the default {@link Format#sampleMimeType}), or null if no such alternative
375-
* exists.
378+
* @return Alternative MIME types of a codec that be used decode samples of the provided {@code
379+
* Format} (besides the default {@link Format#sampleMimeType}), or an empty List if no such
380+
* alternative exists.
376381
*/
377-
@Nullable
378-
public static String getAlternativeCodecMimeType(Format format) {
382+
public static List<String> getAlternativeCodecMimeTypes(Format format) {
379383
if (MimeTypes.AUDIO_E_AC3_JOC.equals(format.sampleMimeType)) {
380384
// E-AC3 decoders can decode JOC streams, but in 2-D rather than 3-D.
381-
return MimeTypes.AUDIO_E_AC3;
385+
return Collections.singletonList(MimeTypes.AUDIO_E_AC3);
382386
}
383387
if (MimeTypes.AUDIO_DTS_HD.equals(format.sampleMimeType)
384388
|| MimeTypes.AUDIO_DTS_UHD_P2.equals(format.sampleMimeType)) {
385389
// DTS decoders support DTS-HD streams (but decode only the core layer).
386-
return MimeTypes.AUDIO_DTS;
390+
return Collections.singletonList(MimeTypes.AUDIO_DTS);
387391
}
388392
if (MimeTypes.VIDEO_DOLBY_VISION.equals(format.sampleMimeType)) {
389393
// H.264/AVC, H.265/HEVC or AV1 decoders can decode the base layer of some DV profiles.
@@ -397,24 +401,24 @@ public static String getAlternativeCodecMimeType(Format format) {
397401
int profile = codecProfileAndLevel.getProfile();
398402
if (profile == CodecProfileLevel.DolbyVisionProfileDvheDtr
399403
|| profile == CodecProfileLevel.DolbyVisionProfileDvheSt) {
400-
return MimeTypes.VIDEO_H265;
404+
return Collections.singletonList(MimeTypes.VIDEO_H265);
401405
} else if (profile == CodecProfileLevel.DolbyVisionProfileDvavSe) {
402-
return MimeTypes.VIDEO_H264;
406+
return Collections.singletonList(MimeTypes.VIDEO_H264);
403407
} else if (profile == CodecProfileLevel.DolbyVisionProfileDvav110) {
404408
if (format.colorInfo != null
405409
&& format.colorInfo.colorTransfer == C.COLOR_TRANSFER_ST2084
406410
&& format.colorInfo.colorRange == C.COLOR_RANGE_FULL) {
407-
return null;
411+
return Collections.emptyList();
408412
}
409-
return MimeTypes.VIDEO_AV1;
413+
return Collections.singletonList(MimeTypes.VIDEO_AV1);
410414
}
411415
}
412416
}
413417
if (MimeTypes.VIDEO_MV_HEVC.equals(format.sampleMimeType)) {
414418
// Single-layer HEVC decoders can decode the base layer of MV-HEVC streams.
415-
return MimeTypes.VIDEO_H265;
419+
return Collections.singletonList(MimeTypes.VIDEO_H265);
416420
}
417-
return null;
421+
return Collections.emptyList();
418422
}
419423

420424
// Internal methods.

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3912,9 +3912,9 @@ public VideoTrackInfo(
39123912
@RendererCapabilities.DecoderSupport
39133913
int decoderSupport = RendererCapabilities.getDecoderSupport(formatSupport);
39143914
if (decoderSupport == RendererCapabilities.DECODER_SUPPORT_FALLBACK_MIMETYPE) {
3915-
String fallbackMimeType = MediaCodecUtil.getAlternativeCodecMimeType(format);
3916-
if (fallbackMimeType != null) {
3917-
resolvedMimeType = fallbackMimeType;
3915+
List<String> fallbackMimeType = MediaCodecUtil.getAlternativeCodecMimeTypes(format);
3916+
if (!fallbackMimeType.isEmpty()) {
3917+
resolvedMimeType = fallbackMimeType.get(0);
39183918
}
39193919
}
39203920

libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtilTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import androidx.media3.common.util.CodecSpecificDataUtil.MediaCodecProfileAndLevel;
2626
import androidx.test.ext.junit.runners.AndroidJUnit4;
2727
import com.google.common.collect.ImmutableList;
28+
import java.util.Collections;
2829
import org.junit.Test;
2930
import org.junit.runner.RunWith;
3031

@@ -228,7 +229,7 @@ public void getHevcBaseLayerCodecProfileAndLevel_rejectsFormatWithNoInitializati
228229
}
229230

230231
@Test
231-
public void getAlternativeCodecMimeType_withNonFallbackCompatibleFormat_returnsNull() {
232+
public void getAlternativeCodecMimeType_withNonFallbackCompatibleFormat_returnsEmpty() {
232233
// Profile 10.0 (Full Range PQ) which does NOT allow fallback.
233234
Format formatDav1NoFallbackPossible =
234235
new Format.Builder()
@@ -254,9 +255,9 @@ public void getAlternativeCodecMimeType_withNonFallbackCompatibleFormat_returnsN
254255
.build())
255256
.build();
256257

257-
assertThat(MediaCodecUtil.getAlternativeCodecMimeType(formatDav1NoFallbackPossible)).isNull();
258-
assertThat(MediaCodecUtil.getAlternativeCodecMimeType(formatDav1FallbackToAv1))
259-
.isEqualTo(MimeTypes.VIDEO_AV1);
258+
assertThat(MediaCodecUtil.getAlternativeCodecMimeTypes(formatDav1NoFallbackPossible)).isEmpty();
259+
assertThat(MediaCodecUtil.getAlternativeCodecMimeTypes(formatDav1FallbackToAv1))
260+
.isEqualTo(Collections.singletonList(MimeTypes.VIDEO_AV1));
260261
}
261262

262263
private static void assertHevcBaseLayerCodecProfileAndLevelForFormat(

libraries/inspector/src/main/java/androidx/media3/inspector/MediaExtractorCompatInternal.java

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,17 @@
7272
import androidx.media3.extractor.TrackAwareSeekMap;
7373
import androidx.media3.extractor.TrackOutput;
7474
import androidx.media3.extractor.mp4.PsshAtomUtil;
75+
import com.google.common.collect.ImmutableSet;
7576
import java.io.FileDescriptor;
7677
import java.io.FileNotFoundException;
7778
import java.io.IOException;
7879
import java.nio.ByteBuffer;
7980
import java.util.ArrayDeque;
8081
import java.util.ArrayList;
82+
import java.util.Collections;
8183
import java.util.HashMap;
8284
import java.util.HashSet;
85+
import java.util.List;
8386
import java.util.Map;
8487
import java.util.Set;
8588
import java.util.UUID;
@@ -825,17 +828,19 @@ private void onSampleQueueFormatInitialized(
825828
mediaExtractorSampleQueue,
826829
/* isCompatibilityTrack= */ false,
827830
/* compatibilityTrackMimeType= */ null));
828-
@Nullable
829-
String compatibilityTrackMimeType =
830-
MediaCodecUtil.getAlternativeCodecMimeType(newUpstreamFormat);
831-
if (compatibilityTrackMimeType != null) {
832-
mediaExtractorSampleQueue.setCompatibilityTrackIndex(tracks.size());
831+
List<String> compatibilityTrackMimeTypes =
832+
MediaCodecUtil.getAlternativeCodecMimeTypes(newUpstreamFormat);
833+
ImmutableSet.Builder<Integer> compatibilityTrackIndicesBuilder = new ImmutableSet.Builder<>();
834+
for (String compatibilityTrackMimeType : compatibilityTrackMimeTypes) {
835+
compatibilityTrackIndicesBuilder.add(tracks.size());
833836
tracks.add(
834837
new MediaExtractorTrack(
835838
mediaExtractorSampleQueue,
836839
/* isCompatibilityTrack= */ true,
837840
compatibilityTrackMimeType));
838841
}
842+
mediaExtractorSampleQueue.setCompatibilityTrackIndices(
843+
compatibilityTrackIndicesBuilder.build());
839844
}
840845

841846
private void maybeResolvePendingSeek() throws IOException {
@@ -969,7 +974,7 @@ private final class MediaExtractorSampleQueue extends SampleQueue {
969974
private final int trackId;
970975
private long trackDurationUs;
971976
private int mainTrackIndex;
972-
private int compatibilityTrackIndex;
977+
private Set<Integer> compatibilityTrackIndices;
973978

974979
private MediaExtractorSampleQueue(Allocator allocator, int trackId) {
975980
// We do not need the sample queue to acquire keys for encrypted samples, so we pass null
@@ -978,15 +983,15 @@ private MediaExtractorSampleQueue(Allocator allocator, int trackId) {
978983
this.trackId = trackId;
979984
trackDurationUs = C.TIME_UNSET;
980985
mainTrackIndex = C.INDEX_UNSET;
981-
compatibilityTrackIndex = C.INDEX_UNSET;
986+
compatibilityTrackIndices = Collections.emptySet();
982987
}
983988

984989
private void setMainTrackIndex(int mainTrackIndex) {
985990
this.mainTrackIndex = mainTrackIndex;
986991
}
987992

988-
private void setCompatibilityTrackIndex(int compatibilityTrackIndex) {
989-
this.compatibilityTrackIndex = compatibilityTrackIndex;
993+
private void setCompatibilityTrackIndices(Set<Integer> compatibilityTrackIndices) {
994+
this.compatibilityTrackIndices = compatibilityTrackIndices;
990995
}
991996

992997
// SampleQueue implementation.
@@ -1028,7 +1033,7 @@ public void sampleMetadata(
10281033
public String toString() {
10291034
return String.format(
10301035
"trackId: %s, mainTrackIndex: %s, compatibilityTrackIndex: %s",
1031-
trackId, mainTrackIndex, compatibilityTrackIndex);
1036+
trackId, mainTrackIndex, compatibilityTrackIndices);
10321037
}
10331038

10341039
private void queueSampleMetadata(long timeUs, @C.BufferFlags int flags) {
@@ -1038,7 +1043,7 @@ private void queueSampleMetadata(long timeUs, @C.BufferFlags int flags) {
10381043
mediaExtractorFlags |=
10391044
(flags & C.BUFFER_FLAG_KEY_FRAME) != 0 ? MediaExtractor.SAMPLE_FLAG_SYNC : 0;
10401045

1041-
if (compatibilityTrackIndex != C.INDEX_UNSET) {
1046+
for (Integer compatibilityTrackIndex : compatibilityTrackIndices) {
10421047
sampleMetadataQueue.addLast(
10431048
timeUs, /* flags= */ mediaExtractorFlags, compatibilityTrackIndex);
10441049
}

libraries/transformer/src/main/java/androidx/media3/transformer/SampleExporter.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package androidx.media3.transformer;
1818

1919
import static androidx.media3.common.ColorInfo.isTransferHdr;
20-
import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.getAlternativeCodecMimeType;
20+
import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.getAlternativeCodecMimeTypes;
2121
import static androidx.media3.transformer.EncoderUtil.getSupportedEncoders;
2222
import static androidx.media3.transformer.EncoderUtil.getSupportedEncodersForHdrEditing;
2323
import static androidx.media3.transformer.TransformerUtil.getProcessedTrackType;
@@ -110,10 +110,12 @@ private boolean feedMuxer() throws ExportException {
110110
inputFormat = inputFormat.buildUpon().setMetadata(metadata).build();
111111
}
112112
if (!muxerWrapper.supportsSampleMimeType(inputFormat.sampleMimeType)) {
113-
String alternativeSampleMimeType = getAlternativeCodecMimeType(inputFormat);
114-
if (muxerWrapper.supportsSampleMimeType(alternativeSampleMimeType)) {
115-
inputFormat =
116-
inputFormat.buildUpon().setSampleMimeType(alternativeSampleMimeType).build();
113+
for (String alternativeSampleMimeType : getAlternativeCodecMimeTypes(inputFormat)) {
114+
if (muxerWrapper.supportsSampleMimeType(alternativeSampleMimeType)) {
115+
inputFormat =
116+
inputFormat.buildUpon().setSampleMimeType(alternativeSampleMimeType).build();
117+
break;
118+
}
117119
}
118120
}
119121
try {

libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
import static androidx.media3.common.ColorInfo.SDR_BT709_LIMITED;
2020
import static androidx.media3.common.ColorInfo.isTransferHdr;
21-
import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.getAlternativeCodecMimeType;
21+
import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.getAlternativeCodecMimeTypes;
2222
import static androidx.media3.transformer.Composition.HDR_MODE_KEEP_HDR;
2323
import static androidx.media3.transformer.Composition.HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL;
2424
import static androidx.media3.transformer.EncoderUtil.getSupportedEncodersForHdrEditing;
@@ -159,15 +159,23 @@ private static boolean containsSlowMotionData(Format format) {
159159
if (requestedMimeType != null) {
160160
boolean requestedMimeTypeEqualsPrimaryOrAlternativeMimeType =
161161
requestedMimeType.equals(inputFormat.sampleMimeType)
162-
|| requestedMimeType.equals(getAlternativeCodecMimeType(inputFormat));
162+
|| getAlternativeCodecMimeTypes(inputFormat).contains(requestedMimeType);
163163
if (!requestedMimeTypeEqualsPrimaryOrAlternativeMimeType) {
164164
return true;
165165
}
166166
}
167167
if (requestedMimeType == null
168-
&& !muxerWrapper.supportsSampleMimeType(inputFormat.sampleMimeType)
169-
&& !muxerWrapper.supportsSampleMimeType(getAlternativeCodecMimeType(inputFormat))) {
170-
return true;
168+
&& !muxerWrapper.supportsSampleMimeType(inputFormat.sampleMimeType)) {
169+
boolean supported = false;
170+
for (String mimeType : getAlternativeCodecMimeTypes(inputFormat)) {
171+
supported = muxerWrapper.supportsSampleMimeType(mimeType);
172+
if (supported) {
173+
break;
174+
}
175+
}
176+
if (!supported) {
177+
return true;
178+
}
171179
}
172180
if (inputFormat.pixelWidthHeightRatio != 1f) {
173181
return true;

0 commit comments

Comments
 (0)