mirror of
https://github.com/samsonjs/media.git
synced 2026-03-29 10:05:48 +00:00
Update effect to take in and use a GlObjectsProvider
PiperOrigin-RevId: 514744747
This commit is contained in:
parent
0bec22f31b
commit
82ab327e02
12 changed files with 188 additions and 65 deletions
|
|
@ -25,6 +25,7 @@ import android.opengl.EGL14;
|
|||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.effect.GlShaderProgram;
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.GlTextureInfo;
|
||||
import com.google.android.exoplayer2.util.LibraryLoader;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
|
@ -159,6 +160,9 @@ import java.util.concurrent.Future;
|
|||
() -> errorListener.onError(new VideoFrameProcessingException(error))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {}
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
AppTextureFrame appTextureFrame =
|
||||
|
|
|
|||
|
|
@ -41,6 +41,15 @@ public interface VideoFrameProcessor {
|
|||
|
||||
/** A factory for {@link VideoFrameProcessor} instances. */
|
||||
interface Factory {
|
||||
|
||||
/**
|
||||
* Sets the {@link GlObjectsProvider}.
|
||||
*
|
||||
* <p>Must be called before {@link #create}.
|
||||
*/
|
||||
Factory setGlObjectsProvider(GlObjectsProvider glObjectsProvider);
|
||||
|
||||
// TODO(271433904): Turn parameters with default values into setters.
|
||||
/**
|
||||
* Creates a new {@link VideoFrameProcessor} instance.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.DebugViewProvider;
|
||||
import com.google.android.exoplayer2.util.FrameInfo;
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.GlTextureInfo;
|
||||
import com.google.android.exoplayer2.util.GlUtil;
|
||||
import com.google.android.exoplayer2.util.SurfaceInfo;
|
||||
|
|
@ -410,6 +411,9 @@ public final class DefaultVideoFrameProcessorVideoFrameReleaseTest {
|
|||
@Override
|
||||
public void setErrorListener(Executor executor, ErrorListener errorListener) {}
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {}
|
||||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
// No input is queued in these tests. The BlankFrameProducer is used to produce frames.
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import com.google.android.exoplayer2.C;
|
|||
import com.google.android.exoplayer2.util.DebugViewProvider;
|
||||
import com.google.android.exoplayer2.util.Effect;
|
||||
import com.google.android.exoplayer2.util.FrameInfo;
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.GlUtil;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
import com.google.android.exoplayer2.util.SurfaceInfo;
|
||||
|
|
@ -60,6 +61,20 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||
|
||||
/** A factory for {@link DefaultVideoFrameProcessor} instances. */
|
||||
public static final class Factory implements VideoFrameProcessor.Factory {
|
||||
private GlObjectsProvider glObjectsProvider = GlObjectsProvider.DEFAULT;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default value is {@link GlObjectsProvider#DEFAULT}.
|
||||
*/
|
||||
@Override
|
||||
public DefaultVideoFrameProcessor.Factory setGlObjectsProvider(
|
||||
GlObjectsProvider glObjectsProvider) {
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
|
@ -137,7 +152,8 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||
releaseFramesAutomatically,
|
||||
singleThreadExecutorService,
|
||||
listenerExecutor,
|
||||
listener));
|
||||
listener,
|
||||
glObjectsProvider));
|
||||
|
||||
try {
|
||||
return defaultVideoFrameProcessorFuture.get();
|
||||
|
|
@ -365,7 +381,8 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||
boolean releaseFramesAutomatically,
|
||||
ExecutorService singleThreadExecutorService,
|
||||
Executor executor,
|
||||
Listener listener)
|
||||
Listener listener,
|
||||
GlObjectsProvider glObjectsProvider)
|
||||
throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
||||
|
||||
|
|
@ -378,7 +395,8 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||
: GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_8888;
|
||||
int openGlVersion =
|
||||
ColorInfo.isTransferHdr(inputColorInfo) || ColorInfo.isTransferHdr(outputColorInfo) ? 3 : 2;
|
||||
EGLContext eglContext = GlUtil.createEglContext(eglDisplay, openGlVersion, configAttributes);
|
||||
EGLContext eglContext =
|
||||
glObjectsProvider.createEglContext(eglDisplay, openGlVersion, configAttributes);
|
||||
GlUtil.createFocusedPlaceholderEglSurface(eglContext, eglDisplay, configAttributes);
|
||||
|
||||
// Not releaseFramesAutomatically means outputting to a display surface. HDR display surfaces
|
||||
|
|
@ -407,6 +425,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||
releaseFramesAutomatically,
|
||||
executor,
|
||||
listener);
|
||||
setGlObjectProviderOnShaderPrograms(shaderPrograms, glObjectsProvider);
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor =
|
||||
new VideoFrameProcessingTaskExecutor(singleThreadExecutorService, listener);
|
||||
chainShaderProgramsWithListeners(
|
||||
|
|
@ -520,6 +539,15 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
|||
return shaderProgramListBuilder.build();
|
||||
}
|
||||
|
||||
/** Sets the {@link GlObjectsProvider} on all of the {@linkplain GlShaderProgram}s provided. */
|
||||
private static void setGlObjectProviderOnShaderPrograms(
|
||||
ImmutableList<GlShaderProgram> shaderPrograms, GlObjectsProvider glObjectsProvider) {
|
||||
for (int i = 0; i < shaderPrograms.size() - 1; i++) {
|
||||
GlShaderProgram shaderProgram = shaderPrograms.get(i);
|
||||
shaderProgram.setGlObjectsProvider(glObjectsProvider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chains the given {@link GlShaderProgram} instances using {@link
|
||||
* ChainingGlShaderProgramListener} instances.
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import androidx.annotation.GuardedBy;
|
|||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.DebugViewProvider;
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.GlTextureInfo;
|
||||
import com.google.android.exoplayer2.util.GlUtil;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
|
|
@ -86,9 +87,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
private int inputHeight;
|
||||
@Nullable private DefaultShaderProgram defaultShaderProgram;
|
||||
@Nullable private SurfaceViewWrapper debugSurfaceViewWrapper;
|
||||
private GlObjectsProvider glObjectsProvider;
|
||||
private InputListener inputListener;
|
||||
private @MonotonicNonNull Size outputSizeBeforeSurfaceTransformation;
|
||||
@Nullable private SurfaceView debugSurfaceView;
|
||||
private boolean frameProcessingStarted;
|
||||
|
||||
private volatile boolean outputSizeOrRotationChanged;
|
||||
|
||||
|
|
@ -130,10 +133,19 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
|
||||
textureTransformMatrix = GlUtil.create4x4IdentityMatrix();
|
||||
streamOffsetUsQueue = new ConcurrentLinkedQueue<>();
|
||||
glObjectsProvider = GlObjectsProvider.DEFAULT;
|
||||
inputListener = new InputListener() {};
|
||||
availableFrames = new ConcurrentLinkedQueue<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
|
||||
checkState(
|
||||
!frameProcessingStarted,
|
||||
"The GlObjectsProvider cannot be set after frame processing has started.");
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputListener(InputListener inputListener) {
|
||||
this.inputListener = inputListener;
|
||||
|
|
@ -154,6 +166,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
|
||||
@Override
|
||||
public void signalEndOfCurrentInputStream() {
|
||||
frameProcessingStarted = true;
|
||||
checkState(!streamOffsetUsQueue.isEmpty(), "No input stream to end.");
|
||||
streamOffsetUsQueue.remove();
|
||||
if (streamOffsetUsQueue.isEmpty()) {
|
||||
|
|
@ -178,6 +191,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
frameProcessingStarted = true;
|
||||
long streamOffsetUs =
|
||||
checkStateNotNull(streamOffsetUsQueue.peek(), "No input stream specified.");
|
||||
long offsetPresentationTimeUs = presentationTimeUs + streamOffsetUs;
|
||||
|
|
@ -199,6 +213,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
}
|
||||
|
||||
public void releaseOutputFrame(long releaseTimeNs) {
|
||||
frameProcessingStarted = true;
|
||||
checkState(!releaseFramesAutomatically);
|
||||
Pair<GlTextureInfo, Long> oldestAvailableFrame = availableFrames.remove();
|
||||
renderFrameToSurfaces(
|
||||
|
|
@ -209,6 +224,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
|
||||
@Override
|
||||
public void flush() {
|
||||
frameProcessingStarted = true;
|
||||
// Drops all frames that aren't released yet.
|
||||
availableFrames.clear();
|
||||
if (defaultShaderProgram != null) {
|
||||
|
|
@ -304,7 +320,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
outputEglSurface,
|
||||
outputSurfaceInfo.width,
|
||||
outputSurfaceInfo.height);
|
||||
GlUtil.clearOutputFrame();
|
||||
glObjectsProvider.clearOutputFrame();
|
||||
defaultShaderProgram.drawFrame(inputTexture.texId, presentationTimeUs);
|
||||
|
||||
EGLExt.eglPresentationTimeANDROID(
|
||||
|
|
@ -446,7 +462,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
try {
|
||||
debugSurfaceViewWrapper.maybeRenderToSurfaceView(
|
||||
() -> {
|
||||
GlUtil.clearOutputFrame();
|
||||
glObjectsProvider.clearOutputFrame();
|
||||
@C.ColorTransfer
|
||||
int configuredColorTransfer = defaultShaderProgram.getOutputColorTransfer();
|
||||
defaultShaderProgram.setOutputColorTransfer(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkState;
|
|||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLES20;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.GlProgram;
|
||||
import com.google.android.exoplayer2.util.GlTextureInfo;
|
||||
import com.google.android.exoplayer2.util.GlUtil;
|
||||
|
|
@ -49,10 +49,12 @@ import java.util.concurrent.Executor;
|
|||
private final int capacity;
|
||||
private final boolean useHdr;
|
||||
|
||||
private GlObjectsProvider glObjectsProvider;
|
||||
private InputListener inputListener;
|
||||
private OutputListener outputListener;
|
||||
private ErrorListener errorListener;
|
||||
private Executor errorListenerExecutor;
|
||||
private boolean frameProcessingStarted;
|
||||
|
||||
/** Creates a new instance. */
|
||||
public FrameCacheGlShaderProgram(Context context, int capacity, boolean useHdr)
|
||||
|
|
@ -80,12 +82,21 @@ import java.util.concurrent.Executor;
|
|||
GlUtil.getNormalizedCoordinateBounds(),
|
||||
GlUtil.HOMOGENEOUS_COORDINATE_VECTOR_SIZE);
|
||||
|
||||
glObjectsProvider = GlObjectsProvider.DEFAULT;
|
||||
inputListener = new InputListener() {};
|
||||
outputListener = new OutputListener() {};
|
||||
errorListener = videoFrameProcessingException -> {};
|
||||
errorListenerExecutor = MoreExecutors.directExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
|
||||
checkState(
|
||||
!frameProcessingStarted,
|
||||
"The GlObjectsProvider cannot be set after frame processing has started.");
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputListener(InputListener inputListener) {
|
||||
this.inputListener = inputListener;
|
||||
|
|
@ -115,6 +126,7 @@ import java.util.concurrent.Executor;
|
|||
|
||||
@Override
|
||||
public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) {
|
||||
frameProcessingStarted = true;
|
||||
try {
|
||||
configureAllOutputTextures(inputTexture.width, inputTexture.height);
|
||||
|
||||
|
|
@ -125,7 +137,7 @@ import java.util.concurrent.Executor;
|
|||
// Copy frame to fbo.
|
||||
GlUtil.focusFramebufferUsingCurrentContext(
|
||||
outputTexture.fboId, outputTexture.width, outputTexture.height);
|
||||
GlUtil.clearOutputFrame();
|
||||
glObjectsProvider.clearOutputFrame();
|
||||
drawFrame(inputTexture.texId);
|
||||
inputListener.onInputFrameProcessed(inputTexture);
|
||||
outputListener.onOutputFrameAvailable(outputTexture, presentationTimeUs);
|
||||
|
|
@ -147,6 +159,7 @@ import java.util.concurrent.Executor;
|
|||
|
||||
@Override
|
||||
public void releaseOutputFrame(GlTextureInfo outputTexture) {
|
||||
frameProcessingStarted = true;
|
||||
checkState(inUseOutputTextures.contains(outputTexture));
|
||||
inUseOutputTextures.remove(outputTexture);
|
||||
freeOutputTextures.add(outputTexture);
|
||||
|
|
@ -155,11 +168,13 @@ import java.util.concurrent.Executor;
|
|||
|
||||
@Override
|
||||
public void signalEndOfCurrentInputStream() {
|
||||
frameProcessingStarted = true;
|
||||
outputListener.onCurrentOutputStreamEnded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
frameProcessingStarted = true;
|
||||
freeOutputTextures.addAll(inUseOutputTextures);
|
||||
inUseOutputTextures.clear();
|
||||
inputListener.onFlush();
|
||||
|
|
@ -170,6 +185,7 @@ import java.util.concurrent.Executor;
|
|||
|
||||
@Override
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
frameProcessingStarted = true;
|
||||
try {
|
||||
deleteAllOutputTextures();
|
||||
} catch (GlUtil.GlException e) {
|
||||
|
|
@ -196,9 +212,8 @@ import java.util.concurrent.Executor;
|
|||
checkState(inUseOutputTextures.isEmpty());
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
int outputTexId = GlUtil.createTexture(width, height, useHdr);
|
||||
int outputFboId = GlUtil.createFboForTexture(outputTexId);
|
||||
GlTextureInfo outputTexture =
|
||||
new GlTextureInfo(outputTexId, outputFboId, /* rboId= */ C.INDEX_UNSET, width, height);
|
||||
glObjectsProvider.createBuffersForTexture(outputTexId, width, height);
|
||||
freeOutputTextures.add(outputTexture);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.effect;
|
||||
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.GlTextureInfo;
|
||||
import com.google.android.exoplayer2.util.VideoFrameProcessingException;
|
||||
import java.util.concurrent.Executor;
|
||||
|
|
@ -146,6 +147,13 @@ public interface GlShaderProgram {
|
|||
*/
|
||||
void setErrorListener(Executor executor, ErrorListener errorListener);
|
||||
|
||||
/**
|
||||
* Sets the {@link GlObjectsProvider}.
|
||||
*
|
||||
* <p>This method should not be called after any of the frame processing methods.
|
||||
*/
|
||||
void setGlObjectsProvider(GlObjectsProvider glObjectsProvider);
|
||||
|
||||
/**
|
||||
* Processes an input frame if possible.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package com.google.android.exoplayer2.effect;
|
|||
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.GlTextureInfo;
|
||||
import com.google.android.exoplayer2.util.GlUtil;
|
||||
import com.google.android.exoplayer2.util.Size;
|
||||
|
|
@ -42,6 +42,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
|||
|
||||
private final boolean useHdr;
|
||||
|
||||
private GlObjectsProvider glObjectsProvider;
|
||||
private InputListener inputListener;
|
||||
private OutputListener outputListener;
|
||||
private ErrorListener errorListener;
|
||||
|
|
@ -50,6 +51,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
|||
private int inputHeight;
|
||||
private @MonotonicNonNull GlTextureInfo outputTexture;
|
||||
private boolean outputTextureInUse;
|
||||
private boolean frameProcessingStarted;
|
||||
|
||||
/**
|
||||
* Creates a {@code SingleFrameGlShaderProgram} instance.
|
||||
|
|
@ -59,6 +61,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
|||
*/
|
||||
public SingleFrameGlShaderProgram(boolean useHdr) {
|
||||
this.useHdr = useHdr;
|
||||
glObjectsProvider = GlObjectsProvider.DEFAULT;
|
||||
inputListener = new InputListener() {};
|
||||
outputListener = new OutputListener() {};
|
||||
errorListener = (videoFrameProcessingException) -> {};
|
||||
|
|
@ -96,6 +99,14 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
|||
public abstract void drawFrame(int inputTexId, long presentationTimeUs)
|
||||
throws VideoFrameProcessingException;
|
||||
|
||||
@Override
|
||||
public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
|
||||
checkState(
|
||||
!frameProcessingStarted,
|
||||
"The GlObjectsProvider cannot be set after frame processing has started.");
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setInputListener(InputListener inputListener) {
|
||||
this.inputListener = inputListener;
|
||||
|
|
@ -121,7 +132,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
|||
!outputTextureInUse,
|
||||
"The shader program does not currently accept input frames. Release prior output frames"
|
||||
+ " first.");
|
||||
|
||||
frameProcessingStarted = true;
|
||||
try {
|
||||
if (outputTexture == null
|
||||
|| inputTexture.width != inputWidth
|
||||
|
|
@ -131,7 +142,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
|||
outputTextureInUse = true;
|
||||
GlUtil.focusFramebufferUsingCurrentContext(
|
||||
outputTexture.fboId, outputTexture.width, outputTexture.height);
|
||||
GlUtil.clearOutputFrame();
|
||||
glObjectsProvider.clearOutputFrame();
|
||||
drawFrame(inputTexture.texId, presentationTimeUs);
|
||||
inputListener.onInputFrameProcessed(inputTexture);
|
||||
outputListener.onOutputFrameAvailable(outputTexture, presentationTimeUs);
|
||||
|
|
@ -159,25 +170,22 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
|||
GlUtil.deleteFbo(outputTexture.fboId);
|
||||
}
|
||||
int outputTexId = GlUtil.createTexture(outputSize.getWidth(), outputSize.getHeight(), useHdr);
|
||||
int outputFboId = GlUtil.createFboForTexture(outputTexId);
|
||||
outputTexture =
|
||||
new GlTextureInfo(
|
||||
outputTexId,
|
||||
outputFboId,
|
||||
/* rboId= */ C.INDEX_UNSET,
|
||||
outputSize.getWidth(),
|
||||
outputSize.getHeight());
|
||||
glObjectsProvider.createBuffersForTexture(
|
||||
outputTexId, outputSize.getWidth(), outputSize.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void releaseOutputFrame(GlTextureInfo outputTexture) {
|
||||
outputTextureInUse = false;
|
||||
frameProcessingStarted = true;
|
||||
inputListener.onReadyToAcceptInputFrame();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void signalEndOfCurrentInputStream() {
|
||||
frameProcessingStarted = true;
|
||||
outputListener.onCurrentOutputStreamEnded();
|
||||
}
|
||||
|
||||
|
|
@ -185,6 +193,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
|||
@CallSuper
|
||||
public void flush() {
|
||||
outputTextureInUse = false;
|
||||
frameProcessingStarted = true;
|
||||
inputListener.onFlush();
|
||||
inputListener.onReadyToAcceptInputFrame();
|
||||
}
|
||||
|
|
@ -192,6 +201,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
|||
@Override
|
||||
@CallSuper
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
frameProcessingStarted = true;
|
||||
if (outputTexture != null) {
|
||||
try {
|
||||
GlUtil.deleteTexture(outputTexture.texId);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.google.android.exoplayer2.MediaItem;
|
|||
import com.google.android.exoplayer2.audio.AudioProcessor;
|
||||
import com.google.android.exoplayer2.effect.DefaultVideoFrameProcessor;
|
||||
import com.google.android.exoplayer2.util.Effect;
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.VideoFrameProcessor;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
|
|
@ -47,15 +48,25 @@ public final class Effects {
|
|||
* applying the {@code videoEffects} to the video frames.
|
||||
*/
|
||||
public final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||
/**
|
||||
* The {@link GlObjectsProvider} used to create and maintain certain GL Objects in the {@link
|
||||
* VideoFrameProcessor}.
|
||||
*/
|
||||
public final GlObjectsProvider glObjectsProvider;
|
||||
|
||||
/**
|
||||
* Creates an instance using a {@link DefaultVideoFrameProcessor.Factory}.
|
||||
*
|
||||
* <p>This is equivalent to calling {@link Effects#Effects(List, List,
|
||||
* VideoFrameProcessor.Factory)} with a {@link DefaultVideoFrameProcessor.Factory}.
|
||||
* VideoFrameProcessor.Factory, GlObjectsProvider)} with a {@link
|
||||
* DefaultVideoFrameProcessor.Factory} and {@link GlObjectsProvider#DEFAULT}.
|
||||
*/
|
||||
public Effects(List<AudioProcessor> audioProcessors, List<Effect> videoEffects) {
|
||||
this(audioProcessors, videoEffects, new DefaultVideoFrameProcessor.Factory());
|
||||
this(
|
||||
audioProcessors,
|
||||
videoEffects,
|
||||
new DefaultVideoFrameProcessor.Factory(),
|
||||
GlObjectsProvider.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -64,13 +75,16 @@ public final class Effects {
|
|||
* @param audioProcessors The {@link #audioProcessors}.
|
||||
* @param videoEffects The {@link #videoEffects}.
|
||||
* @param videoFrameProcessorFactory The {@link #videoFrameProcessorFactory}.
|
||||
* @param glObjectsProvider The {@link GlObjectsProvider}.
|
||||
*/
|
||||
public Effects(
|
||||
List<AudioProcessor> audioProcessors,
|
||||
List<Effect> videoEffects,
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory) {
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||
GlObjectsProvider glObjectsProvider) {
|
||||
this.audioProcessors = ImmutableList.copyOf(audioProcessors);
|
||||
this.videoEffects = ImmutableList.copyOf(videoEffects);
|
||||
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
|||
import com.google.android.exoplayer2.util.Clock;
|
||||
import com.google.android.exoplayer2.util.DebugViewProvider;
|
||||
import com.google.android.exoplayer2.util.Effect;
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.HandlerWrapper;
|
||||
import com.google.android.exoplayer2.util.ListenerSet;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
|
@ -86,6 +87,7 @@ public final class Transformer {
|
|||
private ListenerSet<Transformer.Listener> listeners;
|
||||
private AssetLoader.@MonotonicNonNull Factory assetLoaderFactory;
|
||||
private VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||
private GlObjectsProvider glObjectsProvider;
|
||||
private Codec.EncoderFactory encoderFactory;
|
||||
private Muxer.Factory muxerFactory;
|
||||
private Looper looper;
|
||||
|
|
@ -103,6 +105,7 @@ public final class Transformer {
|
|||
audioProcessors = ImmutableList.of();
|
||||
videoEffects = ImmutableList.of();
|
||||
videoFrameProcessorFactory = new DefaultVideoFrameProcessor.Factory();
|
||||
glObjectsProvider = GlObjectsProvider.DEFAULT;
|
||||
encoderFactory = new DefaultEncoderFactory.Builder(this.context).build();
|
||||
muxerFactory = new DefaultMuxer.Factory();
|
||||
looper = Util.getCurrentOrMainLooper();
|
||||
|
|
@ -122,6 +125,7 @@ public final class Transformer {
|
|||
this.listeners = transformer.listeners;
|
||||
this.assetLoaderFactory = transformer.assetLoaderFactory;
|
||||
this.videoFrameProcessorFactory = transformer.videoFrameProcessorFactory;
|
||||
this.glObjectsProvider = transformer.glObjectsProvider;
|
||||
this.encoderFactory = transformer.encoderFactory;
|
||||
this.muxerFactory = transformer.muxerFactory;
|
||||
this.looper = transformer.looper;
|
||||
|
|
@ -395,6 +399,7 @@ public final class Transformer {
|
|||
listeners,
|
||||
assetLoaderFactory,
|
||||
videoFrameProcessorFactory,
|
||||
glObjectsProvider,
|
||||
encoderFactory,
|
||||
muxerFactory,
|
||||
looper,
|
||||
|
|
@ -554,6 +559,7 @@ public final class Transformer {
|
|||
private final ListenerSet<Transformer.Listener> listeners;
|
||||
private final AssetLoader.Factory assetLoaderFactory;
|
||||
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||
private final GlObjectsProvider glObjectsProvider;
|
||||
private final Codec.EncoderFactory encoderFactory;
|
||||
private final Muxer.Factory muxerFactory;
|
||||
private final Looper looper;
|
||||
|
|
@ -573,6 +579,7 @@ public final class Transformer {
|
|||
ListenerSet<Listener> listeners,
|
||||
AssetLoader.Factory assetLoaderFactory,
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
Muxer.Factory muxerFactory,
|
||||
Looper looper,
|
||||
|
|
@ -589,6 +596,7 @@ public final class Transformer {
|
|||
this.listeners = listeners;
|
||||
this.assetLoaderFactory = assetLoaderFactory;
|
||||
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||
this.glObjectsProvider = glObjectsProvider;
|
||||
this.encoderFactory = encoderFactory;
|
||||
this.muxerFactory = muxerFactory;
|
||||
this.looper = looper;
|
||||
|
|
@ -792,7 +800,9 @@ public final class Transformer {
|
|||
.setRemoveAudio(removeAudio)
|
||||
.setRemoveVideo(removeVideo)
|
||||
.setFlattenForSlowMotion(flattenForSlowMotion)
|
||||
.setEffects(new Effects(audioProcessors, videoEffects, videoFrameProcessorFactory))
|
||||
.setEffects(
|
||||
new Effects(
|
||||
audioProcessors, videoEffects, videoFrameProcessorFactory, glObjectsProvider))
|
||||
.build();
|
||||
start(editedMediaItem, path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -533,6 +533,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
firstEditedMediaItem.effects.videoEffects,
|
||||
compositionPresentation,
|
||||
firstEditedMediaItem.effects.videoFrameProcessorFactory,
|
||||
firstEditedMediaItem.effects.glObjectsProvider,
|
||||
encoderFactory,
|
||||
muxerWrapper,
|
||||
/* errorConsumer= */ this::onError,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import com.google.android.exoplayer2.util.Consumer;
|
|||
import com.google.android.exoplayer2.util.DebugViewProvider;
|
||||
import com.google.android.exoplayer2.util.Effect;
|
||||
import com.google.android.exoplayer2.util.FrameInfo;
|
||||
import com.google.android.exoplayer2.util.GlObjectsProvider;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Size;
|
||||
|
|
@ -81,6 +82,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||
ImmutableList<Effect> effects,
|
||||
@Nullable Presentation presentation,
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||
GlObjectsProvider glObjectsProvider,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
MuxerWrapper muxerWrapper,
|
||||
Consumer<ExportException> errorConsumer,
|
||||
|
|
@ -129,52 +131,54 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||
}
|
||||
try {
|
||||
videoFrameProcessor =
|
||||
videoFrameProcessorFactory.create(
|
||||
context,
|
||||
effectsWithPresentation,
|
||||
debugViewProvider,
|
||||
videoFrameProcessorInputColor,
|
||||
videoFrameProcessorOutputColor,
|
||||
MimeTypes.isVideo(firstInputFormat.sampleMimeType),
|
||||
/* releaseFramesAutomatically= */ true,
|
||||
MoreExecutors.directExecutor(),
|
||||
new VideoFrameProcessor.Listener() {
|
||||
private long lastProcessedFramePresentationTimeUs;
|
||||
videoFrameProcessorFactory
|
||||
.setGlObjectsProvider(glObjectsProvider)
|
||||
.create(
|
||||
context,
|
||||
effectsWithPresentation,
|
||||
debugViewProvider,
|
||||
videoFrameProcessorInputColor,
|
||||
videoFrameProcessorOutputColor,
|
||||
MimeTypes.isVideo(firstInputFormat.sampleMimeType),
|
||||
/* releaseFramesAutomatically= */ true,
|
||||
MoreExecutors.directExecutor(),
|
||||
new VideoFrameProcessor.Listener() {
|
||||
private long lastProcessedFramePresentationTimeUs;
|
||||
|
||||
@Override
|
||||
public void onOutputSizeChanged(int width, int height) {
|
||||
try {
|
||||
checkNotNull(videoFrameProcessor)
|
||||
.setOutputSurfaceInfo(encoderWrapper.getSurfaceInfo(width, height));
|
||||
} catch (ExportException exception) {
|
||||
errorConsumer.accept(exception);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onOutputSizeChanged(int width, int height) {
|
||||
try {
|
||||
checkNotNull(videoFrameProcessor)
|
||||
.setOutputSurfaceInfo(encoderWrapper.getSurfaceInfo(width, height));
|
||||
} catch (ExportException exception) {
|
||||
errorConsumer.accept(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOutputFrameAvailable(long presentationTimeUs) {
|
||||
// Frames are released automatically.
|
||||
lastProcessedFramePresentationTimeUs = presentationTimeUs;
|
||||
}
|
||||
@Override
|
||||
public void onOutputFrameAvailable(long presentationTimeUs) {
|
||||
// Frames are released automatically.
|
||||
lastProcessedFramePresentationTimeUs = presentationTimeUs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(VideoFrameProcessingException exception) {
|
||||
errorConsumer.accept(
|
||||
ExportException.createForVideoFrameProcessingException(
|
||||
exception, ExportException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED));
|
||||
}
|
||||
@Override
|
||||
public void onError(VideoFrameProcessingException exception) {
|
||||
errorConsumer.accept(
|
||||
ExportException.createForVideoFrameProcessingException(
|
||||
exception, ExportException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnded() {
|
||||
VideoSamplePipeline.this.finalFramePresentationTimeUs =
|
||||
lastProcessedFramePresentationTimeUs;
|
||||
try {
|
||||
encoderWrapper.signalEndOfInputStream();
|
||||
} catch (ExportException exception) {
|
||||
errorConsumer.accept(exception);
|
||||
}
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onEnded() {
|
||||
VideoSamplePipeline.this.finalFramePresentationTimeUs =
|
||||
lastProcessedFramePresentationTimeUs;
|
||||
try {
|
||||
encoderWrapper.signalEndOfInputStream();
|
||||
} catch (ExportException exception) {
|
||||
errorConsumer.accept(exception);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (VideoFrameProcessingException e) {
|
||||
throw ExportException.createForVideoFrameProcessingException(
|
||||
e, ExportException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED);
|
||||
|
|
|
|||
Loading…
Reference in a new issue