diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/EncoderUtil.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/EncoderUtil.java index ee422166cd..0218c199fb 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/EncoderUtil.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/EncoderUtil.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer2.transformer; -import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static java.lang.Math.max; import static java.lang.Math.round; @@ -29,8 +28,10 @@ import android.util.Pair; import android.util.Range; import android.util.Size; import androidx.annotation.DoNotInline; +import androidx.annotation.GuardedBy; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C.ColorTransfer; import com.google.android.exoplayer2.Format; @@ -39,10 +40,8 @@ import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.ColorInfo; import com.google.common.base.Ascii; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Ints; @@ -52,20 +51,29 @@ public final class EncoderUtil { /** A value to indicate the encoding level is not set. */ public static final int LEVEL_UNSET = Format.NO_VALUE; - private static final Supplier> - MIME_TYPE_TO_ENCODERS = Suppliers.memoize(EncoderUtil::populateEncoderInfos); + @GuardedBy("EncoderUtil.class") + private static final ArrayListMultimap mimeTypeToEncoders = + ArrayListMultimap.create(); /** * Returns a list of {@linkplain MediaCodecInfo encoders} that support the given {@code mimeType}, * or an empty list if there is none. */ - public static ImmutableList getSupportedEncoders(String mimeType) { - return checkNotNull(MIME_TYPE_TO_ENCODERS.get()).get(Ascii.toLowerCase(mimeType)); + public static synchronized ImmutableList getSupportedEncoders(String mimeType) { + maybePopulateEncoderInfo(); + return ImmutableList.copyOf(mimeTypeToEncoders.get(Ascii.toLowerCase(mimeType))); } /** Returns a list of video {@linkplain MimeTypes MIME types} that can be encoded. */ - public static ImmutableSet getSupportedVideoMimeTypes() { - return checkNotNull(MIME_TYPE_TO_ENCODERS.get()).keySet(); + public static synchronized ImmutableSet getSupportedVideoMimeTypes() { + maybePopulateEncoderInfo(); + return ImmutableSet.copyOf(mimeTypeToEncoders.keySet()); + } + + /** Clears the cached list of encoders. */ + @VisibleForTesting + public static synchronized void clearCachedEncoders() { + mimeTypeToEncoders.clear(); } /** @@ -431,9 +439,10 @@ public final class EncoderUtil { : alignment * Math.round((float) size / alignment); } - private static ImmutableListMultimap populateEncoderInfos() { - ImmutableListMultimap.Builder encoderInfosBuilder = - new ImmutableListMultimap.Builder<>(); + private static synchronized void maybePopulateEncoderInfo() { + if (!mimeTypeToEncoders.isEmpty()) { + return; + } MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); MediaCodecInfo[] allCodecInfos = mediaCodecList.getCodecInfos(); @@ -444,10 +453,9 @@ public final class EncoderUtil { } String[] supportedMimeTypes = mediaCodecInfo.getSupportedTypes(); for (String mimeType : supportedMimeTypes) { - encoderInfosBuilder.put(Ascii.toLowerCase(mimeType), mediaCodecInfo); + mimeTypeToEncoders.put(Ascii.toLowerCase(mimeType), mediaCodecInfo); } } - return encoderInfosBuilder.build(); } @RequiresApi(29) diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactoryTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactoryTest.java index d280d94544..68c80b359e 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactoryTest.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactoryTest.java @@ -28,11 +28,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.util.MimeTypes; import com.google.common.collect.ImmutableList; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; import org.robolectric.shadows.MediaCodecInfoBuilder; +import org.robolectric.shadows.ShadowMediaCodec; import org.robolectric.shadows.ShadowMediaCodecList; /** Unit test for {@link DefaultEncoderFactory}. */ @@ -45,6 +47,13 @@ public class DefaultEncoderFactoryTest { createShadowH264Encoder(); } + @After + public void tearDown() { + ShadowMediaCodec.clearCodecs(); + ShadowMediaCodecList.reset(); + EncoderUtil.clearCachedEncoders(); + } + private static void createShadowH264Encoder() { MediaFormat avcFormat = new MediaFormat(); avcFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC); diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/EncoderUtilTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/EncoderUtilTest.java index 56eb3e8ef1..5304c4f409 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/EncoderUtilTest.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/EncoderUtilTest.java @@ -27,11 +27,13 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.video.ColorInfo; import com.google.common.collect.ImmutableList; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; import org.robolectric.shadows.MediaCodecInfoBuilder; +import org.robolectric.shadows.ShadowMediaCodec; import org.robolectric.shadows.ShadowMediaCodecList; /** Unit test for {@link EncoderUtil}. */ @@ -64,6 +66,13 @@ public class EncoderUtilTest { .build()); } + @After + public void tearDown() { + ShadowMediaCodecList.reset(); + ShadowMediaCodec.clearCodecs(); + EncoderUtil.clearCachedEncoders(); + } + @Test public void getSupportedResolution_withSupportedResolution_succeeds() { ImmutableList supportedEncoders = EncoderUtil.getSupportedEncoders(MIME_TYPE); diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java index 0a16ff5b61..7b9ce8c343 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java @@ -898,6 +898,8 @@ public final class TransformerEndToEndTest { private static void removeEncodersAndDecoders() { ShadowMediaCodec.clearCodecs(); + ShadowMediaCodecList.reset(); + EncoderUtil.clearCachedEncoders(); } private static String getDumpFileName(String originalFileName) { diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/VideoEncoderWrapperTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/VideoEncoderWrapperTest.java index f187aa370c..58a10c6380 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/VideoEncoderWrapperTest.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/VideoEncoderWrapperTest.java @@ -32,10 +32,12 @@ import com.google.android.exoplayer2.util.ListenerSet; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.SurfaceInfo; import com.google.common.collect.ImmutableList; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.shadows.MediaCodecInfoBuilder; +import org.robolectric.shadows.ShadowMediaCodec; import org.robolectric.shadows.ShadowMediaCodecList; /** Unit tests for {@link VideoTranscodingSamplePipeline.EncoderWrapper}. */ @@ -63,6 +65,13 @@ public final class VideoEncoderWrapperTest { createShadowH264Encoder(); } + @After + public void tearDown() { + ShadowMediaCodec.clearCodecs(); + ShadowMediaCodecList.reset(); + EncoderUtil.clearCachedEncoders(); + } + @Test public void getSurfaceInfo_landscape_leavesOrientationUnchanged() throws Exception { int inputWidth = 200;