mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Rename CompositingVideoSinkProvider and PreviewAudioPipeline
The components are mirror components for video and audio so they should have a matching name PiperOrigin-RevId: 673357081
This commit is contained in:
parent
4be5b74366
commit
8271a5f920
@ -66,7 +66,7 @@
|
|||||||
<init>();
|
<init>();
|
||||||
}
|
}
|
||||||
|
|
||||||
# Constructors and methods accessed via reflection in CompositingVideoSinkProvider
|
# Constructors and methods accessed via reflection in PlaybackVideoGraphWrapper
|
||||||
-dontnote androidx.media3.effect.PreviewingSingleInputVideoGraph$Factory
|
-dontnote androidx.media3.effect.PreviewingSingleInputVideoGraph$Factory
|
||||||
-keepclasseswithmembers class androidx.media3.effect.PreviewingSingleInputVideoGraph$Factory {
|
-keepclasseswithmembers class androidx.media3.effect.PreviewingSingleInputVideoGraph$Factory {
|
||||||
<init>(androidx.media3.common.VideoFrameProcessor$Factory);
|
<init>(androidx.media3.common.VideoFrameProcessor$Factory);
|
||||||
|
@ -410,8 +410,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
* explicitly using {@link MediaFormat#KEY_OPERATING_RATE}).
|
* explicitly using {@link MediaFormat#KEY_OPERATING_RATE}).
|
||||||
* @param videoSink The {@link VideoSink} consuming the frames. If {@code null} and effects are
|
* @param videoSink The {@link VideoSink} consuming the frames. If {@code null} and effects are
|
||||||
* {@linkplain #MSG_SET_VIDEO_EFFECTS set}, a {@link VideoSink} produced by a {@link
|
* {@linkplain #MSG_SET_VIDEO_EFFECTS set}, a {@link VideoSink} produced by a {@link
|
||||||
* CompositingVideoSinkProvider} with its default configuration will be used to apply effects
|
* PlaybackVideoGraphWrapper} with its default configuration will be used to apply effects and
|
||||||
* and render the frames on the output.
|
* render the frames on the output.
|
||||||
*/
|
*/
|
||||||
public MediaCodecVideoRenderer(
|
public MediaCodecVideoRenderer(
|
||||||
Context context,
|
Context context,
|
||||||
@ -677,7 +677,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
|||||||
if (!hasSetVideoSink) {
|
if (!hasSetVideoSink) {
|
||||||
if (videoEffects != null && videoSink == null) {
|
if (videoEffects != null && videoSink == null) {
|
||||||
videoSink =
|
videoSink =
|
||||||
new CompositingVideoSinkProvider.Builder(context, videoFrameReleaseControl)
|
new PlaybackVideoGraphWrapper.Builder(context, videoFrameReleaseControl)
|
||||||
.setClock(getClock())
|
.setClock(getClock())
|
||||||
.build()
|
.build()
|
||||||
.getSink();
|
.getSink();
|
||||||
|
@ -65,53 +65,56 @@ import java.util.concurrent.Executor;
|
|||||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/** Handles composition of video sinks. */
|
/**
|
||||||
|
* Processes input from {@link VideoSink} instances, plumbing the data through a {@link VideoGraph}
|
||||||
|
* and rendering the output.
|
||||||
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@RestrictTo({Scope.LIBRARY_GROUP})
|
@RestrictTo({Scope.LIBRARY_GROUP})
|
||||||
public final class CompositingVideoSinkProvider implements VideoSinkProvider, VideoGraph.Listener {
|
public final class PlaybackVideoGraphWrapper implements VideoSinkProvider, VideoGraph.Listener {
|
||||||
|
|
||||||
/** Listener for {@link CompositingVideoSinkProvider} events. */
|
/** Listener for {@link PlaybackVideoGraphWrapper} events. */
|
||||||
public interface Listener {
|
public interface Listener {
|
||||||
/**
|
/**
|
||||||
* Called when the video frame processor renders the first frame.
|
* Called when the video frame processor renders the first frame.
|
||||||
*
|
*
|
||||||
* @param compositingVideoSinkProvider The compositing video sink provider which triggered this
|
* @param playbackVideoGraphWrapper The {@link PlaybackVideoGraphWrapper} which triggered this
|
||||||
* event.
|
* event.
|
||||||
*/
|
*/
|
||||||
void onFirstFrameRendered(CompositingVideoSinkProvider compositingVideoSinkProvider);
|
void onFirstFrameRendered(PlaybackVideoGraphWrapper playbackVideoGraphWrapper);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the video frame processor dropped a frame.
|
* Called when the video frame processor dropped a frame.
|
||||||
*
|
*
|
||||||
* @param compositingVideoSinkProvider The compositing video sink provider which triggered this
|
* @param playbackVideoGraphWrapper The {@link PlaybackVideoGraphWrapper} which triggered this
|
||||||
* event.
|
* event.
|
||||||
*/
|
*/
|
||||||
void onFrameDropped(CompositingVideoSinkProvider compositingVideoSinkProvider);
|
void onFrameDropped(PlaybackVideoGraphWrapper playbackVideoGraphWrapper);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called before a frame is rendered for the first time since setting the surface, and each time
|
* Called before a frame is rendered for the first time since setting the surface, and each time
|
||||||
* there's a change in the size, rotation or pixel aspect ratio of the video being rendered.
|
* there's a change in the size, rotation or pixel aspect ratio of the video being rendered.
|
||||||
*
|
*
|
||||||
* @param compositingVideoSinkProvider The compositing video sink provider which triggered this
|
* @param playbackVideoGraphWrapper The {@link PlaybackVideoGraphWrapper} which triggered this
|
||||||
* event.
|
* event.
|
||||||
* @param videoSize The video size.
|
* @param videoSize The video size.
|
||||||
*/
|
*/
|
||||||
void onVideoSizeChanged(
|
void onVideoSizeChanged(
|
||||||
CompositingVideoSinkProvider compositingVideoSinkProvider, VideoSize videoSize);
|
PlaybackVideoGraphWrapper playbackVideoGraphWrapper, VideoSize videoSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the video frame processor encountered an error.
|
* Called when the video frame processor encountered an error.
|
||||||
*
|
*
|
||||||
* @param compositingVideoSinkProvider The compositing video sink provider which triggered this
|
* @param playbackVideoGraphWrapper The {@link PlaybackVideoGraphWrapper} which triggered this
|
||||||
* event.
|
* event.
|
||||||
* @param videoFrameProcessingException The error.
|
* @param videoFrameProcessingException The error.
|
||||||
*/
|
*/
|
||||||
void onError(
|
void onError(
|
||||||
CompositingVideoSinkProvider compositingVideoSinkProvider,
|
PlaybackVideoGraphWrapper playbackVideoGraphWrapper,
|
||||||
VideoFrameProcessingException videoFrameProcessingException);
|
VideoFrameProcessingException videoFrameProcessingException);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A builder for {@link CompositingVideoSinkProvider} instances. */
|
/** A builder for {@link PlaybackVideoGraphWrapper} instances. */
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final VideoFrameReleaseControl videoFrameReleaseControl;
|
private final VideoFrameReleaseControl videoFrameReleaseControl;
|
||||||
@ -176,12 +179,12 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the {@link CompositingVideoSinkProvider}.
|
* Builds the {@link PlaybackVideoGraphWrapper}.
|
||||||
*
|
*
|
||||||
* <p>This method must be called at most once and will throw an {@link IllegalStateException} if
|
* <p>This method must be called at most once and will throw an {@link IllegalStateException} if
|
||||||
* it has already been called.
|
* it has already been called.
|
||||||
*/
|
*/
|
||||||
public CompositingVideoSinkProvider build() {
|
public PlaybackVideoGraphWrapper build() {
|
||||||
checkState(!built);
|
checkState(!built);
|
||||||
|
|
||||||
if (previewingVideoGraphFactory == null) {
|
if (previewingVideoGraphFactory == null) {
|
||||||
@ -191,10 +194,9 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
previewingVideoGraphFactory =
|
previewingVideoGraphFactory =
|
||||||
new ReflectivePreviewingSingleInputVideoGraphFactory(videoFrameProcessorFactory);
|
new ReflectivePreviewingSingleInputVideoGraphFactory(videoFrameProcessorFactory);
|
||||||
}
|
}
|
||||||
CompositingVideoSinkProvider compositingVideoSinkProvider =
|
PlaybackVideoGraphWrapper playbackVideoGraphWrapper = new PlaybackVideoGraphWrapper(this);
|
||||||
new CompositingVideoSinkProvider(this);
|
|
||||||
built = true;
|
built = true;
|
||||||
return compositingVideoSinkProvider;
|
return playbackVideoGraphWrapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +218,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
private final VideoFrameRenderControl videoFrameRenderControl;
|
private final VideoFrameRenderControl videoFrameRenderControl;
|
||||||
private final PreviewingVideoGraph.Factory previewingVideoGraphFactory;
|
private final PreviewingVideoGraph.Factory previewingVideoGraphFactory;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final CopyOnWriteArraySet<CompositingVideoSinkProvider.Listener> listeners;
|
private final CopyOnWriteArraySet<PlaybackVideoGraphWrapper.Listener> listeners;
|
||||||
|
|
||||||
private @MonotonicNonNull Format outputFormat;
|
private @MonotonicNonNull Format outputFormat;
|
||||||
private @MonotonicNonNull VideoFrameMetadataListener videoFrameMetadataListener;
|
private @MonotonicNonNull VideoFrameMetadataListener videoFrameMetadataListener;
|
||||||
@ -233,7 +235,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
*/
|
*/
|
||||||
private long bufferTimestampAdjustmentUs;
|
private long bufferTimestampAdjustmentUs;
|
||||||
|
|
||||||
private CompositingVideoSinkProvider(Builder builder) {
|
private PlaybackVideoGraphWrapper(Builder builder) {
|
||||||
context = builder.context;
|
context = builder.context;
|
||||||
videoSinkImpl = new VideoSinkImpl(context);
|
videoSinkImpl = new VideoSinkImpl(context);
|
||||||
clock = builder.clock;
|
clock = builder.clock;
|
||||||
@ -248,20 +250,20 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a {@link CompositingVideoSinkProvider.Listener}.
|
* Adds a {@link PlaybackVideoGraphWrapper.Listener}.
|
||||||
*
|
*
|
||||||
* @param listener The listener to be added.
|
* @param listener The listener to be added.
|
||||||
*/
|
*/
|
||||||
public void addListener(CompositingVideoSinkProvider.Listener listener) {
|
public void addListener(PlaybackVideoGraphWrapper.Listener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a {@link CompositingVideoSinkProvider.Listener}.
|
* Removes a {@link PlaybackVideoGraphWrapper.Listener}.
|
||||||
*
|
*
|
||||||
* @param listener The listener to be removed.
|
* @param listener The listener to be removed.
|
||||||
*/
|
*/
|
||||||
public void removeListener(CompositingVideoSinkProvider.Listener listener) {
|
public void removeListener(PlaybackVideoGraphWrapper.Listener listener) {
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +323,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
@Override
|
@Override
|
||||||
public void onOutputFrameAvailableForRendering(long framePresentationTimeUs) {
|
public void onOutputFrameAvailableForRendering(long framePresentationTimeUs) {
|
||||||
if (pendingFlushCount > 0) {
|
if (pendingFlushCount > 0) {
|
||||||
// Ignore available frames while the sink provider is flushing
|
// Ignore available frames while flushing
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// The frame presentation time is relative to the start of the Composition and without the
|
// The frame presentation time is relative to the start of the Composition and without the
|
||||||
@ -337,8 +339,8 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(VideoFrameProcessingException exception) {
|
public void onError(VideoFrameProcessingException exception) {
|
||||||
for (CompositingVideoSinkProvider.Listener listener : listeners) {
|
for (PlaybackVideoGraphWrapper.Listener listener : listeners) {
|
||||||
listener.onError(/* compositingVideoSinkProvider= */ this, exception);
|
listener.onError(/* playbackVideoGraphWrapper= */ this, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,7 +466,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Receives input from an ExoPlayer renderer and forwards it to the video graph. */
|
/** Receives input from an ExoPlayer renderer and forwards it to the video graph. */
|
||||||
private final class VideoSinkImpl implements VideoSink, CompositingVideoSinkProvider.Listener {
|
private final class VideoSinkImpl implements VideoSink, PlaybackVideoGraphWrapper.Listener {
|
||||||
|
|
||||||
private final int videoFrameProcessorMaxPendingFrameCount;
|
private final int videoFrameProcessorMaxPendingFrameCount;
|
||||||
private final ArrayList<Effect> videoEffects;
|
private final ArrayList<Effect> videoEffects;
|
||||||
@ -539,7 +541,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
@Override
|
@Override
|
||||||
public void initialize(Format sourceFormat) throws VideoSinkException {
|
public void initialize(Format sourceFormat) throws VideoSinkException {
|
||||||
checkState(!isInitialized());
|
checkState(!isInitialized());
|
||||||
videoFrameProcessor = CompositingVideoSinkProvider.this.initialize(sourceFormat);
|
videoFrameProcessor = PlaybackVideoGraphWrapper.this.initialize(sourceFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -556,7 +558,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
hasRegisteredFirstInputStream = false;
|
hasRegisteredFirstInputStream = false;
|
||||||
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
lastBufferPresentationTimeUs = C.TIME_UNSET;
|
lastBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
CompositingVideoSinkProvider.this.flush();
|
PlaybackVideoGraphWrapper.this.flush();
|
||||||
if (resetPosition) {
|
if (resetPosition) {
|
||||||
videoFrameReleaseControl.reset();
|
videoFrameReleaseControl.reset();
|
||||||
}
|
}
|
||||||
@ -569,7 +571,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isReady(boolean rendererOtherwiseReady) {
|
public boolean isReady(boolean rendererOtherwiseReady) {
|
||||||
return CompositingVideoSinkProvider.this.isReady(
|
return PlaybackVideoGraphWrapper.this.isReady(
|
||||||
/* rendererOtherwiseReady= */ rendererOtherwiseReady && isInitialized());
|
/* rendererOtherwiseReady= */ rendererOtherwiseReady && isInitialized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,7 +579,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
public boolean isEnded() {
|
public boolean isEnded() {
|
||||||
return isInitialized()
|
return isInitialized()
|
||||||
&& finalBufferPresentationTimeUs != C.TIME_UNSET
|
&& finalBufferPresentationTimeUs != C.TIME_UNSET
|
||||||
&& CompositingVideoSinkProvider.this.hasReleasedFrame(finalBufferPresentationTimeUs);
|
&& PlaybackVideoGraphWrapper.this.hasReleasedFrame(finalBufferPresentationTimeUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -619,12 +621,12 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
@Override
|
@Override
|
||||||
public void setVideoFrameMetadataListener(
|
public void setVideoFrameMetadataListener(
|
||||||
VideoFrameMetadataListener videoFrameMetadataListener) {
|
VideoFrameMetadataListener videoFrameMetadataListener) {
|
||||||
CompositingVideoSinkProvider.this.setVideoFrameMetadataListener(videoFrameMetadataListener);
|
PlaybackVideoGraphWrapper.this.setVideoFrameMetadataListener(videoFrameMetadataListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPlaybackSpeed(@FloatRange(from = 0, fromInclusive = false) float speed) {
|
public void setPlaybackSpeed(@FloatRange(from = 0, fromInclusive = false) float speed) {
|
||||||
CompositingVideoSinkProvider.this.setPlaybackSpeed(speed);
|
PlaybackVideoGraphWrapper.this.setPlaybackSpeed(speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -660,12 +662,12 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOutputSurfaceInfo(Surface outputSurface, Size outputResolution) {
|
public void setOutputSurfaceInfo(Surface outputSurface, Size outputResolution) {
|
||||||
CompositingVideoSinkProvider.this.setOutputSurfaceInfo(outputSurface, outputResolution);
|
PlaybackVideoGraphWrapper.this.setOutputSurfaceInfo(outputSurface, outputResolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearOutputSurfaceInfo() {
|
public void clearOutputSurfaceInfo() {
|
||||||
CompositingVideoSinkProvider.this.clearOutputSurfaceInfo();
|
PlaybackVideoGraphWrapper.this.clearOutputSurfaceInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -734,7 +736,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
// input frame from the next input stream.
|
// input frame from the next input stream.
|
||||||
if (isInputStreamChangePending) {
|
if (isInputStreamChangePending) {
|
||||||
if (pendingInputStreamBufferPresentationTimeUs == C.TIME_UNSET
|
if (pendingInputStreamBufferPresentationTimeUs == C.TIME_UNSET
|
||||||
|| CompositingVideoSinkProvider.this.hasReleasedFrame(
|
|| PlaybackVideoGraphWrapper.this.hasReleasedFrame(
|
||||||
pendingInputStreamBufferPresentationTimeUs)) {
|
pendingInputStreamBufferPresentationTimeUs)) {
|
||||||
maybeRegisterInputStream();
|
maybeRegisterInputStream();
|
||||||
isInputStreamChangePending = false;
|
isInputStreamChangePending = false;
|
||||||
@ -793,7 +795,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
@Override
|
@Override
|
||||||
public void render(long positionUs, long elapsedRealtimeUs) throws VideoSinkException {
|
public void render(long positionUs, long elapsedRealtimeUs) throws VideoSinkException {
|
||||||
try {
|
try {
|
||||||
CompositingVideoSinkProvider.this.render(positionUs, elapsedRealtimeUs);
|
PlaybackVideoGraphWrapper.this.render(positionUs, elapsedRealtimeUs);
|
||||||
} catch (ExoPlaybackException e) {
|
} catch (ExoPlaybackException e) {
|
||||||
throw new VideoSinkException(
|
throw new VideoSinkException(
|
||||||
e, inputFormat != null ? inputFormat : new Format.Builder().build());
|
e, inputFormat != null ? inputFormat : new Format.Builder().build());
|
||||||
@ -807,14 +809,14 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() {
|
||||||
CompositingVideoSinkProvider.this.release();
|
PlaybackVideoGraphWrapper.this.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other methods
|
// Other methods
|
||||||
|
|
||||||
private void maybeSetStreamOffsetChange(long bufferPresentationTimeUs) {
|
private void maybeSetStreamOffsetChange(long bufferPresentationTimeUs) {
|
||||||
if (pendingInputStreamOffsetChange) {
|
if (pendingInputStreamOffsetChange) {
|
||||||
CompositingVideoSinkProvider.this.onStreamOffsetChange(
|
PlaybackVideoGraphWrapper.this.onStreamOffsetChange(
|
||||||
inputBufferTimestampAdjustmentUs,
|
inputBufferTimestampAdjustmentUs,
|
||||||
bufferPresentationTimeUs,
|
bufferPresentationTimeUs,
|
||||||
/* streamOffsetUs= */ inputStreamOffsetUs);
|
/* streamOffsetUs= */ inputStreamOffsetUs);
|
||||||
@ -834,7 +836,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
// An input stream is fully decoded, wait until all of its frames are released before queueing
|
// An input stream is fully decoded, wait until all of its frames are released before queueing
|
||||||
// input frame from the next input stream.
|
// input frame from the next input stream.
|
||||||
if (pendingInputStreamBufferPresentationTimeUs == C.TIME_UNSET
|
if (pendingInputStreamBufferPresentationTimeUs == C.TIME_UNSET
|
||||||
|| CompositingVideoSinkProvider.this.hasReleasedFrame(
|
|| PlaybackVideoGraphWrapper.this.hasReleasedFrame(
|
||||||
pendingInputStreamBufferPresentationTimeUs)) {
|
pendingInputStreamBufferPresentationTimeUs)) {
|
||||||
maybeRegisterInputStream();
|
maybeRegisterInputStream();
|
||||||
isInputStreamChangePending = false;
|
isInputStreamChangePending = false;
|
||||||
@ -864,16 +866,16 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
finalBufferPresentationTimeUs = C.TIME_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompositingVideoSinkProvider.Listener implementation
|
// PlaybackVideoGraphWrapper.Listener implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFirstFrameRendered(CompositingVideoSinkProvider compositingVideoSinkProvider) {
|
public void onFirstFrameRendered(PlaybackVideoGraphWrapper playbackVideoGraphWrapper) {
|
||||||
VideoSink.Listener currentListener = listener;
|
VideoSink.Listener currentListener = listener;
|
||||||
listenerExecutor.execute(() -> currentListener.onFirstFrameRendered(/* videoSink= */ this));
|
listenerExecutor.execute(() -> currentListener.onFirstFrameRendered(/* videoSink= */ this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFrameDropped(CompositingVideoSinkProvider compositingVideoSinkProvider) {
|
public void onFrameDropped(PlaybackVideoGraphWrapper playbackVideoGraphWrapper) {
|
||||||
VideoSink.Listener currentListener = listener;
|
VideoSink.Listener currentListener = listener;
|
||||||
listenerExecutor.execute(
|
listenerExecutor.execute(
|
||||||
() -> currentListener.onFrameDropped(checkStateNotNull(/* reference= */ this)));
|
() -> currentListener.onFrameDropped(checkStateNotNull(/* reference= */ this)));
|
||||||
@ -881,7 +883,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onVideoSizeChanged(
|
public void onVideoSizeChanged(
|
||||||
CompositingVideoSinkProvider compositingVideoSinkProvider, VideoSize videoSize) {
|
PlaybackVideoGraphWrapper playbackVideoGraphWrapper, VideoSize videoSize) {
|
||||||
VideoSink.Listener currentListener = listener;
|
VideoSink.Listener currentListener = listener;
|
||||||
listenerExecutor.execute(
|
listenerExecutor.execute(
|
||||||
() -> currentListener.onVideoSizeChanged(/* videoSink= */ this, videoSize));
|
() -> currentListener.onVideoSizeChanged(/* videoSink= */ this, videoSize));
|
||||||
@ -889,7 +891,7 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(
|
public void onError(
|
||||||
CompositingVideoSinkProvider compositingVideoSinkProvider,
|
PlaybackVideoGraphWrapper playbackVideoGraphWrapper,
|
||||||
VideoFrameProcessingException videoFrameProcessingException) {
|
VideoFrameProcessingException videoFrameProcessingException) {
|
||||||
VideoSink.Listener currentListener = listener;
|
VideoSink.Listener currentListener = listener;
|
||||||
listenerExecutor.execute(
|
listenerExecutor.execute(
|
||||||
@ -911,8 +913,8 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
.setHeight(videoSize.height)
|
.setHeight(videoSize.height)
|
||||||
.setSampleMimeType(MimeTypes.VIDEO_RAW)
|
.setSampleMimeType(MimeTypes.VIDEO_RAW)
|
||||||
.build();
|
.build();
|
||||||
for (CompositingVideoSinkProvider.Listener listener : listeners) {
|
for (PlaybackVideoGraphWrapper.Listener listener : listeners) {
|
||||||
listener.onVideoSizeChanged(CompositingVideoSinkProvider.this, videoSize);
|
listener.onVideoSizeChanged(PlaybackVideoGraphWrapper.this, videoSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -923,8 +925,8 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
long streamOffsetUs,
|
long streamOffsetUs,
|
||||||
boolean isFirstFrame) {
|
boolean isFirstFrame) {
|
||||||
if (isFirstFrame && currentSurfaceAndSize != null) {
|
if (isFirstFrame && currentSurfaceAndSize != null) {
|
||||||
for (CompositingVideoSinkProvider.Listener listener : listeners) {
|
for (PlaybackVideoGraphWrapper.Listener listener : listeners) {
|
||||||
listener.onFirstFrameRendered(CompositingVideoSinkProvider.this);
|
listener.onFirstFrameRendered(PlaybackVideoGraphWrapper.this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (videoFrameMetadataListener != null) {
|
if (videoFrameMetadataListener != null) {
|
||||||
@ -942,8 +944,8 @@ public final class CompositingVideoSinkProvider implements VideoSinkProvider, Vi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dropFrame() {
|
public void dropFrame() {
|
||||||
for (CompositingVideoSinkProvider.Listener listener : listeners) {
|
for (PlaybackVideoGraphWrapper.Listener listener : listeners) {
|
||||||
listener.onFrameDropped(CompositingVideoSinkProvider.this);
|
listener.onFrameDropped(PlaybackVideoGraphWrapper.this);
|
||||||
}
|
}
|
||||||
checkStateNotNull(videoGraph).renderOutputFrame(VideoFrameProcessor.DROP_OUTPUT_FRAME);
|
checkStateNotNull(videoGraph).renderOutputFrame(VideoFrameProcessor.DROP_OUTPUT_FRAME);
|
||||||
}
|
}
|
@ -35,14 +35,14 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
/** Unit test for {@link CompositingVideoSinkProvider}. */
|
/** Unit test for {@link PlaybackVideoGraphWrapper}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class CompositingVideoSinkProviderTest {
|
public final class PlaybackVideoGraphWrapperTest {
|
||||||
@Test
|
@Test
|
||||||
public void builder_calledMultipleTimes_throws() {
|
public void builder_calledMultipleTimes_throws() {
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
CompositingVideoSinkProvider.Builder builder =
|
PlaybackVideoGraphWrapper.Builder builder =
|
||||||
new CompositingVideoSinkProvider.Builder(context, createVideoFrameReleaseControl());
|
new PlaybackVideoGraphWrapper.Builder(context, createVideoFrameReleaseControl());
|
||||||
|
|
||||||
builder.build();
|
builder.build();
|
||||||
|
|
||||||
@ -51,16 +51,16 @@ public final class CompositingVideoSinkProviderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void initializeSink_calledTwice_throws() throws VideoSink.VideoSinkException {
|
public void initializeSink_calledTwice_throws() throws VideoSink.VideoSinkException {
|
||||||
CompositingVideoSinkProvider provider = createCompositingVideoSinkProvider();
|
PlaybackVideoGraphWrapper provider = createPlaybackVideoGraphWrapper();
|
||||||
VideoSink sink = provider.getSink();
|
VideoSink sink = provider.getSink();
|
||||||
sink.initialize(new Format.Builder().build());
|
sink.initialize(new Format.Builder().build());
|
||||||
|
|
||||||
assertThrows(IllegalStateException.class, () -> sink.initialize(new Format.Builder().build()));
|
assertThrows(IllegalStateException.class, () -> sink.initialize(new Format.Builder().build()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CompositingVideoSinkProvider createCompositingVideoSinkProvider() {
|
private static PlaybackVideoGraphWrapper createPlaybackVideoGraphWrapper() {
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
return new CompositingVideoSinkProvider.Builder(context, createVideoFrameReleaseControl())
|
return new PlaybackVideoGraphWrapper.Builder(context, createVideoFrameReleaseControl())
|
||||||
.setPreviewingVideoGraphFactory(new TestPreviewingVideoGraphFactory())
|
.setPreviewingVideoGraphFactory(new TestPreviewingVideoGraphFactory())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
@ -442,7 +442,7 @@ public class CompositionPlayerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void playback_videoSinkProviderFails_playerRaisesError() {
|
public void playback_videoGraphWrapperFails_playerRaisesError() {
|
||||||
PlayerTestListener listener = new PlayerTestListener(TEST_TIMEOUT_MS);
|
PlayerTestListener listener = new PlayerTestListener(TEST_TIMEOUT_MS);
|
||||||
EditedMediaItem video =
|
EditedMediaItem video =
|
||||||
new EditedMediaItem.Builder(MediaItem.fromUri(MP4_ASSET.uri))
|
new EditedMediaItem.Builder(MediaItem.fromUri(MP4_ASSET.uri))
|
||||||
@ -478,7 +478,7 @@ public class CompositionPlayerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void release_videoSinkProviderFailsDuringRelease_playerDoesNotRaiseError()
|
public void release_videoGraphWrapperFailsDuringRelease_playerDoesNotRaiseError()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
PlayerTestListener playerTestListener = new PlayerTestListener(TEST_TIMEOUT_MS);
|
PlayerTestListener playerTestListener = new PlayerTestListener(TEST_TIMEOUT_MS);
|
||||||
EditedMediaItem video =
|
EditedMediaItem video =
|
||||||
|
@ -36,7 +36,7 @@ import java.util.Objects;
|
|||||||
/**
|
/**
|
||||||
* An {@link AudioSink} implementation that feeds an {@link AudioGraphInput}.
|
* An {@link AudioSink} implementation that feeds an {@link AudioGraphInput}.
|
||||||
*
|
*
|
||||||
* <p>Should be used by {@link PreviewAudioPipeline}.
|
* <p>Should be used by {@link PlaybackAudioGraphWrapper}.
|
||||||
*/
|
*/
|
||||||
/* package */ final class AudioGraphInputAudioSink implements AudioSink {
|
/* package */ final class AudioGraphInputAudioSink implements AudioSink {
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
|||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
import androidx.media3.exoplayer.util.EventLogger;
|
import androidx.media3.exoplayer.util.EventLogger;
|
||||||
import androidx.media3.exoplayer.video.CompositingVideoSinkProvider;
|
import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper;
|
||||||
import androidx.media3.exoplayer.video.VideoFrameReleaseControl;
|
import androidx.media3.exoplayer.video.VideoFrameReleaseControl;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
@ -109,7 +109,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@RestrictTo(LIBRARY_GROUP)
|
@RestrictTo(LIBRARY_GROUP)
|
||||||
public final class CompositionPlayer extends SimpleBasePlayer
|
public final class CompositionPlayer extends SimpleBasePlayer
|
||||||
implements CompositionPlayerInternal.Listener,
|
implements CompositionPlayerInternal.Listener,
|
||||||
CompositingVideoSinkProvider.Listener,
|
PlaybackVideoGraphWrapper.Listener,
|
||||||
SurfaceHolder.Callback {
|
SurfaceHolder.Callback {
|
||||||
|
|
||||||
/** A builder for {@link CompositionPlayer} instances. */
|
/** A builder for {@link CompositionPlayer} instances. */
|
||||||
@ -369,10 +369,10 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
|||||||
setVideoSurfaceInternal(surface, videoOutputSize);
|
setVideoSurfaceInternal(surface, videoOutputSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompositingVideoSinkProvider.Listener methods. Called on playback thread.
|
// PlaybackVideoGraphWrapper.Listener methods. Called on playback thread.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFirstFrameRendered(CompositingVideoSinkProvider compositingVideoSinkProvider) {
|
public void onFirstFrameRendered(PlaybackVideoGraphWrapper playbackVideoGraphWrapper) {
|
||||||
applicationHandler.post(
|
applicationHandler.post(
|
||||||
() -> {
|
() -> {
|
||||||
CompositionPlayer.this.renderedFirstFrame = true;
|
CompositionPlayer.this.renderedFirstFrame = true;
|
||||||
@ -381,27 +381,27 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFrameDropped(CompositingVideoSinkProvider compositingVideoSinkProvider) {
|
public void onFrameDropped(PlaybackVideoGraphWrapper playbackVideoGraphWrapper) {
|
||||||
// Do not post to application thread on each dropped frame, because onFrameDropped
|
// Do not post to application thread on each dropped frame, because onFrameDropped
|
||||||
// may be called frequently when resources are already scarce.
|
// may be called frequently when resources are already scarce.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onVideoSizeChanged(
|
public void onVideoSizeChanged(
|
||||||
CompositingVideoSinkProvider compositingVideoSinkProvider, VideoSize videoSize) {
|
PlaybackVideoGraphWrapper playbackVideoGraphWrapper, VideoSize videoSize) {
|
||||||
// TODO: b/328219481 - Report video size change to app.
|
// TODO: b/328219481 - Report video size change to app.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(
|
public void onError(
|
||||||
CompositingVideoSinkProvider compositingVideoSinkProvider,
|
PlaybackVideoGraphWrapper playbackVideoGraphWrapper,
|
||||||
VideoFrameProcessingException videoFrameProcessingException) {
|
VideoFrameProcessingException videoFrameProcessingException) {
|
||||||
// The error will also be surfaced from the underlying ExoPlayer instance via
|
// The error will also be surfaced from the underlying ExoPlayer instance via
|
||||||
// PlayerListener.onPlayerError, and it will arrive to the composition player twice.
|
// PlayerListener.onPlayerError, and it will arrive to the composition player twice.
|
||||||
applicationHandler.post(
|
applicationHandler.post(
|
||||||
() ->
|
() ->
|
||||||
maybeUpdatePlaybackError(
|
maybeUpdatePlaybackError(
|
||||||
"error from video sink provider",
|
"Error processing video frames",
|
||||||
videoFrameProcessingException,
|
videoFrameProcessingException,
|
||||||
PlaybackException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED));
|
PlaybackException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED));
|
||||||
}
|
}
|
||||||
@ -660,23 +660,23 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
|||||||
playbackThread = new HandlerThread("CompositionPlaybackThread", Process.THREAD_PRIORITY_AUDIO);
|
playbackThread = new HandlerThread("CompositionPlaybackThread", Process.THREAD_PRIORITY_AUDIO);
|
||||||
playbackThread.start();
|
playbackThread.start();
|
||||||
// Create the audio and video composition components now in order to setup the audio and video
|
// Create the audio and video composition components now in order to setup the audio and video
|
||||||
// pipelines. Once this method returns, further access to the audio and video pipelines must
|
// pipelines. Once this method returns, further access to the audio and video graph wrappers
|
||||||
// done on the playback thread only, to ensure related components are accessed from one thread
|
// must done on the playback thread only, to ensure related components are accessed from one
|
||||||
// only.
|
// thread only.
|
||||||
PreviewAudioPipeline previewAudioPipeline =
|
PlaybackAudioGraphWrapper playbackAudioGraphWrapper =
|
||||||
new PreviewAudioPipeline(
|
new PlaybackAudioGraphWrapper(
|
||||||
new DefaultAudioMixer.Factory(),
|
new DefaultAudioMixer.Factory(),
|
||||||
composition.effects.audioProcessors,
|
composition.effects.audioProcessors,
|
||||||
checkNotNull(finalAudioSink));
|
checkNotNull(finalAudioSink));
|
||||||
VideoFrameReleaseControl videoFrameReleaseControl =
|
VideoFrameReleaseControl videoFrameReleaseControl =
|
||||||
new VideoFrameReleaseControl(
|
new VideoFrameReleaseControl(
|
||||||
context, new CompositionFrameTimingEvaluator(), /* allowedJoiningTimeMs= */ 0);
|
context, new CompositionFrameTimingEvaluator(), /* allowedJoiningTimeMs= */ 0);
|
||||||
CompositingVideoSinkProvider compositingVideoSinkProvider =
|
PlaybackVideoGraphWrapper playbackVideoGraphWrapper =
|
||||||
new CompositingVideoSinkProvider.Builder(context, videoFrameReleaseControl)
|
new PlaybackVideoGraphWrapper.Builder(context, videoFrameReleaseControl)
|
||||||
.setPreviewingVideoGraphFactory(checkNotNull(previewingVideoGraphFactory))
|
.setPreviewingVideoGraphFactory(checkNotNull(previewingVideoGraphFactory))
|
||||||
.setClock(clock)
|
.setClock(clock)
|
||||||
.build();
|
.build();
|
||||||
compositingVideoSinkProvider.addListener(this);
|
playbackVideoGraphWrapper.addListener(this);
|
||||||
|
|
||||||
// Video playback is disabled when one EditedMediaItem removes video.
|
// Video playback is disabled when one EditedMediaItem removes video.
|
||||||
boolean disableVideoPlayback = shouldDisableVideoPlayback(composition);
|
boolean disableVideoPlayback = shouldDisableVideoPlayback(composition);
|
||||||
@ -687,11 +687,11 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
|||||||
? SequencePlayerRenderersWrapper.create(
|
? SequencePlayerRenderersWrapper.create(
|
||||||
context,
|
context,
|
||||||
editedMediaItemSequence,
|
editedMediaItemSequence,
|
||||||
previewAudioPipeline,
|
playbackAudioGraphWrapper,
|
||||||
compositingVideoSinkProvider.getSink(),
|
playbackVideoGraphWrapper.getSink(),
|
||||||
imageDecoderFactory)
|
imageDecoderFactory)
|
||||||
: SequencePlayerRenderersWrapper.createForAudio(
|
: SequencePlayerRenderersWrapper.createForAudio(
|
||||||
context, editedMediaItemSequence, previewAudioPipeline);
|
context, editedMediaItemSequence, playbackAudioGraphWrapper);
|
||||||
ExoPlayer.Builder playerBuilder =
|
ExoPlayer.Builder playerBuilder =
|
||||||
new ExoPlayer.Builder(context)
|
new ExoPlayer.Builder(context)
|
||||||
.setLooper(getApplicationLooper())
|
.setLooper(getApplicationLooper())
|
||||||
@ -725,8 +725,8 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
|||||||
new CompositionPlayerInternal(
|
new CompositionPlayerInternal(
|
||||||
playbackThread.getLooper(),
|
playbackThread.getLooper(),
|
||||||
clock,
|
clock,
|
||||||
previewAudioPipeline,
|
playbackAudioGraphWrapper,
|
||||||
compositingVideoSinkProvider,
|
playbackVideoGraphWrapper,
|
||||||
/* listener= */ this,
|
/* listener= */ this,
|
||||||
compositionInternalListenerHandler);
|
compositionInternalListenerHandler);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ import androidx.media3.common.util.HandlerWrapper;
|
|||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.Size;
|
import androidx.media3.common.util.Size;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.exoplayer.video.CompositingVideoSinkProvider;
|
import androidx.media3.exoplayer.video.PlaybackVideoGraphWrapper;
|
||||||
|
|
||||||
/** Provides access to the composition preview audio and video components on the playback thread. */
|
/** Provides access to the composition preview audio and video components on the playback thread. */
|
||||||
/* package */ final class CompositionPlayerInternal implements Handler.Callback {
|
/* package */ final class CompositionPlayerInternal implements Handler.Callback {
|
||||||
@ -57,10 +57,10 @@ import androidx.media3.exoplayer.video.CompositingVideoSinkProvider;
|
|||||||
private final HandlerWrapper handler;
|
private final HandlerWrapper handler;
|
||||||
|
|
||||||
/** Must be accessed on the playback thread only. */
|
/** Must be accessed on the playback thread only. */
|
||||||
private final PreviewAudioPipeline previewAudioPipeline;
|
private final PlaybackAudioGraphWrapper playbackAudioGraphWrapper;
|
||||||
|
|
||||||
/** Must be accessed on the playback thread only. */
|
/** Must be accessed on the playback thread only. */
|
||||||
private final CompositingVideoSinkProvider compositingVideoSinkProvider;
|
private final PlaybackVideoGraphWrapper playbackVideoGraphWrapper;
|
||||||
|
|
||||||
private final Listener listener;
|
private final Listener listener;
|
||||||
private final HandlerWrapper listenerHandler;
|
private final HandlerWrapper listenerHandler;
|
||||||
@ -72,22 +72,22 @@ import androidx.media3.exoplayer.video.CompositingVideoSinkProvider;
|
|||||||
*
|
*
|
||||||
* @param playbackLooper The playback thread {@link Looper}.
|
* @param playbackLooper The playback thread {@link Looper}.
|
||||||
* @param clock The {@link Clock} used.
|
* @param clock The {@link Clock} used.
|
||||||
* @param previewAudioPipeline The {@link PreviewAudioPipeline}.
|
* @param playbackAudioGraphWrapper The {@link PlaybackAudioGraphWrapper}.
|
||||||
* @param compositingVideoSinkProvider The {@link CompositingVideoSinkProvider}.
|
* @param playbackVideoGraphWrapper The {@link PlaybackVideoGraphWrapper}.
|
||||||
* @param listener A {@link Listener} to send callbacks back to the player.
|
* @param listener A {@link Listener} to send callbacks back to the player.
|
||||||
* @param listenerHandler A {@link HandlerWrapper} to dispatch {@link Listener} callbacks.
|
* @param listenerHandler A {@link HandlerWrapper} to dispatch {@link Listener} callbacks.
|
||||||
*/
|
*/
|
||||||
public CompositionPlayerInternal(
|
public CompositionPlayerInternal(
|
||||||
Looper playbackLooper,
|
Looper playbackLooper,
|
||||||
Clock clock,
|
Clock clock,
|
||||||
PreviewAudioPipeline previewAudioPipeline,
|
PlaybackAudioGraphWrapper playbackAudioGraphWrapper,
|
||||||
CompositingVideoSinkProvider compositingVideoSinkProvider,
|
PlaybackVideoGraphWrapper playbackVideoGraphWrapper,
|
||||||
Listener listener,
|
Listener listener,
|
||||||
HandlerWrapper listenerHandler) {
|
HandlerWrapper listenerHandler) {
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.handler = clock.createHandler(playbackLooper, /* callback= */ this);
|
this.handler = clock.createHandler(playbackLooper, /* callback= */ this);
|
||||||
this.previewAudioPipeline = previewAudioPipeline;
|
this.playbackAudioGraphWrapper = playbackAudioGraphWrapper;
|
||||||
this.compositingVideoSinkProvider = compositingVideoSinkProvider;
|
this.playbackVideoGraphWrapper = playbackVideoGraphWrapper;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.listenerHandler = listenerHandler;
|
this.listenerHandler = listenerHandler;
|
||||||
}
|
}
|
||||||
@ -149,10 +149,10 @@ import androidx.media3.exoplayer.video.CompositingVideoSinkProvider;
|
|||||||
case MSG_START_SEEK:
|
case MSG_START_SEEK:
|
||||||
// Video seeking is currently handled by the video renderers, specifically in
|
// Video seeking is currently handled by the video renderers, specifically in
|
||||||
// onPositionReset.
|
// onPositionReset.
|
||||||
previewAudioPipeline.startSeek(/* positionUs= */ Util.msToUs((long) message.obj));
|
playbackAudioGraphWrapper.startSeek(/* positionUs= */ Util.msToUs((long) message.obj));
|
||||||
break;
|
break;
|
||||||
case MSG_END_SEEK:
|
case MSG_END_SEEK:
|
||||||
previewAudioPipeline.endSeek();
|
playbackAudioGraphWrapper.endSeek();
|
||||||
break;
|
break;
|
||||||
case MSG_RELEASE:
|
case MSG_RELEASE:
|
||||||
releaseInternal(/* conditionVariable= */ (ConditionVariable) message.obj);
|
releaseInternal(/* conditionVariable= */ (ConditionVariable) message.obj);
|
||||||
@ -176,9 +176,9 @@ import androidx.media3.exoplayer.video.CompositingVideoSinkProvider;
|
|||||||
|
|
||||||
private void releaseInternal(ConditionVariable conditionVariable) {
|
private void releaseInternal(ConditionVariable conditionVariable) {
|
||||||
try {
|
try {
|
||||||
previewAudioPipeline.release();
|
playbackAudioGraphWrapper.release();
|
||||||
compositingVideoSinkProvider.clearOutputSurfaceInfo();
|
playbackVideoGraphWrapper.clearOutputSurfaceInfo();
|
||||||
compositingVideoSinkProvider.release();
|
playbackVideoGraphWrapper.release();
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
Log.e(TAG, "error while releasing the player", e);
|
Log.e(TAG, "error while releasing the player", e);
|
||||||
} finally {
|
} finally {
|
||||||
@ -188,7 +188,7 @@ import androidx.media3.exoplayer.video.CompositingVideoSinkProvider;
|
|||||||
|
|
||||||
private void clearOutputSurfaceInternal() {
|
private void clearOutputSurfaceInternal() {
|
||||||
try {
|
try {
|
||||||
compositingVideoSinkProvider.clearOutputSurfaceInfo();
|
playbackVideoGraphWrapper.clearOutputSurfaceInfo();
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
maybeRaiseError(
|
maybeRaiseError(
|
||||||
/* message= */ "error clearing video output",
|
/* message= */ "error clearing video output",
|
||||||
@ -199,7 +199,7 @@ import androidx.media3.exoplayer.video.CompositingVideoSinkProvider;
|
|||||||
|
|
||||||
private void setOutputSurfaceInfoOnInternalThread(OutputSurfaceInfo outputSurfaceInfo) {
|
private void setOutputSurfaceInfoOnInternalThread(OutputSurfaceInfo outputSurfaceInfo) {
|
||||||
try {
|
try {
|
||||||
compositingVideoSinkProvider.setOutputSurfaceInfo(
|
playbackVideoGraphWrapper.setOutputSurfaceInfo(
|
||||||
outputSurfaceInfo.surface, outputSurfaceInfo.size);
|
outputSurfaceInfo.surface, outputSurfaceInfo.size);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
maybeRaiseError(
|
maybeRaiseError(
|
||||||
|
@ -34,7 +34,7 @@ import java.util.Objects;
|
|||||||
*
|
*
|
||||||
* <p>Multiple streams of {@linkplain #createInput() input} are not currently supported.
|
* <p>Multiple streams of {@linkplain #createInput() input} are not currently supported.
|
||||||
*/
|
*/
|
||||||
/* package */ final class PreviewAudioPipeline {
|
/* package */ final class PlaybackAudioGraphWrapper {
|
||||||
private final AudioSink finalAudioSink;
|
private final AudioSink finalAudioSink;
|
||||||
private final AudioGraph audioGraph;
|
private final AudioGraph audioGraph;
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ import java.util.Objects;
|
|||||||
* @param effects The composition-level audio effects that are applied after mixing.
|
* @param effects The composition-level audio effects that are applied after mixing.
|
||||||
* @param finalAudioSink The {@linkplain AudioSink sink} for processed output audio.
|
* @param finalAudioSink The {@linkplain AudioSink sink} for processed output audio.
|
||||||
*/
|
*/
|
||||||
public PreviewAudioPipeline(
|
public PlaybackAudioGraphWrapper(
|
||||||
AudioMixer.Factory mixerFactory,
|
AudioMixer.Factory mixerFactory,
|
||||||
ImmutableList<AudioProcessor> effects,
|
ImmutableList<AudioProcessor> effects,
|
||||||
AudioSink finalAudioSink) {
|
AudioSink finalAudioSink) {
|
@ -62,7 +62,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final EditedMediaItemSequence sequence;
|
private final EditedMediaItemSequence sequence;
|
||||||
private final PreviewAudioPipeline previewAudioPipeline;
|
private final PlaybackAudioGraphWrapper playbackAudioGraphWrapper;
|
||||||
@Nullable private final VideoSink videoSink;
|
@Nullable private final VideoSink videoSink;
|
||||||
@Nullable private final ImageDecoder.Factory imageDecoderFactory;
|
@Nullable private final ImageDecoder.Factory imageDecoderFactory;
|
||||||
|
|
||||||
@ -70,22 +70,22 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
public static SequencePlayerRenderersWrapper create(
|
public static SequencePlayerRenderersWrapper create(
|
||||||
Context context,
|
Context context,
|
||||||
EditedMediaItemSequence sequence,
|
EditedMediaItemSequence sequence,
|
||||||
PreviewAudioPipeline previewAudioPipeline,
|
PlaybackAudioGraphWrapper playbackAudioGraphWrapper,
|
||||||
VideoSink videoSink,
|
VideoSink videoSink,
|
||||||
ImageDecoder.Factory imageDecoderFactory) {
|
ImageDecoder.Factory imageDecoderFactory) {
|
||||||
return new SequencePlayerRenderersWrapper(
|
return new SequencePlayerRenderersWrapper(
|
||||||
context, sequence, previewAudioPipeline, videoSink, imageDecoderFactory);
|
context, sequence, playbackAudioGraphWrapper, videoSink, imageDecoderFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a renderers wrapper that for a player that will only play audio. */
|
/** Creates a renderers wrapper that for a player that will only play audio. */
|
||||||
public static SequencePlayerRenderersWrapper createForAudio(
|
public static SequencePlayerRenderersWrapper createForAudio(
|
||||||
Context context,
|
Context context,
|
||||||
EditedMediaItemSequence sequence,
|
EditedMediaItemSequence sequence,
|
||||||
PreviewAudioPipeline previewAudioPipeline) {
|
PlaybackAudioGraphWrapper playbackAudioGraphWrapper) {
|
||||||
return new SequencePlayerRenderersWrapper(
|
return new SequencePlayerRenderersWrapper(
|
||||||
context,
|
context,
|
||||||
sequence,
|
sequence,
|
||||||
previewAudioPipeline,
|
playbackAudioGraphWrapper,
|
||||||
/* videoSink= */ null,
|
/* videoSink= */ null,
|
||||||
/* imageDecoderFactory= */ null);
|
/* imageDecoderFactory= */ null);
|
||||||
}
|
}
|
||||||
@ -93,12 +93,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private SequencePlayerRenderersWrapper(
|
private SequencePlayerRenderersWrapper(
|
||||||
Context context,
|
Context context,
|
||||||
EditedMediaItemSequence sequence,
|
EditedMediaItemSequence sequence,
|
||||||
PreviewAudioPipeline previewAudioPipeline,
|
PlaybackAudioGraphWrapper playbackAudioGraphWrapper,
|
||||||
@Nullable VideoSink videoSink,
|
@Nullable VideoSink videoSink,
|
||||||
@Nullable ImageDecoder.Factory imageDecoderFactory) {
|
@Nullable ImageDecoder.Factory imageDecoderFactory) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.sequence = sequence;
|
this.sequence = sequence;
|
||||||
this.previewAudioPipeline = previewAudioPipeline;
|
this.playbackAudioGraphWrapper = playbackAudioGraphWrapper;
|
||||||
this.videoSink = videoSink;
|
this.videoSink = videoSink;
|
||||||
this.imageDecoderFactory = imageDecoderFactory;
|
this.imageDecoderFactory = imageDecoderFactory;
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
/* sequencePlayerRenderersWrapper= */ this,
|
/* sequencePlayerRenderersWrapper= */ this,
|
||||||
eventHandler,
|
eventHandler,
|
||||||
audioRendererEventListener,
|
audioRendererEventListener,
|
||||||
previewAudioPipeline.createInput()));
|
playbackAudioGraphWrapper.createInput()));
|
||||||
|
|
||||||
if (videoSink != null) {
|
if (videoSink != null) {
|
||||||
renderers.add(
|
renderers.add(
|
||||||
@ -177,7 +177,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||||
super.render(positionUs, elapsedRealtimeUs);
|
super.render(positionUs, elapsedRealtimeUs);
|
||||||
try {
|
try {
|
||||||
while (sequencePlayerRenderersWrapper.previewAudioPipeline.processData()) {}
|
while (sequencePlayerRenderersWrapper.playbackAudioGraphWrapper.processData()) {}
|
||||||
} catch (ExportException
|
} catch (ExportException
|
||||||
| AudioSink.WriteException
|
| AudioSink.WriteException
|
||||||
| AudioSink.InitializationException
|
| AudioSink.InitializationException
|
||||||
|
@ -36,41 +36,41 @@ import org.mockito.Mockito;
|
|||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
|
|
||||||
/** Unit tests for {@link PreviewAudioPipeline}. */
|
/** Unit tests for {@link PlaybackAudioGraphWrapper}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class PreviewAudioPipelineTest {
|
public class PlaybackAudioGraphWrapperTest {
|
||||||
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
||||||
|
|
||||||
private PreviewAudioPipeline previewAudioPipeline;
|
private PlaybackAudioGraphWrapper playbackAudioGraphWrapper;
|
||||||
@Mock AudioSink outputAudioSink;
|
@Mock AudioSink outputAudioSink;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
previewAudioPipeline =
|
playbackAudioGraphWrapper =
|
||||||
new PreviewAudioPipeline(
|
new PlaybackAudioGraphWrapper(
|
||||||
new DefaultAudioMixer.Factory(), /* effects= */ ImmutableList.of(), outputAudioSink);
|
new DefaultAudioMixer.Factory(), /* effects= */ ImmutableList.of(), outputAudioSink);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
previewAudioPipeline.release();
|
playbackAudioGraphWrapper.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void processData_noAudioSinksCreated_returnsFalse() throws Exception {
|
public void processData_noAudioSinksCreated_returnsFalse() throws Exception {
|
||||||
assertThat(previewAudioPipeline.processData()).isFalse();
|
assertThat(playbackAudioGraphWrapper.processData()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void processData_audioSinkHasNotConfiguredYet_returnsFalse() throws Exception {
|
public void processData_audioSinkHasNotConfiguredYet_returnsFalse() throws Exception {
|
||||||
AudioGraphInputAudioSink unused = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink unused = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
assertThat(previewAudioPipeline.processData()).isFalse();
|
assertThat(playbackAudioGraphWrapper.processData()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inputPlay_withOneInput_playsOutputSink() throws Exception {
|
public void inputPlay_withOneInput_playsOutputSink() throws Exception {
|
||||||
AudioGraphInputAudioSink inputAudioSink = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
inputAudioSink.play();
|
inputAudioSink.play();
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ public class PreviewAudioPipelineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inputPause_withOneInput_pausesOutputSink() throws Exception {
|
public void inputPause_withOneInput_pausesOutputSink() throws Exception {
|
||||||
AudioGraphInputAudioSink inputAudioSink = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
inputAudioSink.play();
|
inputAudioSink.play();
|
||||||
inputAudioSink.pause();
|
inputAudioSink.pause();
|
||||||
@ -89,7 +89,7 @@ public class PreviewAudioPipelineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inputReset_withOneInput_pausesOutputSink() {
|
public void inputReset_withOneInput_pausesOutputSink() {
|
||||||
AudioGraphInputAudioSink inputAudioSink = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
inputAudioSink.play();
|
inputAudioSink.play();
|
||||||
inputAudioSink.reset();
|
inputAudioSink.reset();
|
||||||
@ -99,7 +99,7 @@ public class PreviewAudioPipelineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inputPlay_whenPlaying_doesNotPlayOutputSink() throws Exception {
|
public void inputPlay_whenPlaying_doesNotPlayOutputSink() throws Exception {
|
||||||
AudioGraphInputAudioSink inputAudioSink = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink = playbackAudioGraphWrapper.createInput();
|
||||||
inputAudioSink.play();
|
inputAudioSink.play();
|
||||||
inputAudioSink.play();
|
inputAudioSink.play();
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ public class PreviewAudioPipelineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inputPause_whenNotPlaying_doesNotPauseOutputSink() throws Exception {
|
public void inputPause_whenNotPlaying_doesNotPauseOutputSink() throws Exception {
|
||||||
AudioGraphInputAudioSink inputAudioSink = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
inputAudioSink.pause();
|
inputAudioSink.pause();
|
||||||
|
|
||||||
@ -117,9 +117,9 @@ public class PreviewAudioPipelineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void someInputPlay_withMultipleInputs_doesNotPlayOutputSink() throws Exception {
|
public void someInputPlay_withMultipleInputs_doesNotPlayOutputSink() throws Exception {
|
||||||
AudioGraphInputAudioSink inputAudioSink1 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink1 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink inputAudioSink2 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink2 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink unused = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink unused = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
inputAudioSink1.play();
|
inputAudioSink1.play();
|
||||||
inputAudioSink2.play();
|
inputAudioSink2.play();
|
||||||
@ -128,9 +128,9 @@ public class PreviewAudioPipelineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void allInputPlay_withMultipleInputs_playsOutputSinkOnce() throws Exception {
|
public void allInputPlay_withMultipleInputs_playsOutputSinkOnce() throws Exception {
|
||||||
AudioGraphInputAudioSink inputAudioSink1 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink1 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink inputAudioSink2 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink2 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink inputAudioSink3 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink3 = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
inputAudioSink1.play();
|
inputAudioSink1.play();
|
||||||
inputAudioSink2.play();
|
inputAudioSink2.play();
|
||||||
@ -142,9 +142,9 @@ public class PreviewAudioPipelineTest {
|
|||||||
@Test
|
@Test
|
||||||
public void firstInputPause_withMultipleInputs_pausesOutputSink() throws Exception {
|
public void firstInputPause_withMultipleInputs_pausesOutputSink() throws Exception {
|
||||||
InOrder inOrder = inOrder(outputAudioSink);
|
InOrder inOrder = inOrder(outputAudioSink);
|
||||||
AudioGraphInputAudioSink inputAudioSink1 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink1 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink inputAudioSink2 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink2 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink inputAudioSink3 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink3 = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
inputAudioSink1.play();
|
inputAudioSink1.play();
|
||||||
inputAudioSink2.play();
|
inputAudioSink2.play();
|
||||||
@ -157,9 +157,9 @@ public class PreviewAudioPipelineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void allInputPause_withMultipleInputs_pausesOutputSinkOnce() throws Exception {
|
public void allInputPause_withMultipleInputs_pausesOutputSinkOnce() throws Exception {
|
||||||
AudioGraphInputAudioSink inputAudioSink1 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink1 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink inputAudioSink2 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink2 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink inputAudioSink3 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink3 = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
inputAudioSink1.play();
|
inputAudioSink1.play();
|
||||||
inputAudioSink2.play();
|
inputAudioSink2.play();
|
||||||
@ -174,9 +174,9 @@ public class PreviewAudioPipelineTest {
|
|||||||
@Test
|
@Test
|
||||||
public void inputPlayAfterPause_withMultipleInputs_playsOutputSink() throws Exception {
|
public void inputPlayAfterPause_withMultipleInputs_playsOutputSink() throws Exception {
|
||||||
InOrder inOrder = inOrder(outputAudioSink);
|
InOrder inOrder = inOrder(outputAudioSink);
|
||||||
AudioGraphInputAudioSink inputAudioSink1 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink1 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink inputAudioSink2 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink2 = playbackAudioGraphWrapper.createInput();
|
||||||
AudioGraphInputAudioSink inputAudioSink3 = previewAudioPipeline.createInput();
|
AudioGraphInputAudioSink inputAudioSink3 = playbackAudioGraphWrapper.createInput();
|
||||||
|
|
||||||
inputAudioSink1.play();
|
inputAudioSink1.play();
|
||||||
inputAudioSink2.play();
|
inputAudioSink2.play();
|
Loading…
x
Reference in New Issue
Block a user