Set clock on VideoSink
PiperOrigin-RevId: 628083794
This commit is contained in:
parent
c87940eb27
commit
a6eef7b8d8
@ -201,7 +201,7 @@ public final class CompositingVideoSinkProvider
|
|||||||
private final PreviewingVideoGraph.Factory previewingVideoGraphFactory;
|
private final PreviewingVideoGraph.Factory previewingVideoGraphFactory;
|
||||||
private final CopyOnWriteArraySet<CompositingVideoSinkProvider.Listener> listeners;
|
private final CopyOnWriteArraySet<CompositingVideoSinkProvider.Listener> listeners;
|
||||||
|
|
||||||
private Clock clock;
|
private @MonotonicNonNull Clock clock;
|
||||||
private @MonotonicNonNull VideoFrameReleaseControl videoFrameReleaseControl;
|
private @MonotonicNonNull VideoFrameReleaseControl videoFrameReleaseControl;
|
||||||
private @MonotonicNonNull VideoFrameRenderControl videoFrameRenderControl;
|
private @MonotonicNonNull VideoFrameRenderControl videoFrameRenderControl;
|
||||||
private @MonotonicNonNull Format outputFormat;
|
private @MonotonicNonNull Format outputFormat;
|
||||||
@ -218,7 +218,6 @@ public final class CompositingVideoSinkProvider
|
|||||||
videoSinkImpl = new VideoSinkImpl(context);
|
videoSinkImpl = new VideoSinkImpl(context);
|
||||||
previewingVideoGraphFactory = checkStateNotNull(builder.previewingVideoGraphFactory);
|
previewingVideoGraphFactory = checkStateNotNull(builder.previewingVideoGraphFactory);
|
||||||
listeners = new CopyOnWriteArraySet<>();
|
listeners = new CopyOnWriteArraySet<>();
|
||||||
clock = Clock.DEFAULT;
|
|
||||||
state = STATE_CREATED;
|
state = STATE_CREATED;
|
||||||
playbackSpeed = 1f;
|
playbackSpeed = 1f;
|
||||||
addListener(videoSinkImpl);
|
addListener(videoSinkImpl);
|
||||||
@ -259,12 +258,6 @@ public final class CompositingVideoSinkProvider
|
|||||||
return videoFrameReleaseControl;
|
return videoFrameReleaseControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setClock(Clock clock) {
|
|
||||||
checkState(!isInitialized());
|
|
||||||
this.clock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVideoEffects(List<Effect> videoEffects) {
|
public void setVideoEffects(List<Effect> videoEffects) {
|
||||||
videoSinkImpl.setVideoEffects(videoEffects);
|
videoSinkImpl.setVideoEffects(videoEffects);
|
||||||
@ -382,7 +375,7 @@ public final class CompositingVideoSinkProvider
|
|||||||
Format format = outputFormat == null ? new Format.Builder().build() : outputFormat;
|
Format format = outputFormat == null ? new Format.Builder().build() : outputFormat;
|
||||||
videoFrameMetadataListener.onVideoFrameAboutToBeRendered(
|
videoFrameMetadataListener.onVideoFrameAboutToBeRendered(
|
||||||
/* presentationTimeUs= */ presentationTimeUs - streamOffsetUs,
|
/* presentationTimeUs= */ presentationTimeUs - streamOffsetUs,
|
||||||
clock.nanoTime(),
|
checkStateNotNull(clock).nanoTime(),
|
||||||
format,
|
format,
|
||||||
/* mediaFormat= */ null);
|
/* mediaFormat= */ null);
|
||||||
}
|
}
|
||||||
@ -424,11 +417,12 @@ public final class CompositingVideoSinkProvider
|
|||||||
|
|
||||||
// Internal methods
|
// Internal methods
|
||||||
|
|
||||||
private VideoFrameProcessor initialize(Format sourceFormat) throws VideoSink.VideoSinkException {
|
private VideoFrameProcessor initialize(Format sourceFormat, Clock clock)
|
||||||
|
throws VideoSink.VideoSinkException {
|
||||||
checkState(state == STATE_CREATED);
|
checkState(state == STATE_CREATED);
|
||||||
checkState(videoFrameRenderControl != null && videoFrameReleaseControl != null);
|
checkState(videoFrameRenderControl != null && videoFrameReleaseControl != null);
|
||||||
|
|
||||||
// Lazily initialize the handler here so it's initialized on the playback looper.
|
this.clock = clock;
|
||||||
handler = clock.createHandler(checkStateNotNull(Looper.myLooper()), /* callback= */ null);
|
handler = clock.createHandler(checkStateNotNull(Looper.myLooper()), /* callback= */ null);
|
||||||
|
|
||||||
ColorInfo inputColorInfo = getAdjustedInputColorInfo(sourceFormat.colorInfo);
|
ColorInfo inputColorInfo = getAdjustedInputColorInfo(sourceFormat.colorInfo);
|
||||||
@ -582,9 +576,9 @@ public final class CompositingVideoSinkProvider
|
|||||||
// VideoSink impl
|
// VideoSink impl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(Format sourceFormat) throws VideoSinkException {
|
public void initialize(Format sourceFormat, Clock clock) throws VideoSinkException {
|
||||||
checkState(!isInitialized());
|
checkState(!isInitialized());
|
||||||
videoFrameProcessor = CompositingVideoSinkProvider.this.initialize(sourceFormat);
|
videoFrameProcessor = CompositingVideoSinkProvider.this.initialize(sourceFormat, clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -53,7 +53,6 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
import androidx.media3.common.util.Clock;
|
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.MediaFormatUtil;
|
import androidx.media3.common.util.MediaFormatUtil;
|
||||||
import androidx.media3.common.util.Size;
|
import androidx.media3.common.util.Size;
|
||||||
@ -622,9 +621,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
@Override
|
@Override
|
||||||
protected void onInit() {
|
protected void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
Clock clock = getClock();
|
videoFrameReleaseControl.setClock(getClock());
|
||||||
videoFrameReleaseControl.setClock(clock);
|
|
||||||
videoSinkProvider.setClock(clock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1073,7 +1070,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
videoSink = videoSinkProvider.getSink();
|
videoSink = videoSinkProvider.getSink();
|
||||||
if (!videoSink.isInitialized()) {
|
if (!videoSink.isInitialized()) {
|
||||||
try {
|
try {
|
||||||
videoSink.initialize(format);
|
videoSink.initialize(format, getClock());
|
||||||
} catch (VideoSink.VideoSinkException e) {
|
} catch (VideoSink.VideoSinkException e) {
|
||||||
throw createRendererException(
|
throw createRendererException(
|
||||||
e, format, PlaybackException.ERROR_CODE_VIDEO_FRAME_PROCESSOR_INIT_FAILED);
|
e, format, PlaybackException.ERROR_CODE_VIDEO_FRAME_PROCESSOR_INIT_FAILED);
|
||||||
|
@ -24,6 +24,7 @@ import androidx.annotation.IntDef;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.common.util.TimestampIterator;
|
import androidx.media3.common.util.TimestampIterator;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
@ -112,11 +113,12 @@ public interface VideoSink {
|
|||||||
* Initializes the video sink.
|
* Initializes the video sink.
|
||||||
*
|
*
|
||||||
* @param sourceFormat The format of the compressed video.
|
* @param sourceFormat The format of the compressed video.
|
||||||
|
* @param clock The {@link Clock} that should be used.
|
||||||
* @throws VideoSink.VideoSinkException If initializing the sink failed.
|
* @throws VideoSink.VideoSinkException If initializing the sink failed.
|
||||||
*/
|
*/
|
||||||
void initialize(Format sourceFormat) throws VideoSinkException;
|
void initialize(Format sourceFormat, Clock clock) throws VideoSinkException;
|
||||||
|
|
||||||
/** Returns whether the video sink is {@linkplain #initialize(Format) initialized}. */
|
/** Returns whether the video sink is {@linkplain #initialize(Format, Clock) initialized}. */
|
||||||
boolean isInitialized();
|
boolean isInitialized();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,7 +148,7 @@ public interface VideoSink {
|
|||||||
/**
|
/**
|
||||||
* Returns the input {@link Surface} where the video sink consumes input frames from.
|
* Returns the input {@link Surface} where the video sink consumes input frames from.
|
||||||
*
|
*
|
||||||
* <p>Must be called after the sink is {@linkplain #initialize(Format) initialized}.
|
* <p>Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}.
|
||||||
*/
|
*/
|
||||||
Surface getInputSurface();
|
Surface getInputSurface();
|
||||||
|
|
||||||
@ -159,7 +161,7 @@ public interface VideoSink {
|
|||||||
/**
|
/**
|
||||||
* Informs the video sink that a new input stream will be queued.
|
* Informs the video sink that a new input stream will be queued.
|
||||||
*
|
*
|
||||||
* <p>Must be called after the sink is {@linkplain #initialize(Format) initialized}.
|
* <p>Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}.
|
||||||
*
|
*
|
||||||
* @param inputType The {@link InputType} of the stream.
|
* @param inputType The {@link InputType} of the stream.
|
||||||
* @param format The {@link Format} of the stream.
|
* @param format The {@link Format} of the stream.
|
||||||
@ -170,7 +172,7 @@ public interface VideoSink {
|
|||||||
* Informs the video sink that a frame will be queued to its {@linkplain #getInputSurface() input
|
* Informs the video sink that a frame will be queued to its {@linkplain #getInputSurface() input
|
||||||
* surface}.
|
* surface}.
|
||||||
*
|
*
|
||||||
* <p>Must be called after the sink is {@linkplain #initialize(Format) initialized}.
|
* <p>Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}.
|
||||||
*
|
*
|
||||||
* @param framePresentationTimeUs The frame's presentation time, in microseconds.
|
* @param framePresentationTimeUs The frame's presentation time, in microseconds.
|
||||||
* @param isLastFrame Whether this is the last frame of the video stream.
|
* @param isLastFrame Whether this is the last frame of the video stream.
|
||||||
@ -183,7 +185,7 @@ public interface VideoSink {
|
|||||||
/**
|
/**
|
||||||
* Provides an input {@link Bitmap} to the video sink.
|
* Provides an input {@link Bitmap} to the video sink.
|
||||||
*
|
*
|
||||||
* <p>Must be called after the sink is {@linkplain #initialize(Format) initialized}.
|
* <p>Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}.
|
||||||
*
|
*
|
||||||
* @param inputBitmap The {@link Bitmap} queued to the video sink.
|
* @param inputBitmap The {@link Bitmap} queued to the video sink.
|
||||||
* @param timestampIterator The times within the current stream that the bitmap should be shown
|
* @param timestampIterator The times within the current stream that the bitmap should be shown
|
||||||
@ -196,7 +198,7 @@ public interface VideoSink {
|
|||||||
/**
|
/**
|
||||||
* Incrementally renders processed video frames.
|
* Incrementally renders processed video frames.
|
||||||
*
|
*
|
||||||
* <p>Must be called after the sink is {@linkplain #initialize(Format) initialized}.
|
* <p>Must be called after the sink is {@linkplain #initialize(Format, Clock) initialized}.
|
||||||
*
|
*
|
||||||
* @param positionUs The current playback position, in microseconds.
|
* @param positionUs The current playback position, in microseconds.
|
||||||
* @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds,
|
* @param elapsedRealtimeUs {@link android.os.SystemClock#elapsedRealtime()} in microseconds,
|
||||||
|
@ -34,7 +34,7 @@ public interface VideoSinkProvider {
|
|||||||
* during rendering.
|
* during rendering.
|
||||||
*
|
*
|
||||||
* <p>Must be called before the first {@linkplain #getSink() sink} is {@linkplain
|
* <p>Must be called before the first {@linkplain #getSink() sink} is {@linkplain
|
||||||
* VideoSink#initialize(Format) initialized}.
|
* VideoSink#initialize(Format, Clock) initialized}.
|
||||||
*/
|
*/
|
||||||
void setVideoFrameReleaseControl(VideoFrameReleaseControl videoFrameReleaseControl);
|
void setVideoFrameReleaseControl(VideoFrameReleaseControl videoFrameReleaseControl);
|
||||||
|
|
||||||
@ -47,14 +47,6 @@ public interface VideoSinkProvider {
|
|||||||
*/
|
*/
|
||||||
@Nullable VideoFrameReleaseControl getVideoFrameReleaseControl();
|
@Nullable VideoFrameReleaseControl getVideoFrameReleaseControl();
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@link Clock} that the provider should use internally.
|
|
||||||
*
|
|
||||||
* <p>Must be called before the first {@linkplain #getSink() sink} is {@linkplain
|
|
||||||
* VideoSink#initialize(Format) initialized}.
|
|
||||||
*/
|
|
||||||
void setClock(Clock clock);
|
|
||||||
|
|
||||||
/** Sets video effects on this provider to apply immediately. */
|
/** Sets video effects on this provider to apply immediately. */
|
||||||
void setVideoEffects(List<Effect> videoEffects);
|
void setVideoEffects(List<Effect> videoEffects);
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.PreviewingVideoGraph;
|
import androidx.media3.common.PreviewingVideoGraph;
|
||||||
import androidx.media3.common.VideoFrameProcessor;
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.common.VideoGraph;
|
import androidx.media3.common.VideoGraph;
|
||||||
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -60,16 +61,20 @@ public final class CompositingVideoSinkProviderTest {
|
|||||||
|
|
||||||
assertThrows(
|
assertThrows(
|
||||||
IllegalStateException.class,
|
IllegalStateException.class,
|
||||||
() -> sink.initialize(new Format.Builder().setWidth(640).setHeight(480).build()));
|
() ->
|
||||||
|
sink.initialize(
|
||||||
|
new Format.Builder().setWidth(640).setHeight(480).build(), Clock.DEFAULT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void initializeSink_calledTwice_throws() throws VideoSink.VideoSinkException {
|
public void initializeSink_calledTwice_throws() throws VideoSink.VideoSinkException {
|
||||||
CompositingVideoSinkProvider provider = createCompositingVideoSinkProvider();
|
CompositingVideoSinkProvider provider = createCompositingVideoSinkProvider();
|
||||||
VideoSink sink = provider.getSink();
|
VideoSink sink = provider.getSink();
|
||||||
sink.initialize(new Format.Builder().build());
|
sink.initialize(new Format.Builder().build(), Clock.DEFAULT);
|
||||||
|
|
||||||
assertThrows(IllegalStateException.class, () -> sink.initialize(new Format.Builder().build()));
|
assertThrows(
|
||||||
|
IllegalStateException.class,
|
||||||
|
() -> sink.initialize(new Format.Builder().build(), Clock.DEFAULT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -77,7 +82,7 @@ public final class CompositingVideoSinkProviderTest {
|
|||||||
throws VideoSink.VideoSinkException {
|
throws VideoSink.VideoSinkException {
|
||||||
CompositingVideoSinkProvider provider = createCompositingVideoSinkProvider();
|
CompositingVideoSinkProvider provider = createCompositingVideoSinkProvider();
|
||||||
VideoSink videoSink = provider.getSink();
|
VideoSink videoSink = provider.getSink();
|
||||||
videoSink.initialize(new Format.Builder().build());
|
videoSink.initialize(new Format.Builder().build(), Clock.DEFAULT);
|
||||||
videoSink.registerInputStream(
|
videoSink.registerInputStream(
|
||||||
VideoSink.INPUT_TYPE_SURFACE, new Format.Builder().setWidth(640).setHeight(480).build());
|
VideoSink.INPUT_TYPE_SURFACE, new Format.Builder().setWidth(640).setHeight(480).build());
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user