Move Single/MultiVideoGraph impl to effect
PiperOrigin-RevId: 569188658
This commit is contained in:
parent
d716de02aa
commit
cd6f8a42a5
@ -24,6 +24,7 @@ import androidx.media3.common.util.UnstableApi;
|
|||||||
public interface VideoGraph {
|
public interface VideoGraph {
|
||||||
|
|
||||||
/** Listener for video frame processing events. */
|
/** Listener for video frame processing events. */
|
||||||
|
@UnstableApi
|
||||||
interface Listener {
|
interface Listener {
|
||||||
/**
|
/**
|
||||||
* Called when the output size changes.
|
* Called when the output size changes.
|
||||||
@ -58,6 +59,26 @@ public interface VideoGraph {
|
|||||||
*/
|
*/
|
||||||
void initialize() throws VideoFrameProcessingException;
|
void initialize() throws VideoFrameProcessingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new input to the {@code VideoGraph}.
|
||||||
|
*
|
||||||
|
* <p>A underlying processing {@link VideoFrameProcessor} is created every time this method is
|
||||||
|
* called.
|
||||||
|
*
|
||||||
|
* <p>If the method throws, the caller must call {@link #release}.
|
||||||
|
*
|
||||||
|
* @return The id of the registered input, which can be used to get the underlying {@link
|
||||||
|
* VideoFrameProcessor} via {@link #getProcessor(int)}.
|
||||||
|
*/
|
||||||
|
int registerInput() throws VideoFrameProcessingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link VideoFrameProcessor} that handles the processing for an input registered via
|
||||||
|
* {@link #registerInput()}. If the {@code inputId} is not {@linkplain #registerInput()
|
||||||
|
* registered} before, this method will throw an {@link IllegalStateException}.
|
||||||
|
*/
|
||||||
|
VideoFrameProcessor getProcessor(int inputId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the output surface and supporting information.
|
* Sets the output surface and supporting information.
|
||||||
*
|
*
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.effect;
|
||||||
|
|
||||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID;
|
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID;
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
@ -45,12 +45,7 @@ import androidx.media3.common.VideoFrameProcessingException;
|
|||||||
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.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import androidx.media3.effect.DefaultGlObjectsProvider;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.effect.DefaultVideoCompositor;
|
|
||||||
import androidx.media3.effect.DefaultVideoFrameProcessor;
|
|
||||||
import androidx.media3.effect.GlTextureProducer;
|
|
||||||
import androidx.media3.effect.VideoCompositor;
|
|
||||||
import androidx.media3.effect.VideoCompositorSettings;
|
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -61,32 +56,8 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/** A {@link VideoGraph} that handles multiple input streams. */
|
/** A {@link VideoGraph} that handles multiple input streams. */
|
||||||
/* package */ final class MultipleInputVideoGraph implements TransformerVideoGraph {
|
@UnstableApi
|
||||||
|
public abstract class MultipleInputVideoGraph implements VideoGraph {
|
||||||
public static final class Factory implements TransformerVideoGraph.Factory {
|
|
||||||
@Override
|
|
||||||
public MultipleInputVideoGraph create(
|
|
||||||
Context context,
|
|
||||||
ColorInfo inputColorInfo,
|
|
||||||
ColorInfo outputColorInfo,
|
|
||||||
DebugViewProvider debugViewProvider,
|
|
||||||
Listener listener,
|
|
||||||
Executor listenerExecutor,
|
|
||||||
VideoCompositorSettings videoCompositorSettings,
|
|
||||||
List<Effect> compositionEffects,
|
|
||||||
long initialTimestampOffsetUs) {
|
|
||||||
return new MultipleInputVideoGraph(
|
|
||||||
context,
|
|
||||||
inputColorInfo,
|
|
||||||
outputColorInfo,
|
|
||||||
debugViewProvider,
|
|
||||||
listener,
|
|
||||||
listenerExecutor,
|
|
||||||
videoCompositorSettings,
|
|
||||||
compositionEffects,
|
|
||||||
initialTimestampOffsetUs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String SHARED_EXECUTOR_NAME = "Transformer:MultipleInputVideoGraph:Thread";
|
private static final String SHARED_EXECUTOR_NAME = "Transformer:MultipleInputVideoGraph:Thread";
|
||||||
|
|
||||||
@ -95,15 +66,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private static final int COMPOSITOR_TEXTURE_OUTPUT_CAPACITY = 1;
|
private static final int COMPOSITOR_TEXTURE_OUTPUT_CAPACITY = 1;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
private final ColorInfo inputColorInfo;
|
private final ColorInfo inputColorInfo;
|
||||||
private final ColorInfo outputColorInfo;
|
private final ColorInfo outputColorInfo;
|
||||||
private final GlObjectsProvider glObjectsProvider;
|
private final GlObjectsProvider glObjectsProvider;
|
||||||
private final DebugViewProvider debugViewProvider;
|
private final DebugViewProvider debugViewProvider;
|
||||||
private final Listener listener;
|
private final VideoGraph.Listener listener;
|
||||||
private final Executor listenerExecutor;
|
private final Executor listenerExecutor;
|
||||||
private final VideoCompositorSettings videoCompositorSettings;
|
private final VideoCompositorSettings videoCompositorSettings;
|
||||||
private final List<Effect> compositionEffects;
|
private final List<Effect> compositionEffects;
|
||||||
private final List<VideoFrameProcessingWrapper> preProcessingWrappers;
|
private final List<VideoFrameProcessor> preProcessors;
|
||||||
|
|
||||||
private final ExecutorService sharedExecutorService;
|
private final ExecutorService sharedExecutorService;
|
||||||
|
|
||||||
@ -124,12 +96,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
private volatile boolean hasProducedFrameWithTimestampZero;
|
private volatile boolean hasProducedFrameWithTimestampZero;
|
||||||
|
|
||||||
private MultipleInputVideoGraph(
|
protected MultipleInputVideoGraph(
|
||||||
Context context,
|
Context context,
|
||||||
ColorInfo inputColorInfo,
|
ColorInfo inputColorInfo,
|
||||||
ColorInfo outputColorInfo,
|
ColorInfo outputColorInfo,
|
||||||
DebugViewProvider debugViewProvider,
|
DebugViewProvider debugViewProvider,
|
||||||
Listener listener,
|
VideoGraph.Listener listener,
|
||||||
Executor listenerExecutor,
|
Executor listenerExecutor,
|
||||||
VideoCompositorSettings videoCompositorSettings,
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
List<Effect> compositionEffects,
|
List<Effect> compositionEffects,
|
||||||
@ -144,7 +116,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
this.compositionEffects = new ArrayList<>(compositionEffects);
|
this.compositionEffects = new ArrayList<>(compositionEffects);
|
||||||
this.initialTimestampOffsetUs = initialTimestampOffsetUs;
|
this.initialTimestampOffsetUs = initialTimestampOffsetUs;
|
||||||
lastRenderedPresentationTimeUs = C.TIME_UNSET;
|
lastRenderedPresentationTimeUs = C.TIME_UNSET;
|
||||||
preProcessingWrappers = new ArrayList<>();
|
preProcessors = new ArrayList<>();
|
||||||
sharedExecutorService = newSingleThreadScheduledExecutor(SHARED_EXECUTOR_NAME);
|
sharedExecutorService = newSingleThreadScheduledExecutor(SHARED_EXECUTOR_NAME);
|
||||||
glObjectsProvider = new SingleContextGlObjectsProvider();
|
glObjectsProvider = new SingleContextGlObjectsProvider();
|
||||||
// TODO - b/289986435: Support injecting VideoFrameProcessor.Factory.
|
// TODO - b/289986435: Support injecting VideoFrameProcessor.Factory.
|
||||||
@ -165,7 +137,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@Override
|
@Override
|
||||||
public void initialize() throws VideoFrameProcessingException {
|
public void initialize() throws VideoFrameProcessingException {
|
||||||
checkState(
|
checkState(
|
||||||
preProcessingWrappers.isEmpty()
|
preProcessors.isEmpty()
|
||||||
&& videoCompositor == null
|
&& videoCompositor == null
|
||||||
&& compositionVideoFrameProcessor == null
|
&& compositionVideoFrameProcessor == null
|
||||||
&& !released);
|
&& !released);
|
||||||
@ -243,56 +215,61 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphInput createInput() throws VideoFrameProcessingException {
|
public int registerInput() throws VideoFrameProcessingException {
|
||||||
checkStateNotNull(videoCompositor);
|
checkStateNotNull(videoCompositor);
|
||||||
|
|
||||||
int videoCompositorInputId = videoCompositor.registerInputSource();
|
int videoCompositorInputId = videoCompositor.registerInputSource();
|
||||||
// Creating a new VideoFrameProcessor for the input.
|
// Creating a new VideoFrameProcessor for the input.
|
||||||
VideoFrameProcessingWrapper preProcessingVideoFrameProcessorWrapper =
|
VideoFrameProcessor preProcessor =
|
||||||
new VideoFrameProcessingWrapper(
|
videoFrameProcessorFactory
|
||||||
context,
|
.buildUpon()
|
||||||
videoFrameProcessorFactory
|
.setTextureOutput(
|
||||||
.buildUpon()
|
// Texture output to compositor.
|
||||||
.setTextureOutput(
|
(textureProducer, texture, presentationTimeUs, syncObject) ->
|
||||||
// Texture output to compositor.
|
queuePreProcessingOutputToCompositor(
|
||||||
(textureProducer, texture, presentationTimeUs, syncObject) ->
|
videoCompositorInputId, textureProducer, texture, presentationTimeUs),
|
||||||
queuePreProcessingOutputToCompositor(
|
PRE_COMPOSITOR_TEXTURE_OUTPUT_CAPACITY)
|
||||||
videoCompositorInputId, textureProducer, texture, presentationTimeUs),
|
.build()
|
||||||
PRE_COMPOSITOR_TEXTURE_OUTPUT_CAPACITY)
|
.create(
|
||||||
.build(),
|
context,
|
||||||
inputColorInfo,
|
DebugViewProvider.NONE,
|
||||||
outputColorInfo,
|
inputColorInfo,
|
||||||
DebugViewProvider.NONE,
|
outputColorInfo,
|
||||||
listenerExecutor,
|
// Pre-processors render frames as soon as available, to VideoCompositor.
|
||||||
new VideoFrameProcessor.Listener() {
|
/* renderFramesAutomatically= */ true,
|
||||||
// All of this listener's methods are called on the sharedExecutorService.
|
listenerExecutor,
|
||||||
@Override
|
new VideoFrameProcessor.Listener() {
|
||||||
public void onInputStreamRegistered(
|
// All of this listener's methods are called on the sharedExecutorService.
|
||||||
@VideoFrameProcessor.InputType int inputType,
|
@Override
|
||||||
List<Effect> effects,
|
public void onInputStreamRegistered(
|
||||||
FrameInfo frameInfo) {}
|
@VideoFrameProcessor.InputType int inputType,
|
||||||
|
List<Effect> effects,
|
||||||
|
FrameInfo frameInfo) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOutputSizeChanged(int width, int height) {}
|
public void onOutputSizeChanged(int width, int height) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOutputFrameAvailableForRendering(long presentationTimeUs) {}
|
public void onOutputFrameAvailableForRendering(long presentationTimeUs) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(VideoFrameProcessingException exception) {
|
public void onError(VideoFrameProcessingException exception) {
|
||||||
handleVideoFrameProcessingException(exception);
|
handleVideoFrameProcessingException(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnded() {
|
public void onEnded() {
|
||||||
onPreProcessingVideoFrameProcessorEnded(videoCompositorInputId);
|
onPreProcessingVideoFrameProcessorEnded(videoCompositorInputId);
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
/* renderFramesAutomatically= */ true,
|
preProcessors.add(preProcessor);
|
||||||
/* presentation= */ null,
|
return videoCompositorInputId;
|
||||||
initialTimestampOffsetUs);
|
}
|
||||||
preProcessingWrappers.add(preProcessingVideoFrameProcessorWrapper);
|
|
||||||
return preProcessingVideoFrameProcessorWrapper;
|
@Override
|
||||||
|
public VideoFrameProcessor getProcessor(int inputId) {
|
||||||
|
checkState(inputId < preProcessors.size());
|
||||||
|
return preProcessors.get(inputId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -312,10 +289,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Needs to release the frame processors before their internal executor services are released.
|
// Needs to release the frame processors before their internal executor services are released.
|
||||||
for (int i = 0; i < preProcessingWrappers.size(); i++) {
|
for (int i = 0; i < preProcessors.size(); i++) {
|
||||||
preProcessingWrappers.get(i).release();
|
preProcessors.get(i).release();
|
||||||
}
|
}
|
||||||
preProcessingWrappers.clear();
|
preProcessors.clear();
|
||||||
|
|
||||||
if (videoCompositor != null) {
|
if (videoCompositor != null) {
|
||||||
videoCompositor.release();
|
videoCompositor.release();
|
||||||
@ -338,6 +315,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
released = true;
|
released = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ColorInfo getInputColorInfo() {
|
||||||
|
return inputColorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getInitialTimestampOffsetUs() {
|
||||||
|
return initialTimestampOffsetUs;
|
||||||
|
}
|
||||||
|
|
||||||
// This method is called on the sharedExecutorService.
|
// This method is called on the sharedExecutorService.
|
||||||
private void queuePreProcessingOutputToCompositor(
|
private void queuePreProcessingOutputToCompositor(
|
||||||
int videoCompositorInputId,
|
int videoCompositorInputId,
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.effect;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
@ -30,13 +30,16 @@ import androidx.media3.common.SurfaceInfo;
|
|||||||
import androidx.media3.common.VideoFrameProcessingException;
|
import androidx.media3.common.VideoFrameProcessingException;
|
||||||
import androidx.media3.common.VideoFrameProcessor;
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.common.VideoGraph;
|
import androidx.media3.common.VideoGraph;
|
||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.effect.VideoCompositorSettings;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
/** A {@link VideoGraph} that handles one input stream. */
|
/** A {@link VideoGraph} that handles one input stream. */
|
||||||
/* package */ abstract class SingleInputVideoGraph implements VideoGraph {
|
@UnstableApi
|
||||||
|
public abstract class SingleInputVideoGraph implements VideoGraph {
|
||||||
|
|
||||||
|
/** The ID {@link #registerInput()} returns. */
|
||||||
|
public static final int SINGLE_INPUT_INDEX = 0;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||||
@ -46,10 +49,11 @@ import java.util.concurrent.Executor;
|
|||||||
private final DebugViewProvider debugViewProvider;
|
private final DebugViewProvider debugViewProvider;
|
||||||
private final Executor listenerExecutor;
|
private final Executor listenerExecutor;
|
||||||
private final boolean renderFramesAutomatically;
|
private final boolean renderFramesAutomatically;
|
||||||
|
|
||||||
private final long initialTimestampOffsetUs;
|
private final long initialTimestampOffsetUs;
|
||||||
@Nullable private final Presentation presentation;
|
@Nullable private final Presentation presentation;
|
||||||
|
|
||||||
@Nullable private VideoFrameProcessingWrapper videoFrameProcessingWrapper;
|
@Nullable private VideoFrameProcessor videoFrameProcessor;
|
||||||
|
|
||||||
private boolean released;
|
private boolean released;
|
||||||
private volatile boolean hasProducedFrameWithTimestampZero;
|
private volatile boolean hasProducedFrameWithTimestampZero;
|
||||||
@ -93,16 +97,21 @@ import java.util.concurrent.Executor;
|
|||||||
* <p>This method must be called at most once.
|
* <p>This method must be called at most once.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void initialize() throws VideoFrameProcessingException {
|
public void initialize() {
|
||||||
checkStateNotNull(videoFrameProcessingWrapper == null && !released);
|
// Initialization is deferred to registerInput();
|
||||||
|
}
|
||||||
|
|
||||||
videoFrameProcessingWrapper =
|
@Override
|
||||||
new VideoFrameProcessingWrapper(
|
public int registerInput() throws VideoFrameProcessingException {
|
||||||
|
checkStateNotNull(videoFrameProcessor == null && !released);
|
||||||
|
|
||||||
|
videoFrameProcessor =
|
||||||
|
videoFrameProcessorFactory.create(
|
||||||
context,
|
context,
|
||||||
videoFrameProcessorFactory,
|
debugViewProvider,
|
||||||
inputColorInfo,
|
inputColorInfo,
|
||||||
outputColorInfo,
|
outputColorInfo,
|
||||||
debugViewProvider,
|
renderFramesAutomatically,
|
||||||
listenerExecutor,
|
listenerExecutor,
|
||||||
new VideoFrameProcessor.Listener() {
|
new VideoFrameProcessor.Listener() {
|
||||||
private long lastProcessedFramePresentationTimeUs;
|
private long lastProcessedFramePresentationTimeUs;
|
||||||
@ -136,15 +145,18 @@ import java.util.concurrent.Executor;
|
|||||||
public void onEnded() {
|
public void onEnded() {
|
||||||
listener.onEnded(lastProcessedFramePresentationTimeUs);
|
listener.onEnded(lastProcessedFramePresentationTimeUs);
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
renderFramesAutomatically,
|
return SINGLE_INPUT_INDEX;
|
||||||
presentation,
|
}
|
||||||
initialTimestampOffsetUs);
|
|
||||||
|
@Override
|
||||||
|
public VideoFrameProcessor getProcessor(int inputId) {
|
||||||
|
return checkStateNotNull(videoFrameProcessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOutputSurfaceInfo(@Nullable SurfaceInfo outputSurfaceInfo) {
|
public void setOutputSurfaceInfo(@Nullable SurfaceInfo outputSurfaceInfo) {
|
||||||
checkNotNull(videoFrameProcessingWrapper).setOutputSurfaceInfo(outputSurfaceInfo);
|
checkNotNull(videoFrameProcessor).setOutputSurfaceInfo(outputSurfaceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -158,14 +170,23 @@ import java.util.concurrent.Executor;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoFrameProcessingWrapper != null) {
|
if (videoFrameProcessor != null) {
|
||||||
videoFrameProcessingWrapper.release();
|
videoFrameProcessor.release();
|
||||||
videoFrameProcessingWrapper = null;
|
videoFrameProcessor = null;
|
||||||
}
|
}
|
||||||
released = true;
|
released = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected VideoFrameProcessingWrapper getVideoFrameProcessingWrapper() {
|
protected ColorInfo getInputColorInfo() {
|
||||||
return checkNotNull(videoFrameProcessingWrapper);
|
return inputColorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getInitialTimestampOffsetUs() {
|
||||||
|
return initialTimestampOffsetUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected Presentation getPresentation() {
|
||||||
|
return presentation;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.media3.common.ColorInfo;
|
||||||
|
import androidx.media3.common.DebugViewProvider;
|
||||||
|
import androidx.media3.common.Effect;
|
||||||
|
import androidx.media3.common.VideoFrameProcessingException;
|
||||||
|
import androidx.media3.common.VideoGraph;
|
||||||
|
import androidx.media3.effect.MultipleInputVideoGraph;
|
||||||
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link TransformerVideoGraph Transformer}-specific implementation of {@link
|
||||||
|
* MultipleInputVideoGraph}.
|
||||||
|
*/
|
||||||
|
/* package */ final class TransformerMultipleInputVideoGraph extends MultipleInputVideoGraph
|
||||||
|
implements TransformerVideoGraph {
|
||||||
|
|
||||||
|
/** A factory for creating {@link TransformerMultipleInputVideoGraph} instances. */
|
||||||
|
public static final class Factory implements TransformerVideoGraph.Factory {
|
||||||
|
@Override
|
||||||
|
public TransformerMultipleInputVideoGraph create(
|
||||||
|
Context context,
|
||||||
|
ColorInfo inputColorInfo,
|
||||||
|
ColorInfo outputColorInfo,
|
||||||
|
DebugViewProvider debugViewProvider,
|
||||||
|
VideoGraph.Listener listener,
|
||||||
|
Executor listenerExecutor,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
|
List<Effect> compositionEffects,
|
||||||
|
long initialTimestampOffsetUs) {
|
||||||
|
return new TransformerMultipleInputVideoGraph(
|
||||||
|
context,
|
||||||
|
inputColorInfo,
|
||||||
|
outputColorInfo,
|
||||||
|
debugViewProvider,
|
||||||
|
listener,
|
||||||
|
listenerExecutor,
|
||||||
|
videoCompositorSettings,
|
||||||
|
compositionEffects,
|
||||||
|
initialTimestampOffsetUs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TransformerMultipleInputVideoGraph(
|
||||||
|
Context context,
|
||||||
|
ColorInfo inputColorInfo,
|
||||||
|
ColorInfo outputColorInfo,
|
||||||
|
DebugViewProvider debugViewProvider,
|
||||||
|
Listener listener,
|
||||||
|
Executor listenerExecutor,
|
||||||
|
VideoCompositorSettings videoCompositorSettings,
|
||||||
|
List<Effect> compositionEffects,
|
||||||
|
long initialTimestampOffsetUs) {
|
||||||
|
super(
|
||||||
|
context,
|
||||||
|
inputColorInfo,
|
||||||
|
outputColorInfo,
|
||||||
|
debugViewProvider,
|
||||||
|
listener,
|
||||||
|
listenerExecutor,
|
||||||
|
videoCompositorSettings,
|
||||||
|
compositionEffects,
|
||||||
|
initialTimestampOffsetUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GraphInput createInput() throws VideoFrameProcessingException {
|
||||||
|
int inputId = registerInput();
|
||||||
|
return new VideoFrameProcessingWrapper(
|
||||||
|
getProcessor(inputId),
|
||||||
|
getInputColorInfo(),
|
||||||
|
/* presentation= */ null,
|
||||||
|
getInitialTimestampOffsetUs());
|
||||||
|
}
|
||||||
|
}
|
@ -16,21 +16,30 @@
|
|||||||
|
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.ColorInfo;
|
import androidx.media3.common.ColorInfo;
|
||||||
import androidx.media3.common.DebugViewProvider;
|
import androidx.media3.common.DebugViewProvider;
|
||||||
import androidx.media3.common.Effect;
|
import androidx.media3.common.Effect;
|
||||||
|
import androidx.media3.common.VideoFrameProcessingException;
|
||||||
import androidx.media3.common.VideoFrameProcessor;
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.effect.Presentation;
|
||||||
|
import androidx.media3.effect.SingleInputVideoGraph;
|
||||||
import androidx.media3.effect.VideoCompositorSettings;
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link TransformerVideoGraph Transformer}-specific implementation of {@link
|
||||||
|
* SingleInputVideoGraph}.
|
||||||
|
*/
|
||||||
/* package */ final class TransformerSingleInputVideoGraph extends SingleInputVideoGraph
|
/* package */ final class TransformerSingleInputVideoGraph extends SingleInputVideoGraph
|
||||||
implements TransformerVideoGraph {
|
implements TransformerVideoGraph {
|
||||||
|
|
||||||
/** A factory for creating a {@link SingleInputVideoGraph}. */
|
/** A factory for creating {@link TransformerSingleInputVideoGraph} instances. */
|
||||||
public static final class Factory implements TransformerVideoGraph.Factory {
|
public static final class Factory implements TransformerVideoGraph.Factory {
|
||||||
|
|
||||||
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||||
@ -40,7 +49,7 @@ import java.util.concurrent.Executor;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransformerVideoGraph create(
|
public TransformerSingleInputVideoGraph create(
|
||||||
Context context,
|
Context context,
|
||||||
ColorInfo inputColorInfo,
|
ColorInfo inputColorInfo,
|
||||||
ColorInfo outputColorInfo,
|
ColorInfo outputColorInfo,
|
||||||
@ -72,6 +81,8 @@ import java.util.concurrent.Executor;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @MonotonicNonNull VideoFrameProcessingWrapper videoFrameProcessingWrapper;
|
||||||
|
|
||||||
private TransformerSingleInputVideoGraph(
|
private TransformerSingleInputVideoGraph(
|
||||||
Context context,
|
Context context,
|
||||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||||
@ -99,7 +110,15 @@ import java.util.concurrent.Executor;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphInput createInput() {
|
public GraphInput createInput() throws VideoFrameProcessingException {
|
||||||
return getVideoFrameProcessingWrapper();
|
checkState(videoFrameProcessingWrapper == null);
|
||||||
|
int inputId = registerInput();
|
||||||
|
videoFrameProcessingWrapper =
|
||||||
|
new VideoFrameProcessingWrapper(
|
||||||
|
getProcessor(inputId),
|
||||||
|
getInputColorInfo(),
|
||||||
|
getPresentation(),
|
||||||
|
getInitialTimestampOffsetUs());
|
||||||
|
return videoFrameProcessingWrapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,26 +21,22 @@ import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_SURFACE;
|
|||||||
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID;
|
import static androidx.media3.common.VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID;
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.ColorInfo;
|
import androidx.media3.common.ColorInfo;
|
||||||
import androidx.media3.common.DebugViewProvider;
|
|
||||||
import androidx.media3.common.Effect;
|
import androidx.media3.common.Effect;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.FrameInfo;
|
import androidx.media3.common.FrameInfo;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.OnInputFrameProcessedListener;
|
import androidx.media3.common.OnInputFrameProcessedListener;
|
||||||
import androidx.media3.common.SurfaceInfo;
|
import androidx.media3.common.SurfaceInfo;
|
||||||
import androidx.media3.common.VideoFrameProcessingException;
|
|
||||||
import androidx.media3.common.VideoFrameProcessor;
|
import androidx.media3.common.VideoFrameProcessor;
|
||||||
import androidx.media3.common.util.Size;
|
import androidx.media3.common.util.Size;
|
||||||
import androidx.media3.common.util.TimestampIterator;
|
import androidx.media3.common.util.TimestampIterator;
|
||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.effect.Presentation;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
/** A wrapper for {@link VideoFrameProcessor} that handles {@link GraphInput} events. */
|
/** A wrapper for {@link VideoFrameProcessor} that handles {@link GraphInput} events. */
|
||||||
@ -52,31 +48,15 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
@Nullable final Presentation presentation;
|
@Nullable final Presentation presentation;
|
||||||
|
|
||||||
public VideoFrameProcessingWrapper(
|
public VideoFrameProcessingWrapper(
|
||||||
Context context,
|
VideoFrameProcessor videoFrameProcessor,
|
||||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
|
||||||
ColorInfo inputColorInfo,
|
ColorInfo inputColorInfo,
|
||||||
ColorInfo outputColorInfo,
|
|
||||||
DebugViewProvider debugViewProvider,
|
|
||||||
Executor listenerExecutor,
|
|
||||||
VideoFrameProcessor.Listener listener,
|
|
||||||
boolean renderFramesAutomatically,
|
|
||||||
@Nullable Presentation presentation,
|
@Nullable Presentation presentation,
|
||||||
long initialTimestampOffsetUs)
|
long initialTimestampOffsetUs) {
|
||||||
throws VideoFrameProcessingException {
|
this.videoFrameProcessor = videoFrameProcessor;
|
||||||
this.mediaItemOffsetUs = new AtomicLong();
|
this.mediaItemOffsetUs = new AtomicLong();
|
||||||
this.inputColorInfo = inputColorInfo;
|
this.inputColorInfo = inputColorInfo;
|
||||||
this.initialTimestampOffsetUs = initialTimestampOffsetUs;
|
this.initialTimestampOffsetUs = initialTimestampOffsetUs;
|
||||||
this.presentation = presentation;
|
this.presentation = presentation;
|
||||||
|
|
||||||
videoFrameProcessor =
|
|
||||||
videoFrameProcessorFactory.create(
|
|
||||||
context,
|
|
||||||
debugViewProvider,
|
|
||||||
inputColorInfo,
|
|
||||||
outputColorInfo,
|
|
||||||
renderFramesAutomatically,
|
|
||||||
listenerExecutor,
|
|
||||||
listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -147,7 +147,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
new VideoGraphWrapper(
|
new VideoGraphWrapper(
|
||||||
context,
|
context,
|
||||||
hasMultipleInputs
|
hasMultipleInputs
|
||||||
? new MultipleInputVideoGraph.Factory()
|
? new TransformerMultipleInputVideoGraph.Factory()
|
||||||
: new TransformerSingleInputVideoGraph.Factory(videoFrameProcessorFactory),
|
: new TransformerSingleInputVideoGraph.Factory(videoFrameProcessorFactory),
|
||||||
videoGraphInputColor,
|
videoGraphInputColor,
|
||||||
videoGraphOutputColor,
|
videoGraphOutputColor,
|
||||||
@ -533,6 +533,16 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
videoGraph.initialize();
|
videoGraph.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int registerInput() throws VideoFrameProcessingException {
|
||||||
|
return videoGraph.registerInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VideoFrameProcessor getProcessor(int inputId) {
|
||||||
|
return videoGraph.getProcessor(inputId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GraphInput createInput() throws VideoFrameProcessingException {
|
public GraphInput createInput() throws VideoFrameProcessingException {
|
||||||
return videoGraph.createInput();
|
return videoGraph.createInput();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user