Refactor frame rate notification to release control

Frame rate change is currently notified to the release control when the
video frame processor input frame rate changes, but it should be when
the video frame processor output frame rate changes.

PiperOrigin-RevId: 688512300
This commit is contained in:
kimvde 2024-10-22 05:41:55 -07:00 committed by Copybara-Service
parent 31ece8cbd2
commit 8260bb3d2e
6 changed files with 48 additions and 8 deletions

View File

@ -157,6 +157,14 @@ public interface VideoFrameProcessor {
*/
default void onOutputSizeChanged(int width, int height) {}
/**
* Called when the output frame rate changes.
*
* @param frameRate The output frame rate in frames per second, or {@link Format#NO_VALUE} if
* unknown.
*/
default void onOutputFrameRateChanged(float frameRate) {}
/**
* Called when an output frame with the given {@code presentationTimeUs} becomes available for
* rendering.

View File

@ -35,6 +35,14 @@ public interface VideoGraph {
*/
default void onOutputSizeChanged(int width, int height) {}
/**
* Called when the output frame rate changes.
*
* @param frameRate The output frame rate in frames per second, or {@link Format#NO_VALUE} if
* unknown.
*/
default void onOutputFrameRateChanged(float frameRate) {}
/**
* Called when an output frame with the given {@code framePresentationTimeUs} becomes available
* for rendering.

View File

@ -457,6 +457,8 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
private final List<GlShaderProgram> intermediateGlShaderPrograms;
private final ConditionVariable inputStreamRegisteredCondition;
private @MonotonicNonNull InputStreamInfo currentInputStreamInfo;
/**
* The input stream that is {@linkplain #registerInputStream registered}, but the pipeline has not
* adapted to processing it.
@ -515,7 +517,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
if (pendingInputStreamInfo != null) {
InputStreamInfo pendingInputStreamInfo = this.pendingInputStreamInfo;
videoFrameProcessingTaskExecutor.submit(
() -> configureEffects(pendingInputStreamInfo, /* forceReconfigure= */ false));
() -> configure(pendingInputStreamInfo, /* forceReconfigure= */ false));
this.pendingInputStreamInfo = null;
}
}
@ -659,7 +661,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
registeredFirstInputStream = true;
inputStreamRegisteredCondition.close();
videoFrameProcessingTaskExecutor.submit(
() -> configureEffects(pendingInputStreamInfo, /* forceReconfigure= */ true));
() -> configure(pendingInputStreamInfo, /* forceReconfigure= */ true));
} else {
// Rejects further inputs after signaling EOS and before the next input stream is fully
// configured.
@ -988,16 +990,17 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
}
/**
* Configures the {@link GlShaderProgram} instances for {@code effects}.
* Configures for a new input stream.
*
* <p>The pipeline will only re-configure if the {@link InputStreamInfo#effects new effects}
* doesn't match the {@link #activeEffects}, or when {@code forceReconfigure} is set to {@code
* true}.
* <p>The effect pipeline will only re-configure if the {@link InputStreamInfo#effects new
* effects} don't match the {@link #activeEffects}, or when {@code forceReconfigure} is set to
* {@code true}.
*/
private void configureEffects(InputStreamInfo inputStreamInfo, boolean forceReconfigure)
private void configure(InputStreamInfo inputStreamInfo, boolean forceReconfigure)
throws VideoFrameProcessingException {
checkColors(
/* inputColorInfo= */ checkNotNull(inputStreamInfo.format.colorInfo), outputColorInfo);
if (forceReconfigure || !activeEffects.equals(inputStreamInfo.effects)) {
if (!intermediateGlShaderPrograms.isEmpty()) {
for (int i = 0; i < intermediateGlShaderPrograms.size(); i++) {
@ -1035,10 +1038,17 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
onInputSurfaceReadyListener = null;
}
}
listenerExecutor.execute(
() ->
listener.onInputStreamRegistered(
inputStreamInfo.inputType, inputStreamInfo.format, inputStreamInfo.effects));
if (currentInputStreamInfo == null
|| inputStreamInfo.format.frameRate != currentInputStreamInfo.format.frameRate) {
listenerExecutor.execute(
() -> listener.onOutputFrameRateChanged(inputStreamInfo.format.frameRate));
}
this.currentInputStreamInfo = inputStreamInfo;
}
/** Checks that color configuration is valid for {@link DefaultVideoFrameProcessor}. */

View File

@ -176,6 +176,11 @@ public abstract class MultipleInputVideoGraph implements VideoGraph {
listenerExecutor.execute(() -> listener.onOutputSizeChanged(width, height));
}
@Override
public void onOutputFrameRateChanged(float frameRate) {
listenerExecutor.execute(() -> listener.onOutputFrameRateChanged(frameRate));
}
@Override
public void onOutputFrameAvailableForRendering(long presentationTimeUs) {
if (presentationTimeUs == 0) {

View File

@ -114,6 +114,11 @@ public abstract class SingleInputVideoGraph implements VideoGraph {
listenerExecutor.execute(() -> listener.onOutputSizeChanged(width, height));
}
@Override
public void onOutputFrameRateChanged(float frameRate) {
listenerExecutor.execute(() -> listener.onOutputFrameRateChanged(frameRate));
}
@Override
public void onOutputFrameAvailableForRendering(long presentationTimeUs) {
if (isEnded) {

View File

@ -339,6 +339,11 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
defaultVideoSink.onInputStreamChanged(INPUT_TYPE_SURFACE, format);
}
@Override
public void onOutputFrameRateChanged(float frameRate) {
videoFrameReleaseControl.setFrameRate(frameRate);
}
@Override
public void onOutputFrameAvailableForRendering(long framePresentationTimeUs) {
if (pendingFlushCount > 0) {
@ -596,7 +601,6 @@ public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, Video
default:
throw new UnsupportedOperationException("Unsupported input type " + inputType);
}
videoFrameReleaseControl.setFrameRate(format.frameRate);
this.inputType = inputType;
this.inputFormat = format;