Make sure effects are applied on the correct frame
The events happens in the following order, assuming two media items: 1. First media item is fully decoded, record the last frame's pts - Note frame processing is still ongoing for this media item 2. Renderer sends `onOutputFormatChanged()` to signal the second media item 3. **Block sending the frames of the second media item to the `VideoSink`** 4. Frame processing finishes on the first media item 5. The last frame of the first media item is released 6. **Reconfigure the `VideoSink` to apply new effects** 7. **Start sending the frames of the second media item to the `VideoSink`** This CL implements the **events in bold** PiperOrigin-RevId: 576098798
This commit is contained in:
parent
e8adbd9075
commit
1fc34676d9
@ -215,6 +215,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
// TODO b/292111083 - Remove the field and trigger the callback on every video size change.
|
// TODO b/292111083 - Remove the field and trigger the callback on every video size change.
|
||||||
private boolean onVideoSizeChangedCalled;
|
private boolean onVideoSizeChangedCalled;
|
||||||
|
private boolean hasRegisteredFirstInputStream;
|
||||||
|
private boolean inputStreamRegistrationPending;
|
||||||
|
private long lastFramePresentationTimeUs;
|
||||||
|
|
||||||
/** Creates a new instance. */
|
/** Creates a new instance. */
|
||||||
public VideoSinkImpl(
|
public VideoSinkImpl(
|
||||||
@ -283,6 +286,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
Util.SDK_INT < 21 && sourceFormat.rotationDegrees != 0
|
Util.SDK_INT < 21 && sourceFormat.rotationDegrees != 0
|
||||||
? ScaleAndRotateAccessor.createRotationEffect(sourceFormat.rotationDegrees)
|
? ScaleAndRotateAccessor.createRotationEffect(sourceFormat.rotationDegrees)
|
||||||
: null;
|
: null;
|
||||||
|
lastFramePresentationTimeUs = C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
// VideoSink impl
|
// VideoSink impl
|
||||||
@ -294,6 +298,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
streamOffsets.clear();
|
streamOffsets.clear();
|
||||||
handler.removeCallbacksAndMessages(/* token= */ null);
|
handler.removeCallbacksAndMessages(/* token= */ null);
|
||||||
renderedFirstFrame = false;
|
renderedFirstFrame = false;
|
||||||
|
lastFramePresentationTimeUs = C.TIME_UNSET;
|
||||||
|
hasRegisteredFirstInputStream = false;
|
||||||
if (registeredLastFrame) {
|
if (registeredLastFrame) {
|
||||||
registeredLastFrame = false;
|
registeredLastFrame = false;
|
||||||
processedLastFrame = false;
|
processedLastFrame = false;
|
||||||
@ -317,7 +323,17 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
throw new UnsupportedOperationException("Unsupported input type " + inputType);
|
throw new UnsupportedOperationException("Unsupported input type " + inputType);
|
||||||
}
|
}
|
||||||
this.inputFormat = format;
|
this.inputFormat = format;
|
||||||
maybeRegisterInputStream();
|
|
||||||
|
if (!hasRegisteredFirstInputStream) {
|
||||||
|
maybeRegisterInputStream();
|
||||||
|
hasRegisteredFirstInputStream = true;
|
||||||
|
// If an input stream registration is pending and seek to another MediaItem, execution
|
||||||
|
// reaches here before registerInputFrame(), resetting inputStreamRegistrationPending to
|
||||||
|
// avoid registering the same input stream again in registerInputFrame().
|
||||||
|
inputStreamRegistrationPending = false;
|
||||||
|
} else {
|
||||||
|
inputStreamRegistrationPending = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (registeredLastFrame) {
|
if (registeredLastFrame) {
|
||||||
registeredLastFrame = false;
|
registeredLastFrame = false;
|
||||||
@ -349,6 +365,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@Override
|
@Override
|
||||||
public long registerInputFrame(long framePresentationTimeUs, boolean isLastFrame) {
|
public long registerInputFrame(long framePresentationTimeUs, boolean isLastFrame) {
|
||||||
checkState(videoFrameProcessorMaxPendingFrameCount != C.LENGTH_UNSET);
|
checkState(videoFrameProcessorMaxPendingFrameCount != C.LENGTH_UNSET);
|
||||||
|
|
||||||
|
// An input stream is fully decoded, wait until all of its frames are released before queueing
|
||||||
|
// input frame from the next input stream.
|
||||||
|
if (inputStreamRegistrationPending) {
|
||||||
|
if (lastFramePresentationTimeUs == C.TIME_UNSET) {
|
||||||
|
// A seek took place after signaling a new input stream, but the input stream is yet to be
|
||||||
|
// registered.
|
||||||
|
maybeRegisterInputStream();
|
||||||
|
inputStreamRegistrationPending = false;
|
||||||
|
} else {
|
||||||
|
return C.TIME_UNSET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (videoFrameProcessor.getPendingInputFrameCount()
|
if (videoFrameProcessor.getPendingInputFrameCount()
|
||||||
>= videoFrameProcessorMaxPendingFrameCount) {
|
>= videoFrameProcessorMaxPendingFrameCount) {
|
||||||
return C.TIME_UNSET;
|
return C.TIME_UNSET;
|
||||||
@ -356,6 +386,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
if (!videoFrameProcessor.registerInputFrame()) {
|
if (!videoFrameProcessor.registerInputFrame()) {
|
||||||
return C.TIME_UNSET;
|
return C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
lastFramePresentationTimeUs = framePresentationTimeUs;
|
||||||
// The sink takes in frames with monotonically increasing, non-offset frame
|
// The sink takes in frames with monotonically increasing, non-offset frame
|
||||||
// timestamps. That is, with two ten-second long videos, the first frame of the second video
|
// timestamps. That is, with two ten-second long videos, the first frame of the second video
|
||||||
// should bear a timestamp of 10s seen from VideoFrameProcessor; while in ExoPlayer, the
|
// should bear a timestamp of 10s seen from VideoFrameProcessor; while in ExoPlayer, the
|
||||||
@ -416,6 +447,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
: frameRenderTimeNs,
|
: frameRenderTimeNs,
|
||||||
isLastFrame);
|
isLastFrame);
|
||||||
|
|
||||||
|
if (framePresentationTimeUs == lastFramePresentationTimeUs
|
||||||
|
&& inputStreamRegistrationPending) {
|
||||||
|
maybeRegisterInputStream();
|
||||||
|
inputStreamRegistrationPending = false;
|
||||||
|
}
|
||||||
|
|
||||||
maybeNotifyVideoSizeChanged(bufferPresentationTimeUs);
|
maybeNotifyVideoSizeChanged(bufferPresentationTimeUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user