Skip to content

Commit 68f1011

Browse files
microkatzcopybara-github
authored andcommitted
Remove support for specific AC-4 profiles for Automotive scenarios
According to [DolbyVision Support](https://professionalsupport.dolby.com/s/article/Support-Dolby-Atmos-in-Android-Automotive-OS-media-apps?language=en_US), the "Dolby Atmos decoders in Android Automotive OS systems do not support Dolby AC-4 Immersive Stereo (IMS) decoding." Issue: #2609 PiperOrigin-RevId: 845227548
1 parent 4e0fb5d commit 68f1011

File tree

11 files changed

+123
-36
lines changed

11 files changed

+123
-36
lines changed

RELEASENOTES.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,16 @@
3232
'libiamf' to 'iamf_tools'.
3333
* `IamfDecoder` now supports changing output mix (layout or ID) during
3434
playback.
35+
* Correctly remove support for `AC4Profile22` and other legacy profiles
36+
when assessing AC-4 decoder support in Automotive scenarios
37+
([#2609](https://github.com/androidx/media/pull/2609)).
3538
* Video:
3639
* Text:
3740
* Metadata:
3841
* Image:
3942
* DataSource:
4043
* DRM:
4144
* Effect:
42-
* Deprecate `TimestampAdjustment` and `TimestampAdjustmentShaderProgram`
43-
in favour of `EditedMediaItem.Builder#setSpeed(SpeedProvider)`.
4445
* Muxers:
4546
* IMA extension:
4647
* Bug fix: Corrected an issue where the ad MIME type did not match the

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ public MediaCodecAudioRenderer(
312312
AudioSink audioSink,
313313
@Nullable LoudnessCodecController loudnessCodecController) {
314314
super(
315+
context.getApplicationContext(),
315316
C.TRACK_TYPE_AUDIO,
316317
codecAdapterFactory,
317318
mediaCodecSelector,
@@ -373,13 +374,13 @@ public String getName() {
373374
// Check whether the first decoder supports the format. This is the preferred decoder for the
374375
// format's MIME type, according to the MediaCodecSelector.
375376
MediaCodecInfo decoderInfo = decoderInfos.get(0);
376-
boolean isFormatSupported = decoderInfo.isFormatSupported(format);
377+
boolean isFormatSupported = decoderInfo.isFormatSupported(context, format);
377378
boolean isPreferredDecoder = true;
378379
if (!isFormatSupported) {
379380
// Check whether any of the other decoders support the format.
380381
for (int i = 1; i < decoderInfos.size(); i++) {
381382
MediaCodecInfo otherDecoderInfo = decoderInfos.get(i);
382-
if (otherDecoderInfo.isFormatSupported(format)) {
383+
if (otherDecoderInfo.isFormatSupported(context, format)) {
383384
decoderInfo = otherDecoderInfo;
384385
isFormatSupported = true;
385386
isPreferredDecoder = false;
@@ -431,7 +432,9 @@ protected List<MediaCodecInfo> getDecoderInfos(
431432
MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder)
432433
throws DecoderQueryException {
433434
return MediaCodecUtil.getDecoderInfosSortedByFormatSupport(
434-
getDecoderInfos(mediaCodecSelector, format, requiresSecureDecoder, audioSink), format);
435+
context,
436+
getDecoderInfos(mediaCodecSelector, format, requiresSecureDecoder, audioSink),
437+
format);
435438
}
436439

437440
/**

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

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.createCodecProfileLevel;
3535
import static com.google.common.base.Preconditions.checkNotNull;
3636

37+
import android.content.Context;
3738
import android.graphics.Point;
3839
import android.media.MediaCodec;
3940
import android.media.MediaCodecInfo.AudioCapabilities;
@@ -263,16 +264,17 @@ public int getMaxSupportedInstances() {
263264
* Returns whether the decoder may support decoding the given {@code format} both functionally and
264265
* performantly.
265266
*
267+
* @param context A context.
266268
* @param format The input media format.
267269
* @return Whether the decoder may support decoding the given {@code format}.
268-
* @throws MediaCodecUtil.DecoderQueryException Thrown if an error occurs while querying decoders.
269270
*/
270-
public boolean isFormatSupported(Format format) throws MediaCodecUtil.DecoderQueryException {
271+
public boolean isFormatSupported(Context context, Format format) {
271272
if (!isSampleMimeTypeSupported(format)) {
272273
return false;
273274
}
274275

275-
if (!isCodecProfileAndLevelSupported(format, /* checkPerformanceCapabilities= */ true)) {
276+
if (!isCodecProfileAndLevelSupported(
277+
context, format, /* checkPerformanceCapabilities= */ true)) {
276278
return false;
277279
}
278280

@@ -296,12 +298,14 @@ public boolean isFormatSupported(Format format) throws MediaCodecUtil.DecoderQue
296298
/**
297299
* Returns whether the decoder may functionally support decoding the given {@code format}.
298300
*
301+
* @param context A context.
299302
* @param format The input media format.
300303
* @return Whether the decoder may functionally support decoding the given {@code format}.
301304
*/
302-
public boolean isFormatFunctionallySupported(Format format) {
305+
public boolean isFormatFunctionallySupported(Context context, Format format) {
303306
return isSampleMimeTypeSupported(format)
304-
&& isCodecProfileAndLevelSupported(format, /* checkPerformanceCapabilities= */ false)
307+
&& isCodecProfileAndLevelSupported(
308+
context, format, /* checkPerformanceCapabilities= */ false)
305309
&& isCompressedAudioBitDepthSupported(format);
306310
}
307311

@@ -311,7 +315,7 @@ private boolean isSampleMimeTypeSupported(Format format) {
311315
}
312316

313317
private boolean isCodecProfileAndLevelSupported(
314-
Format format, boolean checkPerformanceCapabilities) {
318+
Context context, Format format, boolean checkPerformanceCapabilities) {
315319
Pair<Integer, Integer> codecProfileAndLevel =
316320
CodecSpecificDataUtil.getCodecProfileAndLevel(format);
317321
if (format.sampleMimeType != null && format.sampleMimeType.equals(MimeTypes.VIDEO_MV_HEVC)) {
@@ -370,7 +374,7 @@ private boolean isCodecProfileAndLevelSupported(
370374
if (mimeType.equals(MimeTypes.AUDIO_AC4) && profileLevels.length == 0) {
371375
// Some older devices don't report profile levels for AC-4. Estimate them using other data
372376
// in the codec capabilities.
373-
profileLevels = estimateLegacyAc4ProfileLevels(capabilities);
377+
profileLevels = estimateLegacyAc4ProfileLevels(context, capabilities);
374378
}
375379
if (SDK_INT == 23 && MimeTypes.VIDEO_VP9.equals(mimeType) && profileLevels.length == 0) {
376380
// Some older devices don't report profile levels for VP9. Estimate them using other data in
@@ -875,7 +879,7 @@ private static Point alignVideoSize(VideoCapabilities capabilities, int width, i
875879
* @return The estimated {@link CodecProfileLevel CodecProfileLevels} for the decoder.
876880
*/
877881
private static CodecProfileLevel[] estimateLegacyAc4ProfileLevels(
878-
@Nullable CodecCapabilities capabilities) {
882+
Context context, @Nullable CodecCapabilities capabilities) {
879883
int maxInChannelCount = 2;
880884
if (capabilities != null) {
881885
@Nullable AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities();
@@ -889,6 +893,15 @@ private static CodecProfileLevel[] estimateLegacyAc4ProfileLevels(
889893
level = CodecProfileLevel.AC4Level4;
890894
}
891895

896+
// Automotive platform MediaCodec AC-4 decoders are not expected to support AC4Profile22 and
897+
// deprecated profiles AC4Profile00, AC4Profile10 and AC4Profile11. See
898+
// https://professionalsupport.dolby.com/s/article/Support-Dolby-Atmos-in-Android-Automotive-OS-media-apps?language=en_US
899+
if (Util.isAutomotive(context)) {
900+
return new CodecProfileLevel[] {
901+
createCodecProfileLevel(CodecProfileLevel.AC4Profile21, level)
902+
};
903+
}
904+
892905
return new CodecProfileLevel[] {
893906
createCodecProfileLevel(CodecProfileLevel.AC4Profile00, level),
894907
createCodecProfileLevel(CodecProfileLevel.AC4Profile10, level),

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import static java.lang.Math.max;
3333
import static java.lang.annotation.ElementType.TYPE_USE;
3434

35+
import android.content.Context;
3536
import android.media.MediaCodec;
3637
import android.media.MediaCodec.CodecException;
3738
import android.media.MediaCrypto;
@@ -330,6 +331,7 @@ private static String buildCustomDiagnosticInfo(int errorCode) {
330331

331332
private static final int ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT = 32;
332333

334+
private final Context context;
333335
private final MediaCodecAdapter.Factory codecAdapterFactory;
334336
private final MediaCodecSelector mediaCodecSelector;
335337
private final boolean enableDecoderFallback;
@@ -415,6 +417,7 @@ private static String buildCustomDiagnosticInfo(int errorCode) {
415417
private ImmutableSet<String> subscribedCodecParameterKeys;
416418

417419
/**
420+
* @param context A context.
418421
* @param trackType The {@link C.TrackType track type} that the renderer handles.
419422
* @param codecAdapterFactory A factory for {@link MediaCodecAdapter} instances.
420423
* @param mediaCodecSelector A decoder selector.
@@ -426,12 +429,14 @@ private static String buildCustomDiagnosticInfo(int errorCode) {
426429
* explicitly using {@link MediaFormat#KEY_OPERATING_RATE}).
427430
*/
428431
public MediaCodecRenderer(
432+
Context context,
429433
@C.TrackType int trackType,
430434
MediaCodecAdapter.Factory codecAdapterFactory,
431435
MediaCodecSelector mediaCodecSelector,
432436
boolean enableDecoderFallback,
433437
float assumedMinimumCodecOperatingRate) {
434438
super(trackType);
439+
this.context = context.getApplicationContext();
435440
this.codecAdapterFactory = codecAdapterFactory;
436441
this.mediaCodecSelector = checkNotNull(mediaCodecSelector);
437442
this.enableDecoderFallback = enableDecoderFallback;
@@ -1390,7 +1395,7 @@ private void initCodec(MediaCodecInfo codecInfo, @Nullable MediaCrypto crypto) t
13901395
}
13911396
codecInitializedTimestamp = getClock().elapsedRealtime();
13921397

1393-
if (!codecInfo.isFormatSupported(inputFormat)) {
1398+
if (!codecInfo.isFormatSupported(context, inputFormat)) {
13941399
Log.w(
13951400
TAG,
13961401
Util.formatInvariant(

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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static java.lang.Math.max;
2121

2222
import android.annotation.SuppressLint;
23+
import android.content.Context;
2324
import android.media.MediaCodecInfo.CodecCapabilities;
2425
import android.media.MediaCodecInfo.CodecProfileLevel;
2526
import android.media.MediaCodecList;
@@ -256,10 +257,11 @@ public static List<MediaCodecInfo> getAlternativeDecoderInfos(
256257
*/
257258
@CheckResult
258259
public static List<MediaCodecInfo> getDecoderInfosSortedByFormatSupport(
259-
List<MediaCodecInfo> decoderInfos, Format format) {
260+
Context context, List<MediaCodecInfo> decoderInfos, Format format) {
260261
decoderInfos = new ArrayList<>(decoderInfos);
261262
sortByScore(
262-
decoderInfos, decoderInfo -> decoderInfo.isFormatFunctionallySupported(format) ? 1 : 0);
263+
decoderInfos,
264+
decoderInfo -> decoderInfo.isFormatFunctionallySupported(context, format) ? 1 : 0);
263265
return decoderInfos;
264266
}
265267

@@ -269,16 +271,12 @@ public static List<MediaCodecInfo> getDecoderInfosSortedByFormatSupport(
269271
*/
270272
@CheckResult
271273
public static List<MediaCodecInfo> getDecoderInfosSortedByFullFormatSupport(
272-
List<MediaCodecInfo> decoderInfos, Format format) {
274+
Context context, List<MediaCodecInfo> decoderInfos, Format format) {
273275
decoderInfos = new ArrayList<>(decoderInfos);
274276
sortByScore(
275277
decoderInfos,
276278
decoderInfo -> {
277-
try {
278-
return decoderInfo.isFormatSupported(format) ? 1 : 0;
279-
} catch (DecoderQueryException e) {
280-
return -1;
281-
}
279+
return decoderInfo.isFormatSupported(context, format) ? 1 : 0;
282280
});
283281
return decoderInfos;
284282
}

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ public MediaCodecVideoRenderer(
622622
*/
623623
protected MediaCodecVideoRenderer(Builder builder) {
624624
super(
625+
builder.context.getApplicationContext(),
625626
C.TRACK_TYPE_VIDEO,
626627
builder.codecAdapterFactory,
627628
builder.mediaCodecSelector,
@@ -759,13 +760,13 @@ public String getName() {
759760
// Check whether the first decoder supports the format. This is the preferred decoder for the
760761
// format's MIME type, according to the MediaCodecSelector.
761762
MediaCodecInfo decoderInfo = decoderInfos.get(0);
762-
boolean isFormatSupported = decoderInfo.isFormatSupported(format);
763+
boolean isFormatSupported = decoderInfo.isFormatSupported(context, format);
763764
boolean isPreferredDecoder = true;
764765
if (!isFormatSupported) {
765766
// Check whether any of the other decoders support the format.
766767
for (int i = 1; i < decoderInfos.size(); i++) {
767768
MediaCodecInfo otherDecoderInfo = decoderInfos.get(i);
768-
if (otherDecoderInfo.isFormatSupported(format)) {
769+
if (otherDecoderInfo.isFormatSupported(context, format)) {
769770
decoderInfo = otherDecoderInfo;
770771
isFormatSupported = true;
771772
isPreferredDecoder = false;
@@ -805,9 +806,10 @@ public String getName() {
805806
/* requiresTunnelingDecoder= */ true);
806807
if (!tunnelingDecoderInfos.isEmpty()) {
807808
MediaCodecInfo tunnelingDecoderInfo =
808-
MediaCodecUtil.getDecoderInfosSortedByFormatSupport(tunnelingDecoderInfos, format)
809+
MediaCodecUtil.getDecoderInfosSortedByFormatSupport(
810+
context, tunnelingDecoderInfos, format)
809811
.get(0);
810-
if (tunnelingDecoderInfo.isFormatSupported(format)
812+
if (tunnelingDecoderInfo.isFormatSupported(context, format)
811813
&& tunnelingDecoderInfo.isSeamlessAdaptationSupported(format)) {
812814
tunnelingSupport = TUNNELING_SUPPORTED;
813815
}
@@ -827,6 +829,7 @@ protected List<MediaCodecInfo> getDecoderInfos(
827829
MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder)
828830
throws DecoderQueryException {
829831
return MediaCodecUtil.getDecoderInfosSortedByFormatSupport(
832+
context,
830833
getDecoderInfos(context, mediaCodecSelector, format, requiresSecureDecoder, tunneling),
831834
format);
832835
}
@@ -842,6 +845,7 @@ protected List<MediaCodecInfo> getDecoderInfos(
842845
* MediaCodecUtil#getDecoderInfosSortedByFormatSupport} can be used to further sort the list into
843846
* an order where decoders that fully support the format come first.
844847
*
848+
* @param context A context.
845849
* @param mediaCodecSelector The decoder selector.
846850
* @param format The {@link Format} for which a decoder is required.
847851
* @param requiresSecureDecoder Whether a secure decoder is required.

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

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,21 @@
3434
import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_YES_WITH_RECONFIGURATION;
3535
import static com.google.common.truth.Truth.assertThat;
3636

37+
import android.content.Context;
38+
import android.content.pm.PackageManager;
3739
import androidx.media3.common.C;
3840
import androidx.media3.common.ColorInfo;
3941
import androidx.media3.common.Format;
42+
import androidx.media3.common.util.MediaFormatUtil;
4043
import androidx.media3.exoplayer.DecoderReuseEvaluation;
44+
import androidx.test.core.app.ApplicationProvider;
4145
import androidx.test.ext.junit.runners.AndroidJUnit4;
4246
import com.google.common.collect.ImmutableList;
4347
import org.junit.Test;
4448
import org.junit.runner.RunWith;
49+
import org.robolectric.Shadows;
50+
import org.robolectric.shadows.MediaCodecInfoBuilder;
51+
import org.robolectric.shadows.ShadowPackageManager;
4552

4653
/** Unit tests for {@link MediaCodecInfo}. */
4754
@RunWith(AndroidJUnit4.class)
@@ -433,7 +440,7 @@ public void canReuseCodec_eac3joc_returnsYesWithoutReconfiguration() {
433440

434441
@Test
435442
public void canReuseCodec_ac4_returnsYesWithoutReconfiguration() {
436-
MediaCodecInfo codecInfo = buildAc4CodecInfo();
443+
MediaCodecInfo codecInfo = buildAc4CodecInfo(FORMAT_AC4);
437444

438445
assertThat(codecInfo.canReuseCodec(FORMAT_AC4, FORMAT_AC4))
439446
.isEqualTo(
@@ -447,7 +454,7 @@ public void canReuseCodec_ac4_returnsYesWithoutReconfiguration() {
447454

448455
@Test
449456
public void canReuseCodec_ac4WithDifferentCodecs_returnsYesWithFlush() {
450-
MediaCodecInfo codecInfo = buildAc4CodecInfo();
457+
MediaCodecInfo codecInfo = buildAc4CodecInfo(FORMAT_AC4);
451458

452459
Format ac4VariantFormat = FORMAT_AC4.buildUpon().setCodecs("ac-4.02.01.03").build();
453460
assertThat(codecInfo.canReuseCodec(FORMAT_AC4, ac4VariantFormat))
@@ -460,6 +467,52 @@ public void canReuseCodec_ac4WithDifferentCodecs_returnsYesWithFlush() {
460467
/* discardReasons= */ 0));
461468
}
462469

470+
@Test
471+
public void isFormatSupported_ac4Profile00InAutomotiveContext_returnsFalse() throws Exception {
472+
Context context = ApplicationProvider.getApplicationContext();
473+
ShadowPackageManager shadowPackageManager = Shadows.shadowOf(context.getPackageManager());
474+
475+
Format formatAc4Profile00 =
476+
new Format.Builder()
477+
.setSampleMimeType(AUDIO_AC4)
478+
.setCodecs("ac-4.00.00.01") // AC4Profile00
479+
.setChannelCount(2)
480+
.setSampleRate(48000)
481+
.build();
482+
MediaCodecInfo codecInfo = buildAc4CodecInfo(formatAc4Profile00);
483+
484+
// Test non-Automotive case (FEATURE_AUTOMOTIVE is false)
485+
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, false);
486+
assertThat(codecInfo.isFormatSupported(context, formatAc4Profile00)).isTrue();
487+
488+
// Test Automotive case (FEATURE_AUTOMOTIVE is true)
489+
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
490+
assertThat(codecInfo.isFormatSupported(context, formatAc4Profile00)).isFalse();
491+
}
492+
493+
@Test
494+
public void isFormatSupported_ac4Profile21InAutomotiveContext_returnsTrue() throws Exception {
495+
Context context = ApplicationProvider.getApplicationContext();
496+
ShadowPackageManager shadowPackageManager = Shadows.shadowOf(context.getPackageManager());
497+
498+
Format formatAc4Profile21 =
499+
new Format.Builder()
500+
.setSampleMimeType(AUDIO_AC4)
501+
.setCodecs("ac-4.21.01.01") // AC4Profile21
502+
.setChannelCount(2)
503+
.setSampleRate(48000)
504+
.build();
505+
MediaCodecInfo codecInfo = buildAc4CodecInfo(formatAc4Profile21);
506+
507+
// Test non-Automotive case (FEATURE_AUTOMOTIVE is false)
508+
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, false);
509+
assertThat(codecInfo.isFormatSupported(context, formatAc4Profile21)).isTrue();
510+
511+
// Test Automotive case (FEATURE_AUTOMOTIVE is true)
512+
shadowPackageManager.setSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
513+
assertThat(codecInfo.isFormatSupported(context, formatAc4Profile21)).isTrue();
514+
}
515+
463516
private static MediaCodecInfo buildH264CodecInfo(boolean adaptive) {
464517
return new MediaCodecInfo(
465518
"h264",
@@ -505,12 +558,18 @@ private static MediaCodecInfo buildAacCodecInfo() {
505558
/* detachedSurfaceSupported= */ false);
506559
}
507560

508-
private static MediaCodecInfo buildAc4CodecInfo() {
561+
private static MediaCodecInfo buildAc4CodecInfo(Format ac4Format) {
562+
@SuppressWarnings("UnnecessarilyFullyQualified") // Unnecessary to import CodecCapabilities.
563+
android.media.MediaCodecInfo.CodecCapabilities capabilities =
564+
MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder()
565+
.setMediaFormat(MediaFormatUtil.createMediaFormatFromFormat(ac4Format))
566+
.build();
567+
509568
return new MediaCodecInfo(
510569
"ac4",
511570
AUDIO_AC4,
512571
AUDIO_AC4,
513-
/* capabilities= */ null,
572+
/* capabilities= */ capabilities,
514573
/* hardwareAccelerated= */ false,
515574
/* softwareOnly= */ true,
516575
/* vendor= */ true,

0 commit comments

Comments
 (0)