Enable non-reference frame skipping in FrameExtractor

Speed up FrameExtractor for videos with non-ref frames

PiperOrigin-RevId: 715789816
This commit is contained in:
dancho 2025-01-15 07:24:48 -08:00 committed by Copybara-Service
parent c5feb28838
commit 0936b549ae
2 changed files with 33 additions and 1 deletions

View File

@ -21,7 +21,9 @@ import static androidx.media3.exoplayer.SeekParameters.CLOSEST_SYNC;
import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap; import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap;
import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap; import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
import static androidx.media3.test.utils.TestUtil.assertBitmapsAreSimilar; import static androidx.media3.test.utils.TestUtil.assertBitmapsAreSimilar;
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET;
import static androidx.media3.transformer.AndroidTestUtil.MP4_TRIM_OPTIMIZATION_270; import static androidx.media3.transformer.AndroidTestUtil.MP4_TRIM_OPTIMIZATION_270;
import static androidx.media3.transformer.AndroidTestUtil.assumeFormatsSupported;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
@ -473,4 +475,26 @@ public class FrameExtractorTest {
assertThat(frameSecondItem.presentationTimeMs).isEqualTo(8_531); assertThat(frameSecondItem.presentationTimeMs).isEqualTo(8_531);
assertBitmapsAreSimilar(expectedBitmapSecondItem, actualBitmapSecondItem, PSNR_THRESHOLD); assertBitmapsAreSimilar(expectedBitmapSecondItem, actualBitmapSecondItem, PSNR_THRESHOLD);
} }
@Test
public void extractFrame_oneFrame_decodesReferenceFramesOnly() throws Exception {
assumeFormatsSupported(
context, testId, /* inputFormat= */ MP4_ASSET.videoFormat, /* outputFormat= */ null);
frameExtractor =
new ExperimentalFrameExtractor(
context, new ExperimentalFrameExtractor.Configuration.Builder().build());
frameExtractor.setMediaItem(
MediaItem.fromUri(MP4_ASSET.uri), /* effects= */ ImmutableList.of());
ListenableFuture<Frame> frameFuture = frameExtractor.getFrame(/* positionMs= */ 967);
Frame frame = frameFuture.get(TIMEOUT_SECONDS, SECONDS);
assertThat(frame.presentationTimeMs).isEqualTo(967);
assertThat(
frameExtractor
.getDecoderCounters()
.get(TIMEOUT_SECONDS, SECONDS)
.skippedInputBufferCount)
.isEqualTo(13);
}
} }

View File

@ -49,6 +49,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.concurrent.futures.CallbackToFutureAdapter; import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.media3.common.C;
import androidx.media3.common.Effect; import androidx.media3.common.Effect;
import androidx.media3.common.Format; import androidx.media3.common.Format;
import androidx.media3.common.GlObjectsProvider; import androidx.media3.common.GlObjectsProvider;
@ -80,9 +81,11 @@ import androidx.media3.exoplayer.analytics.AnalyticsListener;
import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter; import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter;
import androidx.media3.exoplayer.mediacodec.MediaCodecInfo; import androidx.media3.exoplayer.mediacodec.MediaCodecInfo;
import androidx.media3.exoplayer.mediacodec.MediaCodecSelector; import androidx.media3.exoplayer.mediacodec.MediaCodecSelector;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.video.MediaCodecVideoRenderer; import androidx.media3.exoplayer.video.MediaCodecVideoRenderer;
import androidx.media3.exoplayer.video.VideoRendererEventListener; import androidx.media3.exoplayer.video.VideoRendererEventListener;
import androidx.media3.extractor.DefaultExtractorsFactory;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
@ -273,6 +276,10 @@ public final class ExperimentalFrameExtractor {
* @param configuration The {@link Configuration} for this frame extractor. * @param configuration The {@link Configuration} for this frame extractor.
*/ */
public ExperimentalFrameExtractor(Context context, Configuration configuration) { public ExperimentalFrameExtractor(Context context, Configuration configuration) {
MediaSource.Factory mediaSourceFactory =
new DefaultMediaSourceFactory(context, new DefaultExtractorsFactory())
.experimentalSetCodecsToParseWithinGopSampleDependencies(
C.VIDEO_CODEC_FLAG_H264 | C.VIDEO_CODEC_FLAG_H265);
player = player =
new ExoPlayer.Builder( new ExoPlayer.Builder(
context, context,
@ -287,7 +294,8 @@ public final class ExperimentalFrameExtractor {
configuration.mediaCodecSelector, configuration.mediaCodecSelector,
videoRendererEventListener, videoRendererEventListener,
/* toneMapHdrToSdr= */ !configuration.extractHdrFrames) /* toneMapHdrToSdr= */ !configuration.extractHdrFrames)
}) },
mediaSourceFactory)
.setSeekParameters(configuration.seekParameters) .setSeekParameters(configuration.seekParameters)
.build(); .build();
player.addAnalyticsListener(new PlayerListener()); player.addAnalyticsListener(new PlayerListener());