Skip to content

Commit

Permalink
Create VideoFrameRenderControl
Browse files Browse the repository at this point in the history
Split CompositingVideoSinkProvider.VideoSinkImpl in two classes:
- VideoSinkImpl now only receives input from MediaCodecVideoRenderer and
  forwards frames to its connected VideoFrameProcessor
- VideoFrameRenderControl takes composited frames out of the VideoGraph
  and schedules the rendering of those.
- CompositingVideoSinkProvider connects VideoSinkImpl with
  VideoFramesRenderer.

PiperOrigin-RevId: 584605078
  • Loading branch information
christosts authored and copybara-github committed Nov 22, 2023
1 parent a063d13 commit 6435ddb
Show file tree
Hide file tree
Showing 9 changed files with 987 additions and 363 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,22 @@ PreviewingVideoGraph create(
long initialTimestampOffsetUs)
throws VideoFrameProcessingException;
}

/**
* Renders the oldest unrendered output frame that has become {@linkplain
* Listener#onOutputFrameAvailableForRendering(long) available for rendering} at the given {@code
* renderTimeNs}.
*
* <p>This will either render the output frame to the {@linkplain #setOutputSurfaceInfo output
* surface}, or drop the frame, per {@code renderTimeNs}.
*
* <p>The {@code renderTimeNs} may be passed to {@link
* android.opengl.EGLExt#eglPresentationTimeANDROID} depending on the implementation.
*
* @param renderTimeNs The render time to use for the frame, in nanoseconds. The render time can
* be before or after the current system time. Use {@link
* VideoFrameProcessor#DROP_OUTPUT_FRAME} to drop the frame, or {@link
* VideoFrameProcessor#RENDER_OUTPUT_FRAME_IMMEDIATELY} to render the frame immediately.
*/
void renderOutputFrame(long renderTimeNs);
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,9 @@ private PreviewingSingleInputVideoGraph(
presentation,
initialTimestampOffsetUs);
}

@Override
public void renderOutputFrame(long renderTimeNs) {
getProcessor(SINGLE_INPUT_INDEX).renderOutputFrame(renderTimeNs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import androidx.media3.common.VideoFrameProcessor;
import androidx.media3.common.VideoGraph;
import androidx.media3.common.util.UnstableApi;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.List;
import java.util.concurrent.Executor;

Expand All @@ -54,6 +55,7 @@ public abstract class SingleInputVideoGraph implements VideoGraph {
@Nullable private final Presentation presentation;

@Nullable private VideoFrameProcessor videoFrameProcessor;
private boolean isEnded;

private boolean released;
private volatile boolean hasProducedFrameWithTimestampZero;
Expand Down Expand Up @@ -112,7 +114,7 @@ public int registerInput() throws VideoFrameProcessingException {
inputColorInfo,
outputColorInfo,
renderFramesAutomatically,
listenerExecutor,
/* listenerExecutor= */ MoreExecutors.directExecutor(),
new VideoFrameProcessor.Listener() {
private long lastProcessedFramePresentationTimeUs;

Expand All @@ -129,6 +131,12 @@ public void onOutputSizeChanged(int width, int height) {

@Override
public void onOutputFrameAvailableForRendering(long presentationTimeUs) {
if (isEnded) {
onError(
new VideoFrameProcessingException(
"onOutputFrameAvailableForRendering() received after onEnded()"));
return;
}
// Frames are rendered automatically.
if (presentationTimeUs == 0) {
hasProducedFrameWithTimestampZero = true;
Expand All @@ -145,7 +153,13 @@ public void onError(VideoFrameProcessingException exception) {

@Override
public void onEnded() {
listener.onEnded(lastProcessedFramePresentationTimeUs);
if (isEnded) {
onError(new VideoFrameProcessingException("onEnded() received multiple times"));
return;
}
isEnded = true;
listenerExecutor.execute(
() -> listener.onEnded(lastProcessedFramePresentationTimeUs));
}
});
return SINGLE_INPUT_INDEX;
Expand Down
Loading

0 comments on commit 6435ddb

Please sign in to comment.