Skip to content

Commit 9ce1603

Browse files
ychaparovcopybara-github
authored andcommitted
Frame extractor: support custom GlObjectsProvider
Add Configuration.Builder.setGlObjectsProvider to accept custom GlObjectsProvider. PiperOrigin-RevId: 784490477
1 parent 7cdd4ea commit 9ce1603

File tree

2 files changed

+94
-4
lines changed

2 files changed

+94
-4
lines changed

libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameExtractorTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,16 @@
3333
import android.app.Instrumentation;
3434
import android.content.Context;
3535
import android.graphics.Bitmap;
36+
import androidx.media3.common.GlObjectsProvider;
37+
import androidx.media3.common.GlTextureInfo;
3638
import androidx.media3.common.MediaItem;
3739
import androidx.media3.common.PlaybackException;
3840
import androidx.media3.common.util.ConditionVariable;
3941
import androidx.media3.common.util.NullableType;
42+
import androidx.media3.effect.DefaultGlObjectsProvider;
43+
import androidx.media3.effect.GlEffect;
44+
import androidx.media3.effect.GlShaderProgram;
45+
import androidx.media3.effect.PassthroughShaderProgram;
4046
import androidx.media3.effect.Presentation;
4147
import androidx.media3.exoplayer.ExoPlaybackException;
4248
import androidx.media3.transformer.ExperimentalFrameExtractor.Frame;
@@ -525,4 +531,40 @@ public void extractFrame_oneFrame_decodesReferenceFramesOnly() throws Exception
525531
.skippedInputBufferCount)
526532
.isEqualTo(13);
527533
}
534+
535+
@Test
536+
public void extractFrame_withGlObjectsProvider_usesCustomObjectsProvider() throws Exception {
537+
GlObjectsProvider customObjectsProvider = new DefaultGlObjectsProvider();
538+
AtomicReference<GlObjectsProvider> glObjectsProviderUsedByEffects = new AtomicReference<>();
539+
frameExtractor =
540+
new ExperimentalFrameExtractor(
541+
context,
542+
new ExperimentalFrameExtractor.Configuration.Builder()
543+
.setGlObjectsProvider(customObjectsProvider)
544+
.build());
545+
frameExtractor.setMediaItem(
546+
MediaItem.fromUri(FILE_PATH),
547+
/* effects= */ ImmutableList.of(
548+
new GlEffect() {
549+
@Override
550+
public GlShaderProgram toGlShaderProgram(Context context, boolean useHdr) {
551+
return new PassthroughShaderProgram() {
552+
@Override
553+
public void queueInputFrame(
554+
GlObjectsProvider glObjectsProvider,
555+
GlTextureInfo inputTexture,
556+
long presentationTimeUs) {
557+
glObjectsProviderUsedByEffects.set(glObjectsProvider);
558+
super.queueInputFrame(glObjectsProvider, inputTexture, presentationTimeUs);
559+
}
560+
};
561+
}
562+
}));
563+
564+
ListenableFuture<Frame> frameFuture = frameExtractor.getFrame(/* positionMs= */ 8_500);
565+
Frame frame = frameFuture.get(TIMEOUT_SECONDS, SECONDS);
566+
567+
assertThat(frame.presentationTimeMs).isEqualTo(8_531);
568+
assertThat(glObjectsProviderUsedByEffects.get()).isEqualTo(customObjectsProvider);
569+
}
528570
}

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

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,22 @@
5858
import androidx.media3.common.PlaybackException;
5959
import androidx.media3.common.Player;
6060
import androidx.media3.common.VideoFrameProcessingException;
61+
import androidx.media3.common.VideoGraph;
6162
import androidx.media3.common.util.ConditionVariable;
6263
import androidx.media3.common.util.GlProgram;
6364
import androidx.media3.common.util.GlUtil;
6465
import androidx.media3.common.util.NullableType;
6566
import androidx.media3.common.util.UnstableApi;
6667
import androidx.media3.common.util.Util;
68+
import androidx.media3.effect.DefaultGlObjectsProvider;
69+
import androidx.media3.effect.DefaultVideoFrameProcessor;
6770
import androidx.media3.effect.GlEffect;
6871
import androidx.media3.effect.GlShaderProgram;
6972
import androidx.media3.effect.MatrixTransformation;
7073
import androidx.media3.effect.PassthroughShaderProgram;
7174
import androidx.media3.effect.RgbMatrix;
7275
import androidx.media3.effect.ScaleAndRotateTransformation;
76+
import androidx.media3.effect.SingleInputVideoGraph;
7377
import androidx.media3.exoplayer.DecoderCounters;
7478
import androidx.media3.exoplayer.DecoderReuseEvaluation;
7579
import androidx.media3.exoplayer.ExoPlaybackException;
@@ -83,6 +87,8 @@
8387
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
8488
import androidx.media3.exoplayer.source.MediaSource;
8589
import androidx.media3.exoplayer.video.MediaCodecVideoRenderer;
90+
import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper;
91+
import androidx.media3.exoplayer.video.VideoFrameReleaseControl;
8692
import androidx.media3.exoplayer.video.VideoRendererEventListener;
8793
import androidx.media3.extractor.DefaultExtractorsFactory;
8894
import com.google.common.collect.ImmutableList;
@@ -135,6 +141,7 @@ public static final class Builder {
135141
private SeekParameters seekParameters;
136142
private MediaCodecSelector mediaCodecSelector;
137143
private boolean extractHdrFrames;
144+
@Nullable private GlObjectsProvider glObjectsProvider;
138145

139146
/** Creates a new instance with default values. */
140147
public Builder() {
@@ -196,9 +203,24 @@ public Builder setExtractHdrFrames(boolean extractHdrFrames) {
196203
return this;
197204
}
198205

206+
/**
207+
* Sets the {@link GlObjectsProvider} to be used by the effect processing pipeline.
208+
*
209+
* <p>By default, a {@link DefaultGlObjectsProvider} is used.
210+
*
211+
* @param glObjectsProvider The {@link GlObjectsProvider}.
212+
* @return This builder.
213+
*/
214+
@CanIgnoreReturnValue
215+
public Builder setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
216+
this.glObjectsProvider = glObjectsProvider;
217+
return this;
218+
}
219+
199220
/** Builds a new {@link Configuration} instance. */
200221
public Configuration build() {
201-
return new Configuration(seekParameters, mediaCodecSelector, extractHdrFrames);
222+
return new Configuration(
223+
seekParameters, mediaCodecSelector, extractHdrFrames, glObjectsProvider);
202224
}
203225
}
204226

