mirror of
https://github.com/androidx/media.git
synced 2025-05-05 06:30:24 +08:00
Add FrameProcessor functionality to release a frame immediately
Currently `FrameProcessor.releaseOutputFrame()` method supports Release at a specific system time Drops the frame This API is not that convenient to use when the caller wants to release a frame, now, regardless of the release time. A use case is to release (present) a frame when no frame is shown for a while, and it's thus better to just release the frame, now. Currently if MCVR wants a frame to be rendered now, MCVR calls release frame with a set offset like 10us: `releaseOutputFrame(System.nanoTime() + 10_000)`. The 10us offset is to prevent the frame processor dropping the frame, due to thread hopping delays. To make the API better usable, consider adding a mode for releasing the frame now, like (bold marks the new mode) - Use C.TIME_UNSET to drop - **Use -1 to release the frame immediately, or** - Use an actual release time. PiperOrigin-RevId: 479044215
This commit is contained in:
parent
20c1ae1411
commit
ff8dd0b4b9
@ -135,6 +135,29 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
|||||||
assertThat(outputReleaseTimesNs).containsExactly(releaseTimesNs);
|
assertThat(outputReleaseTimesNs).containsExactly(releaseTimesNs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void controlledFrameRelease_withOneFrameRequestImmediateRelease_releasesFrame()
|
||||||
|
throws Exception {
|
||||||
|
long originalPresentationTimeUs = 1234;
|
||||||
|
long releaseTimesNs = FrameProcessor.RELEASE_OUTPUT_FRAME_IMMEDIATELY;
|
||||||
|
AtomicLong actualPresentationTimeUs = new AtomicLong();
|
||||||
|
setupGlEffectsFrameProcessorWithBlankFrameProducer(
|
||||||
|
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
|
||||||
|
/* onFrameAvailableListener= */ presentationTimeUs -> {
|
||||||
|
actualPresentationTimeUs.set(presentationTimeUs);
|
||||||
|
checkNotNull(glEffectsFrameProcessor).releaseOutputFrame(releaseTimesNs);
|
||||||
|
},
|
||||||
|
/* releaseFramesAutomatically= */ false);
|
||||||
|
|
||||||
|
checkNotNull(produceBlankFramesTask).run();
|
||||||
|
Thread.sleep(FRAME_PROCESSING_WAIT_MS);
|
||||||
|
|
||||||
|
assertThat(frameProcessingException.get()).isNull();
|
||||||
|
assertThat(actualPresentationTimeUs.get()).isEqualTo(originalPresentationTimeUs);
|
||||||
|
// The actual release time is determined by the FrameProcessor when releasing the frame.
|
||||||
|
assertThat(outputReleaseTimesNs).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void controlledFrameRelease_withLateFrame_dropsFrame() throws Exception {
|
public void controlledFrameRelease_withLateFrame_dropsFrame() throws Exception {
|
||||||
long originalPresentationTimeUs = 1234;
|
long originalPresentationTimeUs = 1234;
|
||||||
|
@ -170,12 +170,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
public void releaseOutputFrame(long releaseTimeNs) {
|
public void releaseOutputFrame(long releaseTimeNs) {
|
||||||
checkState(!releaseFramesAutomatically);
|
checkState(!releaseFramesAutomatically);
|
||||||
|
|
||||||
|
boolean dropLateFrame = true;
|
||||||
|
if (releaseTimeNs == FrameProcessor.RELEASE_OUTPUT_FRAME_IMMEDIATELY) {
|
||||||
|
dropLateFrame = false;
|
||||||
|
releaseTimeNs = System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
Pair<TextureInfo, Long> oldestAvailableFrame = availableFrames.remove();
|
Pair<TextureInfo, Long> oldestAvailableFrame = availableFrames.remove();
|
||||||
renderFrameToSurfaces(
|
renderFrameToSurfaces(
|
||||||
/* inputTexture= */ oldestAvailableFrame.first,
|
/* inputTexture= */ oldestAvailableFrame.first,
|
||||||
/* presentationTimeUs= */ oldestAvailableFrame.second,
|
/* presentationTimeUs= */ oldestAvailableFrame.second,
|
||||||
releaseTimeNs,
|
releaseTimeNs,
|
||||||
/* dropLateFrame= */ true);
|
dropLateFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -101,6 +101,12 @@ public interface FrameProcessor {
|
|||||||
void onFrameProcessingEnded();
|
void onFrameProcessingEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the frame should be released immediately after {@link #releaseOutputFrame(long)} is
|
||||||
|
* invoked.
|
||||||
|
*/
|
||||||
|
long RELEASE_OUTPUT_FRAME_IMMEDIATELY = -1;
|
||||||
|
|
||||||
/** Returns the input {@link Surface}, where {@link FrameProcessor} consumes input frames from. */
|
/** Returns the input {@link Surface}, where {@link FrameProcessor} consumes input frames from. */
|
||||||
Surface getInputSurface();
|
Surface getInputSurface();
|
||||||
|
|
||||||
@ -164,8 +170,9 @@ public interface FrameProcessor {
|
|||||||
* {@linkplain Listener#onOutputFrameAvailable(long) available}.
|
* {@linkplain Listener#onOutputFrameAvailable(long) available}.
|
||||||
*
|
*
|
||||||
* @param releaseTimeNs The release time to use for the frame, in nanoseconds. Use {@link
|
* @param releaseTimeNs The release time to use for the frame, in nanoseconds. Use {@link
|
||||||
* C#TIME_UNSET} to drop the frame. If {@code releaseTimeNs} is after {@link
|
* C#TIME_UNSET} to drop the frame, or {@link #RELEASE_OUTPUT_FRAME_IMMEDIATELY} to release
|
||||||
* System#nanoTime()} at the time of the release, the frame is also dropped.
|
* the frame immediately. If {@code releaseTimeNs} is after {@link System#nanoTime()} at the
|
||||||
|
* time of the release, the frame is also dropped.
|
||||||
*/
|
*/
|
||||||
void releaseOutputFrame(long releaseTimeNs);
|
void releaseOutputFrame(long releaseTimeNs);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user