@@ -211,13 +233,18 @@ public Configuration build() {
211233
/** Whether extracting HDR frames is requested. */
212234
public final boolean extractHdrFrames;
213235

236+
/** The {@link GlObjectsProvider}. */
237+
@Nullable public final GlObjectsProvider glObjectsProvider;
238+
214239
private Configuration(
215240
SeekParameters seekParameters,
216241
MediaCodecSelector mediaCodecSelector,
217-
boolean extractHdrFrames) {
242+
boolean extractHdrFrames,
243+
@Nullable GlObjectsProvider glObjectsProvider) {
218244
this.seekParameters = seekParameters;
219245
this.mediaCodecSelector = mediaCodecSelector;
220246
this.extractHdrFrames = extractHdrFrames;
247+
this.glObjectsProvider = glObjectsProvider;
221248
}
222249
}
223250

@@ -294,7 +321,8 @@ public ExperimentalFrameExtractor(Context context, Configuration configuration)
294321
context,
295322
configuration.mediaCodecSelector,
296323
videoRendererEventListener,
297-
/* toneMapHdrToSdr= */ !configuration.extractHdrFrames)
324+
/* toneMapHdrToSdr= */ !configuration.extractHdrFrames,
325+
configuration.glObjectsProvider)
298326
},
299327
mediaSourceFactory)
300328
.setSeekParameters(configuration.seekParameters)
@@ -650,6 +678,7 @@ private void ensureConfigured(GlObjectsProvider glObjectsProvider, int width, in
650678
/** A custom MediaCodecVideoRenderer that renders only one frame per position reset. */
651679
private final class FrameExtractorRenderer extends MediaCodecVideoRenderer {
652680
private final boolean toneMapHdrToSdr;
681+
@Nullable private final GlObjectsProvider glObjectsProvider;
653682

654683
private boolean frameRenderedSinceLastPositionReset;
655684
private List<Effect> effectsFromPlayer;
@@ -659,7 +688,8 @@ public FrameExtractorRenderer(
659688
Context context,
660689
MediaCodecSelector mediaCodecSelector,
661690
VideoRendererEventListener videoRendererEventListener,
662-
boolean toneMapHdrToSdr) {
691+
boolean toneMapHdrToSdr,
692+
@Nullable GlObjectsProvider glObjectsProvider) {
663693
super(
664694
new Builder(context)
665695
.setMediaCodecSelector(mediaCodecSelector)
@@ -668,9 +698,27 @@ public FrameExtractorRenderer(
668698
.setEventListener(videoRendererEventListener)
669699
.setMaxDroppedFramesToNotify(0));
670700
this.toneMapHdrToSdr = toneMapHdrToSdr;
701+
this.glObjectsProvider = glObjectsProvider;
671702
effectsFromPlayer = ImmutableList.of();
672703
}
673704

705+
@Override
706+
protected PlaybackVideoGraphWrapper createPlaybackVideoGraphWrapper(
707+
Context context, VideoFrameReleaseControl videoFrameReleaseControl) {
708+
if (glObjectsProvider == null) {
709+
return super.createPlaybackVideoGraphWrapper(context, videoFrameReleaseControl);
710+
}
711+
DefaultVideoFrameProcessor.Factory.Builder videoFrameProcessorFactoryBuilder =
712+
new DefaultVideoFrameProcessor.Factory.Builder().setGlObjectsProvider(glObjectsProvider);
713+
VideoGraph.Factory videoGraphFactory =
714+
new SingleInputVideoGraph.Factory(videoFrameProcessorFactoryBuilder.build());
715+
return new PlaybackVideoGraphWrapper.Builder(context, videoFrameReleaseControl)
716+
.setEnablePlaylistMode(true)
717+
.setClock(getClock())
718+
.setVideoGraphFactory(videoGraphFactory)
719+
.build();
720+
}
721+
674722
@Override
675723
protected void onStreamChanged(
676724
Format[] formats,

0 commit comments

Comments
 (0)