Effect: Rename FrameProcessor
Rename FrameProcessor to VideoFrameProcessor, and GlEffectsFrameProcessor to DefaultVideoFrameProcessor. Most changes are semi-mechanical, semi-manual find-replace, preserving case: * "FrameProc" -> "VideoFrameProc" (ex. FrameProcessor -> VideoFrameProcessor, and FrameProcessingException -> VideoFrameProcessingException) * "GlEffectsVideoFrameProc" -> "DefaultVideoFrameProc" PiperOrigin-RevId: 509887384
This commit is contained in:
parent
41a03dd8a6
commit
cf768329e6
@ -19,7 +19,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLES20;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlProgram;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
@ -59,7 +59,7 @@ import java.io.IOException;
|
||||
* @param minInnerRadius The lower bound of the radius that is unaffected by the effect.
|
||||
* @param maxInnerRadius The upper bound of the radius that is unaffected by the effect.
|
||||
* @param outerRadius The radius after which all pixels are black.
|
||||
* @throws FrameProcessingException If a problem occurs while reading shader files.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while reading shader files.
|
||||
*/
|
||||
public PeriodicVignetteShaderProgram(
|
||||
Context context,
|
||||
@ -69,7 +69,7 @@ import java.io.IOException;
|
||||
float minInnerRadius,
|
||||
float maxInnerRadius,
|
||||
float outerRadius)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
super(useHdr);
|
||||
checkArgument(minInnerRadius <= maxInnerRadius);
|
||||
checkArgument(maxInnerRadius <= outerRadius);
|
||||
@ -78,7 +78,7 @@ import java.io.IOException;
|
||||
try {
|
||||
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
|
||||
} catch (IOException | GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
glProgram.setFloatsUniform("uCenter", new float[] {centerX, centerY});
|
||||
glProgram.setFloatsUniform("uOuterRadius", new float[] {outerRadius});
|
||||
@ -95,7 +95,8 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs) throws FrameProcessingException {
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs)
|
||||
throws VideoFrameProcessingException {
|
||||
try {
|
||||
glProgram.use();
|
||||
glProgram.setSamplerTexIdUniform("uTexSampler", inputTexId, /* texUnitIndex= */ 0);
|
||||
@ -107,17 +108,17 @@ import java.io.IOException;
|
||||
// The four-vertex triangle strip forms a quad.
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e, presentationTimeUs);
|
||||
throw new VideoFrameProcessingException(e, presentationTimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws FrameProcessingException {
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
super.release();
|
||||
try {
|
||||
glProgram.delete();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import android.content.Context;
|
||||
import android.opengl.EGL14;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.LibraryLoader;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.effect.GlShaderProgram;
|
||||
@ -112,7 +112,7 @@ import java.util.concurrent.Future;
|
||||
futures = new ArrayDeque<>();
|
||||
inputListener = new InputListener() {};
|
||||
outputListener = new OutputListener() {};
|
||||
errorListener = (frameProcessingException) -> {};
|
||||
errorListener = (videoFrameProcessingException) -> {};
|
||||
errorListenerExecutor = MoreExecutors.directExecutor();
|
||||
EglManager eglManager = new EglManager(EGL14.eglGetCurrentContext());
|
||||
frameProcessor =
|
||||
@ -155,7 +155,7 @@ import java.util.concurrent.Future;
|
||||
frameProcessor.setAsynchronousErrorListener(
|
||||
error ->
|
||||
errorListenerExecutor.execute(
|
||||
() -> errorListener.onFrameProcessingError(new FrameProcessingException(error))));
|
||||
() -> errorListener.onError(new VideoFrameProcessingException(error))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -191,7 +191,7 @@ import java.util.concurrent.Future;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
errorListenerExecutor.execute(
|
||||
() -> errorListener.onFrameProcessingError(new FrameProcessingException(e)));
|
||||
() -> errorListener.onError(new VideoFrameProcessingException(e)));
|
||||
}
|
||||
if (acceptedFrame) {
|
||||
inputListener.onInputFrameProcessed(inputTexture);
|
||||
@ -213,9 +213,7 @@ import java.util.concurrent.Future;
|
||||
Thread.currentThread().interrupt();
|
||||
if (errorListener != null) {
|
||||
errorListenerExecutor.execute(
|
||||
() ->
|
||||
errorListener.onFrameProcessingError(
|
||||
new FrameProcessingException(e)));
|
||||
() -> errorListener.onError(new VideoFrameProcessingException(e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -254,14 +252,12 @@ import java.util.concurrent.Future;
|
||||
try {
|
||||
if (!singleThreadExecutorService.awaitTermination(RELEASE_WAIT_TIME_MS, MILLISECONDS)) {
|
||||
errorListenerExecutor.execute(
|
||||
() ->
|
||||
errorListener.onFrameProcessingError(
|
||||
new FrameProcessingException("Release timed out")));
|
||||
() -> errorListener.onError(new VideoFrameProcessingException("Release timed out")));
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
errorListenerExecutor.execute(
|
||||
() -> errorListener.onFrameProcessingError(new FrameProcessingException(e)));
|
||||
() -> errorListener.onError(new VideoFrameProcessingException(e)));
|
||||
}
|
||||
|
||||
frameProcessor.close();
|
||||
@ -294,11 +290,11 @@ import java.util.concurrent.Future;
|
||||
futures.remove().get();
|
||||
} catch (ExecutionException e) {
|
||||
errorListenerExecutor.execute(
|
||||
() -> errorListener.onFrameProcessingError(new FrameProcessingException(e)));
|
||||
() -> errorListener.onError(new VideoFrameProcessingException(e)));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
errorListenerExecutor.execute(
|
||||
() -> errorListener.onFrameProcessingError(new FrameProcessingException(e)));
|
||||
() -> errorListener.onError(new VideoFrameProcessingException(e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,10 +230,10 @@ public class PlaybackException extends Exception implements Bundleable {
|
||||
|
||||
// Frame processing errors (7xxx).
|
||||
|
||||
/** Caused by a failure when initializing a {@link FrameProcessor}. */
|
||||
@UnstableApi public static final int ERROR_CODE_FRAME_PROCESSOR_INIT_FAILED = 7000;
|
||||
/** Caused by a failure when processing a frame. */
|
||||
@UnstableApi public static final int ERROR_CODE_FRAME_PROCESSING_FAILED = 7001;
|
||||
/** Caused by a failure when initializing a {@link VideoFrameProcessor}. */
|
||||
@UnstableApi public static final int ERROR_CODE_VIDEO_FRAME_PROCESSOR_INIT_FAILED = 7000;
|
||||
/** Caused by a failure when processing a video frame. */
|
||||
@UnstableApi public static final int ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED = 7001;
|
||||
|
||||
/**
|
||||
* Player implementations that want to surface custom errors can use error codes greater than this
|
||||
@ -312,10 +312,10 @@ public class PlaybackException extends Exception implements Bundleable {
|
||||
return "ERROR_CODE_DRM_DEVICE_REVOKED";
|
||||
case ERROR_CODE_DRM_LICENSE_EXPIRED:
|
||||
return "ERROR_CODE_DRM_LICENSE_EXPIRED";
|
||||
case ERROR_CODE_FRAME_PROCESSOR_INIT_FAILED:
|
||||
return "ERROR_CODE_FRAME_PROCESSOR_INIT_FAILED";
|
||||
case ERROR_CODE_FRAME_PROCESSING_FAILED:
|
||||
return "ERROR_CODE_FRAME_PROCESSING_FAILED";
|
||||
case ERROR_CODE_VIDEO_FRAME_PROCESSOR_INIT_FAILED:
|
||||
return "ERROR_CODE_VIDEO_FRAME_PROCESSOR_INIT_FAILED";
|
||||
case ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED:
|
||||
return "ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED";
|
||||
default:
|
||||
if (errorCode >= CUSTOM_ERROR_CODE_BASE) {
|
||||
return "custom error code";
|
||||
|
@ -22,25 +22,26 @@ import androidx.media3.common.util.UnstableApi;
|
||||
* to video frames.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class FrameProcessingException extends Exception {
|
||||
public final class VideoFrameProcessingException extends Exception {
|
||||
|
||||
/**
|
||||
* Wraps the given exception in a {@code FrameProcessingException} if it is not already a {@code
|
||||
* FrameProcessingException} and returns the exception otherwise.
|
||||
* Wraps the given exception in a {@code VideoFrameProcessingException} if it is not already a
|
||||
* {@code VideoFrameProcessingException} and returns the exception otherwise.
|
||||
*/
|
||||
public static FrameProcessingException from(Exception exception) {
|
||||
public static VideoFrameProcessingException from(Exception exception) {
|
||||
return from(exception, /* presentationTimeUs= */ C.TIME_UNSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given exception in a {@code FrameProcessingException} with the given timestamp if it
|
||||
* is not already a {@code FrameProcessingException} and returns the exception otherwise.
|
||||
* Wraps the given exception in a {@code VideoFrameProcessingException} with the given timestamp
|
||||
* if it is not already a {@code VideoFrameProcessingException} and returns the exception
|
||||
* otherwise.
|
||||
*/
|
||||
public static FrameProcessingException from(Exception exception, long presentationTimeUs) {
|
||||
if (exception instanceof FrameProcessingException) {
|
||||
return (FrameProcessingException) exception;
|
||||
public static VideoFrameProcessingException from(Exception exception, long presentationTimeUs) {
|
||||
if (exception instanceof VideoFrameProcessingException) {
|
||||
return (VideoFrameProcessingException) exception;
|
||||
} else {
|
||||
return new FrameProcessingException(exception, presentationTimeUs);
|
||||
return new VideoFrameProcessingException(exception, presentationTimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +56,7 @@ public final class FrameProcessingException extends Exception {
|
||||
*
|
||||
* @param message The detail message for this exception.
|
||||
*/
|
||||
public FrameProcessingException(String message) {
|
||||
public VideoFrameProcessingException(String message) {
|
||||
this(message, /* presentationTimeUs= */ C.TIME_UNSET);
|
||||
}
|
||||
|
||||
@ -65,7 +66,7 @@ public final class FrameProcessingException extends Exception {
|
||||
* @param message The detail message for this exception.
|
||||
* @param presentationTimeUs The timestamp of the frame for which the exception occurred.
|
||||
*/
|
||||
public FrameProcessingException(String message, long presentationTimeUs) {
|
||||
public VideoFrameProcessingException(String message, long presentationTimeUs) {
|
||||
super(message);
|
||||
this.presentationTimeUs = presentationTimeUs;
|
||||
}
|
||||
@ -76,7 +77,7 @@ public final class FrameProcessingException extends Exception {
|
||||
* @param message The detail message for this exception.
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public FrameProcessingException(String message, Throwable cause) {
|
||||
public VideoFrameProcessingException(String message, Throwable cause) {
|
||||
this(message, cause, /* presentationTimeUs= */ C.TIME_UNSET);
|
||||
}
|
||||
|
||||
@ -87,7 +88,7 @@ public final class FrameProcessingException extends Exception {
|
||||
* @param cause The cause of this exception.
|
||||
* @param presentationTimeUs The timestamp of the frame for which the exception occurred.
|
||||
*/
|
||||
public FrameProcessingException(String message, Throwable cause, long presentationTimeUs) {
|
||||
public VideoFrameProcessingException(String message, Throwable cause, long presentationTimeUs) {
|
||||
super(message, cause);
|
||||
this.presentationTimeUs = presentationTimeUs;
|
||||
}
|
||||
@ -97,7 +98,7 @@ public final class FrameProcessingException extends Exception {
|
||||
*
|
||||
* @param cause The cause of this exception.
|
||||
*/
|
||||
public FrameProcessingException(Throwable cause) {
|
||||
public VideoFrameProcessingException(Throwable cause) {
|
||||
this(cause, /* presentationTimeUs= */ C.TIME_UNSET);
|
||||
}
|
||||
|
||||
@ -107,7 +108,7 @@ public final class FrameProcessingException extends Exception {
|
||||
* @param cause The cause of this exception.
|
||||
* @param presentationTimeUs The timestamp of the frame for which the exception occurred.
|
||||
*/
|
||||
public FrameProcessingException(Throwable cause, long presentationTimeUs) {
|
||||
public VideoFrameProcessingException(Throwable cause, long presentationTimeUs) {
|
||||
super(cause);
|
||||
this.presentationTimeUs = presentationTimeUs;
|
||||
}
|
@ -25,7 +25,7 @@ import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Interface for a frame processor that applies changes to individual video frames.
|
||||
* Interface for a video frame processor that applies changes to individual video frames.
|
||||
*
|
||||
* <p>The changes are specified by {@link Effect} instances passed to {@link Factory#create}.
|
||||
*
|
||||
@ -37,13 +37,13 @@ import java.util.concurrent.Executor;
|
||||
* to the input {@link Surface}.
|
||||
*/
|
||||
@UnstableApi
|
||||
public interface FrameProcessor {
|
||||
public interface VideoFrameProcessor {
|
||||
// TODO(b/243036513): Allow effects to be replaced.
|
||||
|
||||
/** A factory for {@link FrameProcessor} instances. */
|
||||
/** A factory for {@link VideoFrameProcessor} instances. */
|
||||
interface Factory {
|
||||
/**
|
||||
* Creates a new {@link FrameProcessor} instance.
|
||||
* Creates a new {@link VideoFrameProcessor} instance.
|
||||
*
|
||||
* @param context A {@link Context}.
|
||||
* @param effects The {@link Effect} instances to apply to each frame. Applied on the {@code
|
||||
@ -55,18 +55,18 @@ public interface FrameProcessor {
|
||||
* video) or not (e.g. from a {@link Bitmap}). See <a
|
||||
* href="https://source.android.com/docs/core/graphics/arch-st#ext_texture">the
|
||||
* SurfaceTexture docs</a> for more information on external textures.
|
||||
* @param releaseFramesAutomatically If {@code true}, the {@link FrameProcessor} will render
|
||||
* output frames to the {@linkplain #setOutputSurfaceInfo(SurfaceInfo) output surface}
|
||||
* automatically as {@link FrameProcessor} is done processing them. If {@code false}, the
|
||||
* {@link FrameProcessor} will block until {@link #releaseOutputFrame(long)} is called, to
|
||||
* @param releaseFramesAutomatically If {@code true}, the instance will render output frames to
|
||||
* the {@linkplain #setOutputSurfaceInfo(SurfaceInfo) output surface} automatically as
|
||||
* {@link VideoFrameProcessor} is done processing them. If {@code false}, the {@link
|
||||
* VideoFrameProcessor} will block until {@link #releaseOutputFrame(long)} is called, to
|
||||
* render or drop the frame.
|
||||
* @param executor The {@link Executor} on which the {@code listener} is invoked.
|
||||
* @param listener A {@link Listener}.
|
||||
* @return A new instance.
|
||||
* @throws FrameProcessingException If a problem occurs while creating the {@link
|
||||
* FrameProcessor}.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while creating the {@link
|
||||
* VideoFrameProcessor}.
|
||||
*/
|
||||
FrameProcessor create(
|
||||
VideoFrameProcessor create(
|
||||
Context context,
|
||||
List<Effect> effects,
|
||||
DebugViewProvider debugViewProvider,
|
||||
@ -76,7 +76,7 @@ public interface FrameProcessor {
|
||||
boolean releaseFramesAutomatically,
|
||||
Executor executor,
|
||||
Listener listener)
|
||||
throws FrameProcessingException;
|
||||
throws VideoFrameProcessingException;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,15 +106,15 @@ public interface FrameProcessor {
|
||||
void onOutputFrameAvailable(long presentationTimeUs);
|
||||
|
||||
/**
|
||||
* Called when an exception occurs during asynchronous frame processing.
|
||||
* Called when an exception occurs during asynchronous video frame processing.
|
||||
*
|
||||
* <p>If an error occurred, consuming and producing further frames will not work as expected and
|
||||
* the {@link FrameProcessor} should be released.
|
||||
* the {@link VideoFrameProcessor} should be released.
|
||||
*/
|
||||
void onFrameProcessingError(FrameProcessingException exception);
|
||||
void onError(VideoFrameProcessingException exception);
|
||||
|
||||
/** Called after the {@link FrameProcessor} has produced its final output frame. */
|
||||
void onFrameProcessingEnded();
|
||||
/** Called after the {@link VideoFrameProcessor} has produced its final output frame. */
|
||||
void onEnded();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,14 +127,14 @@ public interface FrameProcessor {
|
||||
long DROP_OUTPUT_FRAME = -2;
|
||||
|
||||
/**
|
||||
* Provides an input {@link Bitmap} to the {@link FrameProcessor}.
|
||||
* Provides an input {@link Bitmap} to the {@code VideoFrameProcessor}.
|
||||
*
|
||||
* <p>This method should only be used for when the {@link FrameProcessor}'s {@code
|
||||
* <p>This method should only be used for when the {@code VideoFrameProcessor}'s {@code
|
||||
* isInputTextureExternal} parameter is set to {@code false}.
|
||||
*
|
||||
* <p>Can be called on any thread.
|
||||
*
|
||||
* @param inputBitmap The {@link Bitmap} queued to the {@link FrameProcessor}.
|
||||
* @param inputBitmap The {@link Bitmap} queued to the {@code VideoFrameProcessor}.
|
||||
* @param durationUs The duration for which to display the {@code inputBitmap}, in microseconds.
|
||||
* @param frameRate The frame rate at which to display the {@code inputBitmap}, in frames per
|
||||
* second.
|
||||
@ -144,9 +144,10 @@ public interface FrameProcessor {
|
||||
void queueInputBitmap(Bitmap inputBitmap, long durationUs, float frameRate);
|
||||
|
||||
/**
|
||||
* Returns the input {@link Surface}, where {@link FrameProcessor} consumes input frames from.
|
||||
* Returns the input {@link Surface}, where {@code VideoFrameProcessor} consumes input frames
|
||||
* from.
|
||||
*
|
||||
* <p>This method should only be used for when the {@link FrameProcessor}'s {@code
|
||||
* <p>This method should only be used for when the {@code VideoFrameProcessor}'s {@code
|
||||
* isInputTextureExternal} parameter is set to {@code true}.
|
||||
*
|
||||
* <p>Can be called on any thread.
|
||||
@ -171,11 +172,11 @@ public interface FrameProcessor {
|
||||
void setInputFrameInfo(FrameInfo inputFrameInfo);
|
||||
|
||||
/**
|
||||
* Informs the {@code FrameProcessor} that a frame will be queued to its input surface.
|
||||
* Informs the {@code VideoFrameProcessor} that a frame will be queued to its input surface.
|
||||
*
|
||||
* <p>Must be called before rendering a frame to the frame processor's input surface.
|
||||
* <p>Must be called before rendering a frame to the {@code VideoFrameProcessor}'s input surface.
|
||||
*
|
||||
* <p>This method should only be used for when the {@link FrameProcessor}'s {@code
|
||||
* <p>This method should only be used for when the {@code VideoFrameProcessor}'s {@code
|
||||
* isInputTextureExternal} parameter is set to {@code true}.
|
||||
*
|
||||
* <p>Can be called on any thread.
|
||||
@ -189,7 +190,7 @@ public interface FrameProcessor {
|
||||
* Returns the number of input frames that have been {@linkplain #registerInputFrame() registered}
|
||||
* but not processed off the {@linkplain #getInputSurface() input surface} yet.
|
||||
*
|
||||
* <p>This method should only be used for when the {@link FrameProcessor}'s {@code
|
||||
* <p>This method should only be used for when the {@code VideoFrameProcessor}'s {@code
|
||||
* isInputTextureExternal} parameter is set to {@code true}.
|
||||
*
|
||||
* <p>Can be called on any thread.
|
||||
@ -201,7 +202,7 @@ public interface FrameProcessor {
|
||||
* dropped, they will be rendered to this output {@link SurfaceInfo}.
|
||||
*
|
||||
* <p>The new output {@link SurfaceInfo} is applied from the next output frame rendered onwards.
|
||||
* If the output {@link SurfaceInfo} is {@code null}, the {@code FrameProcessor} will stop
|
||||
* If the output {@link SurfaceInfo} is {@code null}, the {@code VideoFrameProcessor} will stop
|
||||
* rendering pending frames and resume rendering once a non-null {@link SurfaceInfo} is set.
|
||||
*
|
||||
* <p>If the dimensions given in {@link SurfaceInfo} do not match the {@linkplain
|
||||
@ -235,7 +236,7 @@ public interface FrameProcessor {
|
||||
void releaseOutputFrame(long releaseTimeNs);
|
||||
|
||||
/**
|
||||
* Informs the {@code FrameProcessor} that no further input frames should be accepted.
|
||||
* Informs the {@code VideoFrameProcessor} that no further input frames should be accepted.
|
||||
*
|
||||
* <p>Can be called on any thread.
|
||||
*
|
||||
@ -244,12 +245,12 @@ public interface FrameProcessor {
|
||||
void signalEndOfInput();
|
||||
|
||||
/**
|
||||
* Flushes the {@code FrameProcessor}.
|
||||
* Flushes the {@code VideoFrameProcessor}.
|
||||
*
|
||||
* <p>All the frames that are {@linkplain #registerInputFrame() registered} prior to calling this
|
||||
* method are no longer considered to be registered when this method returns.
|
||||
*
|
||||
* <p>This method should only be used for when the {@link FrameProcessor}'s {@code
|
||||
* <p>This method should only be used for when the {@code VideoFrameProcessor}'s {@code
|
||||
* isInputTextureExternal} parameter is set to {@code true}.
|
||||
*
|
||||
* <p>{@link Listener} methods invoked prior to calling this method should be ignored.
|
||||
@ -259,10 +260,9 @@ public interface FrameProcessor {
|
||||
/**
|
||||
* Releases all resources.
|
||||
*
|
||||
* <p>If the frame processor is released before it has {@linkplain
|
||||
* Listener#onFrameProcessingEnded() ended}, it will attempt to cancel processing any input frames
|
||||
* that have already become available. Input frames that become available after release are
|
||||
* ignored.
|
||||
* <p>If the {@code VideoFrameProcessor} is released before it has {@linkplain Listener#onEnded()
|
||||
* ended}, it will attempt to cancel processing any input frames that have already become
|
||||
* available. Input frames that become available after release are ignored.
|
||||
*
|
||||
* <p>This method blocks until all resources are released or releasing times out.
|
||||
*
|
@ -33,7 +33,7 @@ import android.graphics.Color;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
@ -50,7 +50,7 @@ import org.junit.runner.RunWith;
|
||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
* BitmapPixelTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output
|
||||
* bitmaps as recommended in {@link GlEffectsFrameProcessorPixelTest}.
|
||||
* bitmaps as recommended in {@link DefaultVideoFrameProcessorPixelTest}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ContrastPixelTest {
|
||||
@ -89,7 +89,7 @@ public class ContrastPixelTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||
public void release() throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (contrastShaderProgram != null) {
|
||||
contrastShaderProgram.release();
|
||||
}
|
||||
@ -198,7 +198,7 @@ public class ContrastPixelTest {
|
||||
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
||||
}
|
||||
|
||||
private void setupOutputTexture(int outputWidth, int outputHeight) throws GlUtil.GlException {
|
||||
private void setupOutputTexture(int outputWidth, int outputHeight) throws Exception {
|
||||
int outputTexId =
|
||||
GlUtil.createTexture(
|
||||
outputWidth, outputHeight, /* useHighPrecisionColorComponents= */ false);
|
||||
|
@ -30,7 +30,7 @@ import android.graphics.Bitmap;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
@ -48,7 +48,7 @@ import org.junit.runner.RunWith;
|
||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
* BitmapPixelTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output
|
||||
* bitmaps as recommended in {@link GlEffectsFrameProcessorPixelTest}.
|
||||
* bitmaps as recommended in {@link DefaultVideoFrameProcessorPixelTest}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class CropPixelTest {
|
||||
@ -82,7 +82,7 @@ public final class CropPixelTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||
public void release() throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (cropShaderProgram != null) {
|
||||
cropShaderProgram.release();
|
||||
}
|
||||
|
@ -27,10 +27,10 @@ import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Matrix;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
import androidx.media3.test.utils.FrameProcessorTestRunner;
|
||||
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
@ -39,10 +39,10 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Pixel test for frame processing via {@link GlEffectsFrameProcessor}.
|
||||
* Pixel test for video frame processing via {@link DefaultVideoFrameProcessor}.
|
||||
*
|
||||
* <p>Uses a {@link GlEffectsFrameProcessor} to process one frame, and checks that the actual output
|
||||
* matches expected output, either from a golden file or from another edit.
|
||||
* <p>Uses a {@link DefaultVideoFrameProcessor} to process one frame, and checks that the actual
|
||||
* output matches expected output, either from a golden file or from another edit.
|
||||
*
|
||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
@ -50,7 +50,7 @@ import org.junit.runner.RunWith;
|
||||
* bitmaps.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class GlEffectsFrameProcessorPixelTest {
|
||||
public final class DefaultVideoFrameProcessorPixelTest {
|
||||
public static final String ORIGINAL_PNG_ASSET_PATH =
|
||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/original.png";
|
||||
public static final String WRAPPED_CROP_PNG_ASSET_PATH =
|
||||
@ -81,20 +81,20 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
/** Input video of which we only use the first frame. */
|
||||
private static final String INPUT_SDR_MP4_ASSET_STRING = "media/mp4/sample.mp4";
|
||||
|
||||
private @MonotonicNonNull FrameProcessorTestRunner frameProcessorTestRunner;
|
||||
private @MonotonicNonNull VideoFrameProcessorTestRunner videoFrameProcessorTestRunner;
|
||||
|
||||
@After
|
||||
public void release() {
|
||||
checkNotNull(frameProcessorTestRunner).release();
|
||||
checkNotNull(videoFrameProcessorTestRunner).release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noEffects_matchesGoldenFile() throws Exception {
|
||||
String testId = "noEffects_matchesGoldenFile";
|
||||
frameProcessorTestRunner = getDefaultFrameProcessorTestRunnerBuilder(testId).build();
|
||||
videoFrameProcessorTestRunner = getDefaultFrameProcessorTestRunnerBuilder(testId).build();
|
||||
Bitmap expectedBitmap = readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -105,11 +105,11 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void noEffects_withImageInput_matchesGoldenFile() throws Exception {
|
||||
String testId = "noEffects_withImageInput_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId).setIsInputTextureExternal(false).build();
|
||||
Bitmap expectedBitmap = readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processImageFrameAndEnd(expectedBitmap);
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processImageFrameAndEnd(expectedBitmap);
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -120,7 +120,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void wrappedCrop_withImageInput_matchesGoldenFile() throws Exception {
|
||||
String testId = "wrappedCrop_withImageInput_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setIsInputTextureExternal(false)
|
||||
.setEffects(
|
||||
@ -134,7 +134,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
Bitmap originalBitmap = readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||
Bitmap expectedBitmap = readBitmap(WRAPPED_CROP_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processImageFrameAndEnd(originalBitmap);
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processImageFrameAndEnd(originalBitmap);
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -148,13 +148,13 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void noEffects_withFrameCache_matchesGoldenFile() throws Exception {
|
||||
String testId = "noEffects_withFrameCache_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects(new FrameCache(/* capacity= */ 5))
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(ORIGINAL_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -165,11 +165,11 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void setPixelWidthHeightRatio_matchesGoldenFile() throws Exception {
|
||||
String testId = "setPixelWidthHeightRatio_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId).setPixelWidthHeightRatio(2f).build();
|
||||
Bitmap expectedBitmap = readBitmap(SCALE_WIDE_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -182,13 +182,13 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
String testId = "matrixTransformation_matchesGoldenFile";
|
||||
Matrix translateRightMatrix = new Matrix();
|
||||
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects((MatrixTransformation) (long presentationTimeNs) -> translateRightMatrix)
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(TRANSLATE_RIGHT_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -201,7 +201,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
String testId = "matrixAndScaleToFitTransformation_matchesGoldenFile";
|
||||
Matrix translateRightMatrix = new Matrix();
|
||||
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects(
|
||||
(MatrixTransformation) (long presentationTimeUs) -> translateRightMatrix,
|
||||
@ -209,7 +209,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(TRANSLATE_THEN_ROTATE_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -222,13 +222,13 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
String testId = "bitmapOverlay_matchesGoldenFile";
|
||||
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
||||
BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(overlayBitmap);
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects(new OverlayEffect(ImmutableList.of(bitmapOverlay)))
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(BITMAP_OVERLAY_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -241,7 +241,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
String testId = "scaleToFitAndMatrixTransformation_matchesGoldenFile";
|
||||
Matrix translateRightMatrix = new Matrix();
|
||||
translateRightMatrix.postTranslate(/* dx= */ 1, /* dy= */ 0);
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects(
|
||||
new ScaleToFitTransformation.Builder().setRotationDegrees(45).build(),
|
||||
@ -249,7 +249,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(ROTATE_THEN_TRANSLATE_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -260,13 +260,13 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void presentation_createForHeight_matchesGoldenFile() throws Exception {
|
||||
String testId = "presentation_createForHeight_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects(Presentation.createForHeight(480))
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(REQUEST_OUTPUT_HEIGHT_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -277,7 +277,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void cropThenPresentation_matchesGoldenFile() throws Exception {
|
||||
String testId = "cropThenPresentation_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects(
|
||||
new Crop(
|
||||
@ -287,7 +287,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(CROP_THEN_ASPECT_RATIO_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -298,13 +298,13 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void scaleToFitTransformation_rotate45_matchesGoldenFile() throws Exception {
|
||||
String testId = "scaleToFitTransformation_rotate45_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects(new ScaleToFitTransformation.Builder().setRotationDegrees(45).build())
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(ROTATE45_SCALE_TO_FIT_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -315,7 +315,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void twoWrappedScaleToFitTransformations_matchesGoldenFile() throws Exception {
|
||||
String testId = "twoWrappedScaleToFitTransformations_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects(
|
||||
new GlEffectWrapper(
|
||||
@ -327,7 +327,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(ROTATE_THEN_SCALE_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -346,20 +346,20 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
}
|
||||
full10StepRotationAndCenterCrop.add(centerCrop);
|
||||
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setOutputFileLabel("centerCrop")
|
||||
.setEffects(centerCrop)
|
||||
.build();
|
||||
Bitmap centerCropResultBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
frameProcessorTestRunner.release();
|
||||
frameProcessorTestRunner =
|
||||
Bitmap centerCropResultBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
videoFrameProcessorTestRunner.release();
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setOutputFileLabel("full10StepRotationAndCenterCrop")
|
||||
.setEffects(full10StepRotationAndCenterCrop.build())
|
||||
.build();
|
||||
Bitmap fullRotationAndCenterCropResultBitmap =
|
||||
frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -371,11 +371,11 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void increaseBrightness_matchesGoldenFile() throws Exception {
|
||||
String testId = "increaseBrightness_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId).setEffects(new Brightness(0.5f)).build();
|
||||
Bitmap expectedBitmap = readBitmap(INCREASE_BRIGHTNESS_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -399,7 +399,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
new RgbAdjustment.Builder().setBlueScale(5).build(),
|
||||
new Rotation(/* degrees= */ 90),
|
||||
centerCrop);
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setOutputFileLabel("centerCrop")
|
||||
.setEffects(
|
||||
@ -407,16 +407,16 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
centerCrop)
|
||||
.build();
|
||||
Bitmap centerCropAndBrightnessIncreaseResultBitmap =
|
||||
frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
frameProcessorTestRunner.release();
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner.release();
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setOutputFileLabel("full4StepRotationBrightnessIncreaseAndCenterCrop")
|
||||
.setEffects(increaseBrightnessFullRotationCenterCrop)
|
||||
.build();
|
||||
Bitmap fullRotationBrightnessIncreaseAndCenterCropResultBitmap =
|
||||
frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -446,7 +446,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
new Rotation(/* degrees= */ 90),
|
||||
new FrameCache(/* capacity= */ 2),
|
||||
centerCrop);
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setOutputFileLabel("centerCrop")
|
||||
.setEffects(
|
||||
@ -454,16 +454,16 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
centerCrop)
|
||||
.build();
|
||||
Bitmap centerCropAndBrightnessIncreaseResultBitmap =
|
||||
frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
frameProcessorTestRunner.release();
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
videoFrameProcessorTestRunner.release();
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setOutputFileLabel("full4StepRotationBrightnessIncreaseAndCenterCrop")
|
||||
.setEffects(increaseBrightnessFullRotationCenterCrop)
|
||||
.build();
|
||||
|
||||
Bitmap fullRotationBrightnessIncreaseAndCenterCropResultBitmap =
|
||||
frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -477,7 +477,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
@Test
|
||||
public void grayscaleThenIncreaseRedChannel_matchesGoldenFile() throws Exception {
|
||||
String testId = "grayscaleThenIncreaseRedChannel_matchesGoldenFile";
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setEffects(
|
||||
RgbFilter.createGrayscaleFilter(),
|
||||
@ -485,7 +485,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
.build();
|
||||
Bitmap expectedBitmap = readBitmap(GRAYSCALE_THEN_INCREASE_RED_CHANNEL_PNG_ASSET_PATH);
|
||||
|
||||
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
|
||||
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
|
||||
float averagePixelAbsoluteDifference =
|
||||
@ -496,11 +496,11 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
// TODO(b/227624622): Add a test for HDR input after BitmapPixelTestUtil can read HDR bitmaps,
|
||||
// using GlEffectWrapper to ensure usage of intermediate textures.
|
||||
|
||||
private FrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder(
|
||||
private VideoFrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder(
|
||||
String testId) {
|
||||
return new FrameProcessorTestRunner.Builder()
|
||||
return new VideoFrameProcessorTestRunner.Builder()
|
||||
.setTestId(testId)
|
||||
.setFrameProcessorFactory(new GlEffectsFrameProcessor.Factory())
|
||||
.setVideoFrameProcessorFactory(new DefaultVideoFrameProcessor.Factory())
|
||||
.setVideoAssetPath(INPUT_SDR_MP4_ASSET_STRING);
|
||||
}
|
||||
|
||||
@ -538,10 +538,10 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a {@link GlEffect} to prevent the {@link GlEffectsFrameProcessor} from detecting its
|
||||
* Wraps a {@link GlEffect} to prevent the {@link DefaultVideoFrameProcessor} from detecting its
|
||||
* class and optimizing it.
|
||||
*
|
||||
* <p>This ensures that {@link GlEffectsFrameProcessor} uses a separate {@link GlShaderProgram}
|
||||
* <p>This ensures that {@link DefaultVideoFrameProcessor} uses a separate {@link GlShaderProgram}
|
||||
* for the wrapped {@link GlEffect} rather than merging it with preceding or subsequent {@link
|
||||
* GlEffect} instances and applying them in one combined {@link GlShaderProgram}.
|
||||
*/
|
||||
@ -555,7 +555,7 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||
|
||||
@Override
|
||||
public GlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
return effect.toGlShaderProgram(context, useHdr);
|
||||
}
|
||||
}
|
@ -27,9 +27,9 @@ import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.DebugViewProvider;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.SurfaceInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@ -50,9 +50,9 @@ import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Tests for frame release in {@link GlEffectsFrameProcessor}. */
|
||||
/** Tests for frame release in {@link DefaultVideoFrameProcessor}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
public final class DefaultVideoFrameProcessorVideoFrameReleaseTest {
|
||||
|
||||
private static final int WIDTH = 200;
|
||||
private static final int HEIGHT = 100;
|
||||
@ -68,12 +68,12 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
|
||||
private final LinkedBlockingQueue<Long> outputReleaseTimesNs = new LinkedBlockingQueue<>();
|
||||
|
||||
private @MonotonicNonNull GlEffectsFrameProcessor glEffectsFrameProcessor;
|
||||
private @MonotonicNonNull DefaultVideoFrameProcessor defaultVideoFrameProcessor;
|
||||
|
||||
@After
|
||||
public void release() {
|
||||
if (glEffectsFrameProcessor != null) {
|
||||
glEffectsFrameProcessor.release();
|
||||
if (defaultVideoFrameProcessor != null) {
|
||||
defaultVideoFrameProcessor.release();
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
|
||||
/* onFrameAvailableListener= */ presentationTimeUs -> {
|
||||
actualPresentationTimeUs.set(presentationTimeUs);
|
||||
checkNotNull(glEffectsFrameProcessor).releaseOutputFrame(releaseTimesNs);
|
||||
checkNotNull(defaultVideoFrameProcessor).releaseOutputFrame(releaseTimesNs);
|
||||
},
|
||||
/* releaseFramesAutomatically= */ false);
|
||||
|
||||
@ -149,18 +149,18 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
public void controlledFrameRelease_withOneFrameRequestImmediateRelease_releasesFrame()
|
||||
throws Exception {
|
||||
long originalPresentationTimeUs = 1234;
|
||||
long releaseTimesNs = FrameProcessor.RELEASE_OUTPUT_FRAME_IMMEDIATELY;
|
||||
long releaseTimesNs = VideoFrameProcessor.RELEASE_OUTPUT_FRAME_IMMEDIATELY;
|
||||
AtomicLong actualPresentationTimeUs = new AtomicLong();
|
||||
processFramesToEndOfStream(
|
||||
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
|
||||
/* onFrameAvailableListener= */ presentationTimeUs -> {
|
||||
actualPresentationTimeUs.set(presentationTimeUs);
|
||||
checkNotNull(glEffectsFrameProcessor).releaseOutputFrame(releaseTimesNs);
|
||||
checkNotNull(defaultVideoFrameProcessor).releaseOutputFrame(releaseTimesNs);
|
||||
},
|
||||
/* releaseFramesAutomatically= */ false);
|
||||
|
||||
assertThat(actualPresentationTimeUs.get()).isEqualTo(originalPresentationTimeUs);
|
||||
// The actual release time is determined by the FrameProcessor when releasing the frame.
|
||||
// The actual release time is determined by the VideoFrameProcessor when releasing the frame.
|
||||
ImmutableList<Long> actualReleaseTimesNs =
|
||||
waitForFrameReleaseAndGetReleaseTimesNs(/* expectedFrameCount= */ 1);
|
||||
assertThat(actualReleaseTimesNs).hasSize(1);
|
||||
@ -175,14 +175,15 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
|
||||
/* onFrameAvailableListener= */ presentationTimeUs -> {
|
||||
actualPresentationTimeUs.set(presentationTimeUs);
|
||||
checkNotNull(glEffectsFrameProcessor).releaseOutputFrame(releaseTimeBeforeCurrentTimeNs);
|
||||
checkNotNull(defaultVideoFrameProcessor)
|
||||
.releaseOutputFrame(releaseTimeBeforeCurrentTimeNs);
|
||||
},
|
||||
/* releaseFramesAutomatically= */ false);
|
||||
|
||||
ImmutableList<Long> actualReleaseTimesNs =
|
||||
waitForFrameReleaseAndGetReleaseTimesNs(/* expectedFrameCount= */ 1);
|
||||
assertThat(actualReleaseTimesNs).hasSize(1);
|
||||
// The actual release time is determined by the FrameProcessor when releasing the frame.
|
||||
// The actual release time is determined by the VideoFrameProcessor when releasing the frame.
|
||||
assertThat(actualReleaseTimesNs.get(0)).isAtLeast(releaseTimeBeforeCurrentTimeNs);
|
||||
}
|
||||
|
||||
@ -194,8 +195,8 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
/* inputPresentationTimesUs= */ new long[] {originalPresentationTimeUs},
|
||||
/* onFrameAvailableListener= */ presentationTimeNs -> {
|
||||
actualPresentationTimeUs.set(presentationTimeNs);
|
||||
checkNotNull(glEffectsFrameProcessor)
|
||||
.releaseOutputFrame(FrameProcessor.DROP_OUTPUT_FRAME);
|
||||
checkNotNull(defaultVideoFrameProcessor)
|
||||
.releaseOutputFrame(VideoFrameProcessor.DROP_OUTPUT_FRAME);
|
||||
},
|
||||
/* releaseFramesAutomatically= */ false);
|
||||
|
||||
@ -214,7 +215,7 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
/* inputPresentationTimesUs= */ originalPresentationTimesUs,
|
||||
/* onFrameAvailableListener= */ presentationTimeUs -> {
|
||||
actualPresentationTimesUs.add(presentationTimeUs);
|
||||
checkNotNull(glEffectsFrameProcessor)
|
||||
checkNotNull(defaultVideoFrameProcessor)
|
||||
.releaseOutputFrame(releaseTimesNs[frameIndex.getAndIncrement()]);
|
||||
try {
|
||||
// TODO(b/264252759): Investigate output frames being dropped and remove sleep.
|
||||
@ -254,11 +255,11 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
// TODO(b/264252759): Investigate output frames being dropped and remove sleep.
|
||||
// Frames can be dropped silently between EGL and the ImageReader. Sleep after each call
|
||||
// to swap buffers, to avoid this behavior.
|
||||
glEffectsFrameProcessor.releaseOutputFrame(releaseTimesNs[0]);
|
||||
defaultVideoFrameProcessor.releaseOutputFrame(releaseTimesNs[0]);
|
||||
Thread.sleep(PER_FRAME_RELEASE_WAIT_TIME_MS);
|
||||
glEffectsFrameProcessor.releaseOutputFrame(releaseTimesNs[1]);
|
||||
defaultVideoFrameProcessor.releaseOutputFrame(releaseTimesNs[1]);
|
||||
Thread.sleep(PER_FRAME_RELEASE_WAIT_TIME_MS);
|
||||
glEffectsFrameProcessor.releaseOutputFrame(releaseTimesNs[2]);
|
||||
defaultVideoFrameProcessor.releaseOutputFrame(releaseTimesNs[2]);
|
||||
Thread.sleep(PER_FRAME_RELEASE_WAIT_TIME_MS);
|
||||
|
||||
assertThat(actualPresentationTimesUs)
|
||||
@ -276,19 +277,19 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
void onFrameAvailable(long presentationTimeUs);
|
||||
}
|
||||
|
||||
@EnsuresNonNull("glEffectsFrameProcessor")
|
||||
@EnsuresNonNull("defaultVideoFrameProcessor")
|
||||
private void processFramesToEndOfStream(
|
||||
long[] inputPresentationTimesUs,
|
||||
OnFrameAvailableListener onFrameAvailableListener,
|
||||
boolean releaseFramesAutomatically)
|
||||
throws Exception {
|
||||
AtomicReference<@NullableType FrameProcessingException> frameProcessingExceptionReference =
|
||||
new AtomicReference<>();
|
||||
AtomicReference<@NullableType VideoFrameProcessingException>
|
||||
videoFrameProcessingExceptionReference = new AtomicReference<>();
|
||||
BlankFrameProducer blankFrameProducer = new BlankFrameProducer();
|
||||
CountDownLatch frameProcessingEndedCountDownLatch = new CountDownLatch(1);
|
||||
glEffectsFrameProcessor =
|
||||
CountDownLatch videoFrameProcessingEndedCountDownLatch = new CountDownLatch(1);
|
||||
defaultVideoFrameProcessor =
|
||||
checkNotNull(
|
||||
new GlEffectsFrameProcessor.Factory()
|
||||
new DefaultVideoFrameProcessor.Factory()
|
||||
.create(
|
||||
getApplicationContext(),
|
||||
ImmutableList.of((GlEffect) (context, useHdr) -> blankFrameProducer),
|
||||
@ -298,7 +299,7 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
/* isInputTextureExternal= */ true,
|
||||
releaseFramesAutomatically,
|
||||
MoreExecutors.directExecutor(),
|
||||
new FrameProcessor.Listener() {
|
||||
new VideoFrameProcessor.Listener() {
|
||||
@Override
|
||||
public void onOutputSizeChanged(int width, int height) {
|
||||
ImageReader outputImageReader =
|
||||
@ -307,7 +308,7 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
height,
|
||||
PixelFormat.RGBA_8888,
|
||||
/* maxImages= */ inputPresentationTimesUs.length);
|
||||
checkNotNull(glEffectsFrameProcessor)
|
||||
checkNotNull(defaultVideoFrameProcessor)
|
||||
.setOutputSurfaceInfo(
|
||||
new SurfaceInfo(outputImageReader.getSurface(), width, height));
|
||||
outputImageReader.setOnImageAvailableListener(
|
||||
@ -325,34 +326,35 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameProcessingError(FrameProcessingException exception) {
|
||||
frameProcessingExceptionReference.set(exception);
|
||||
frameProcessingEndedCountDownLatch.countDown();
|
||||
public void onError(VideoFrameProcessingException exception) {
|
||||
videoFrameProcessingExceptionReference.set(exception);
|
||||
videoFrameProcessingEndedCountDownLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameProcessingEnded() {
|
||||
frameProcessingEndedCountDownLatch.countDown();
|
||||
public void onEnded() {
|
||||
videoFrameProcessingEndedCountDownLatch.countDown();
|
||||
}
|
||||
}));
|
||||
|
||||
glEffectsFrameProcessor
|
||||
defaultVideoFrameProcessor
|
||||
.getTaskExecutor()
|
||||
.submit(
|
||||
() -> {
|
||||
blankFrameProducer.configureGlObjects();
|
||||
checkNotNull(glEffectsFrameProcessor)
|
||||
checkNotNull(defaultVideoFrameProcessor)
|
||||
.setInputFrameInfo(new FrameInfo.Builder(WIDTH, HEIGHT).build());
|
||||
// A frame needs to be registered despite not queuing any external input to ensure
|
||||
// that
|
||||
// the frame processor knows about the stream offset.
|
||||
glEffectsFrameProcessor.registerInputFrame();
|
||||
// the video frame processor knows about the stream offset.
|
||||
defaultVideoFrameProcessor.registerInputFrame();
|
||||
blankFrameProducer.produceBlankFramesAndQueueEndOfStream(inputPresentationTimesUs);
|
||||
});
|
||||
frameProcessingEndedCountDownLatch.await();
|
||||
@Nullable Exception frameProcessingException = frameProcessingExceptionReference.get();
|
||||
if (frameProcessingException != null) {
|
||||
throw frameProcessingException;
|
||||
videoFrameProcessingEndedCountDownLatch.await();
|
||||
@Nullable
|
||||
Exception videoFrameProcessingException = videoFrameProcessingExceptionReference.get();
|
||||
if (videoFrameProcessingException != null) {
|
||||
throw videoFrameProcessingException;
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,7 +376,7 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
private @MonotonicNonNull TextureInfo blankTexture;
|
||||
private @MonotonicNonNull OutputListener outputListener;
|
||||
|
||||
public void configureGlObjects() throws FrameProcessingException {
|
||||
public void configureGlObjects() throws VideoFrameProcessingException {
|
||||
try {
|
||||
int texId =
|
||||
GlUtil.createTexture(WIDTH, HEIGHT, /* useHighPrecisionColorComponents= */ false);
|
||||
@ -383,7 +385,7 @@ public final class GlEffectsFrameProcessorFrameReleaseTest {
|
||||
GlUtil.focusFramebufferUsingCurrentContext(fboId, WIDTH, HEIGHT);
|
||||
GlUtil.clearOutputFrame();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import android.graphics.Color;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
@ -50,7 +50,7 @@ import org.junit.runner.RunWith;
|
||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
* BitmapPixelTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output
|
||||
* bitmaps as recommended in {@link GlEffectsFrameProcessorPixelTest}.
|
||||
* bitmaps as recommended in {@link DefaultVideoFrameProcessorPixelTest}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class HslAdjustmentPixelTest {
|
||||
@ -100,7 +100,7 @@ public final class HslAdjustmentPixelTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||
public void release() throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (hslProcessor != null) {
|
||||
hslProcessor.release();
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ import android.graphics.Matrix;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@ -47,7 +47,7 @@ import org.junit.runner.RunWith;
|
||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
* BitmapPixelTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output
|
||||
* bitmaps as recommended in {@link GlEffectsFrameProcessorPixelTest}.
|
||||
* bitmaps as recommended in {@link DefaultVideoFrameProcessorPixelTest}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class MatrixShaderProgramPixelTest {
|
||||
@ -87,7 +87,7 @@ public final class MatrixShaderProgramPixelTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||
public void release() throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (matrixShaderProgram != null) {
|
||||
matrixShaderProgram.release();
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ import android.opengl.Matrix;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
@ -54,7 +54,7 @@ import org.junit.runner.RunWith;
|
||||
* <p>Expected bitmaps are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
* BitmapPixelTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output
|
||||
* bitmaps as recommended in {@link GlEffectsFrameProcessorPixelTest}.
|
||||
* bitmaps as recommended in {@link DefaultVideoFrameProcessorPixelTest}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class OverlayShaderProgramPixelTest {
|
||||
@ -101,7 +101,7 @@ public class OverlayShaderProgramPixelTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||
public void release() throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (overlayShaderProgram != null) {
|
||||
overlayShaderProgram.release();
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
@ -49,7 +49,7 @@ import org.junit.runner.RunWith;
|
||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
* BitmapPixelTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output
|
||||
* bitmaps as recommended in {@link GlEffectsFrameProcessorPixelTest}.
|
||||
* bitmaps as recommended in {@link DefaultVideoFrameProcessorPixelTest}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class PresentationPixelTest {
|
||||
@ -91,7 +91,7 @@ public final class PresentationPixelTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||
public void release() throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (presentationShaderProgram != null) {
|
||||
presentationShaderProgram.release();
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ import android.graphics.Color;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
@ -52,7 +52,7 @@ import org.junit.runner.RunWith;
|
||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
* BitmapPixelTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output
|
||||
* bitmaps as recommended in {@link GlEffectsFrameProcessorPixelTest}.
|
||||
* bitmaps as recommended in {@link DefaultVideoFrameProcessorPixelTest}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class RgbAdjustmentPixelTest {
|
||||
@ -99,7 +99,7 @@ public final class RgbAdjustmentPixelTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||
public void release() throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (matrixShaderProgram != null) {
|
||||
matrixShaderProgram.release();
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import android.graphics.Bitmap;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
@ -49,7 +49,7 @@ import org.junit.runner.RunWith;
|
||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
* BitmapPixelTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output
|
||||
* bitmaps as recommended in {@link GlEffectsFrameProcessorPixelTest}.
|
||||
* bitmaps as recommended in {@link DefaultVideoFrameProcessorPixelTest}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class RgbFilterPixelTest {
|
||||
@ -94,7 +94,7 @@ public final class RgbFilterPixelTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||
public void release() throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (matrixShaderProgram != null) {
|
||||
matrixShaderProgram.release();
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import android.graphics.Color;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||
@ -49,7 +49,7 @@ import org.junit.runner.RunWith;
|
||||
* <p>Expected images are taken from an emulator, so tests on different emulators or physical
|
||||
* devices may fail. To test on other devices, please increase the {@link
|
||||
* BitmapPixelTestUtil#MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE} and/or inspect the saved output
|
||||
* bitmaps as recommended in {@link GlEffectsFrameProcessorPixelTest}.
|
||||
* bitmaps as recommended in {@link DefaultVideoFrameProcessorPixelTest}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SingleColorLutPixelTest {
|
||||
@ -88,7 +88,7 @@ public class SingleColorLutPixelTest {
|
||||
}
|
||||
|
||||
@After
|
||||
public void release() throws GlUtil.GlException, FrameProcessingException {
|
||||
public void release() throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (colorLutShaderProgram != null) {
|
||||
colorLutShaderProgram.release();
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.BitmapLoader;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
@ -44,9 +44,9 @@ public abstract class BitmapOverlay extends TextureOverlay {
|
||||
* Returns the overlay bitmap displayed at the specified timestamp.
|
||||
*
|
||||
* @param presentationTimeUs The presentation timestamp of the current frame, in microseconds.
|
||||
* @throws FrameProcessingException If an error occurs while processing or drawing the frame.
|
||||
* @throws VideoFrameProcessingException If an error occurs while processing or drawing the frame.
|
||||
*/
|
||||
public abstract Bitmap getBitmap(long presentationTimeUs) throws FrameProcessingException;
|
||||
public abstract Bitmap getBitmap(long presentationTimeUs) throws VideoFrameProcessingException;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@ -61,7 +61,7 @@ public abstract class BitmapOverlay extends TextureOverlay {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTextureId(long presentationTimeUs) throws FrameProcessingException {
|
||||
public int getTextureId(long presentationTimeUs) throws VideoFrameProcessingException {
|
||||
Bitmap bitmap = getBitmap(presentationTimeUs);
|
||||
if (bitmap != lastBitmap) {
|
||||
try {
|
||||
@ -79,7 +79,7 @@ public abstract class BitmapOverlay extends TextureOverlay {
|
||||
/* border= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
return lastTextureId;
|
||||
@ -134,14 +134,14 @@ public abstract class BitmapOverlay extends TextureOverlay {
|
||||
private @MonotonicNonNull Bitmap lastBitmap;
|
||||
|
||||
@Override
|
||||
public Bitmap getBitmap(long presentationTimeUs) throws FrameProcessingException {
|
||||
public Bitmap getBitmap(long presentationTimeUs) throws VideoFrameProcessingException {
|
||||
if (lastBitmap == null) {
|
||||
BitmapLoader bitmapLoader = new SimpleBitmapLoader();
|
||||
ListenableFuture<Bitmap> future = bitmapLoader.loadBitmap(overlayBitmapUri);
|
||||
try {
|
||||
lastBitmap = future.get();
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
return lastBitmap;
|
||||
|
@ -35,7 +35,7 @@ import java.util.Queue;
|
||||
|
||||
private final GlShaderProgram producingGlShaderProgram;
|
||||
private final GlShaderProgram consumingGlShaderProgram;
|
||||
private final FrameProcessingTaskExecutor frameProcessingTaskExecutor;
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
||||
|
||||
@GuardedBy("this")
|
||||
private final Queue<Pair<TextureInfo, Long>> availableFrames;
|
||||
@ -50,18 +50,18 @@ import java.util.Queue;
|
||||
* as {@link OutputListener}.
|
||||
* @param consumingGlShaderProgram The {@link GlShaderProgram} for which this listener will be set
|
||||
* as {@link InputListener}.
|
||||
* @param frameProcessingTaskExecutor The {@link FrameProcessingTaskExecutor} that is used for
|
||||
* OpenGL calls. All calls to the producing/consuming {@link GlShaderProgram} will be executed
|
||||
* by the {@link FrameProcessingTaskExecutor}. The caller is responsible for releasing the
|
||||
* {@link FrameProcessingTaskExecutor}.
|
||||
* @param videoFrameProcessingTaskExecutor The {@link VideoFrameProcessingTaskExecutor} that is
|
||||
* used for OpenGL calls. All calls to the producing/consuming {@link GlShaderProgram} will be
|
||||
* executed by the {@link VideoFrameProcessingTaskExecutor}. The caller is responsible for
|
||||
* releasing the {@link VideoFrameProcessingTaskExecutor}.
|
||||
*/
|
||||
public ChainingGlShaderProgramListener(
|
||||
GlShaderProgram producingGlShaderProgram,
|
||||
GlShaderProgram consumingGlShaderProgram,
|
||||
FrameProcessingTaskExecutor frameProcessingTaskExecutor) {
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
|
||||
this.producingGlShaderProgram = producingGlShaderProgram;
|
||||
this.consumingGlShaderProgram = consumingGlShaderProgram;
|
||||
this.frameProcessingTaskExecutor = frameProcessingTaskExecutor;
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
availableFrames = new ArrayDeque<>();
|
||||
}
|
||||
|
||||
@ -75,9 +75,10 @@ import java.util.Queue;
|
||||
|
||||
long presentationTimeUs = pendingFrame.second;
|
||||
if (presentationTimeUs == C.TIME_END_OF_SOURCE) {
|
||||
frameProcessingTaskExecutor.submit(consumingGlShaderProgram::signalEndOfCurrentInputStream);
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
consumingGlShaderProgram::signalEndOfCurrentInputStream);
|
||||
} else {
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() ->
|
||||
consumingGlShaderProgram.queueInputFrame(
|
||||
/* inputTexture= */ pendingFrame.first, presentationTimeUs));
|
||||
@ -86,7 +87,7 @@ import java.util.Queue;
|
||||
|
||||
@Override
|
||||
public void onInputFrameProcessed(TextureInfo inputTexture) {
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() -> producingGlShaderProgram.releaseOutputFrame(inputTexture));
|
||||
}
|
||||
|
||||
@ -94,14 +95,14 @@ import java.util.Queue;
|
||||
public synchronized void onFlush() {
|
||||
consumingGlShaderProgramInputCapacity = 0;
|
||||
availableFrames.clear();
|
||||
frameProcessingTaskExecutor.submit(producingGlShaderProgram::flush);
|
||||
videoFrameProcessingTaskExecutor.submit(producingGlShaderProgram::flush);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onOutputFrameAvailable(
|
||||
TextureInfo outputTexture, long presentationTimeUs) {
|
||||
if (consumingGlShaderProgramInputCapacity > 0) {
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() ->
|
||||
consumingGlShaderProgram.queueInputFrame(
|
||||
/* inputTexture= */ outputTexture, presentationTimeUs));
|
||||
@ -116,7 +117,8 @@ import java.util.Queue;
|
||||
if (!availableFrames.isEmpty()) {
|
||||
availableFrames.add(new Pair<>(TextureInfo.UNSET, C.TIME_END_OF_SOURCE));
|
||||
} else {
|
||||
frameProcessingTaskExecutor.submit(consumingGlShaderProgram::signalEndOfCurrentInputStream);
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
consumingGlShaderProgram::signalEndOfCurrentInputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ package androidx.media3.effect;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
@ -45,7 +45,7 @@ public interface ColorLut extends GlEffect {
|
||||
@Override
|
||||
@WorkerThread
|
||||
default SingleFrameGlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
return new ColorLutShaderProgram(context, /* colorLut= */ this, useHdr);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLES20;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlProgram;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
@ -41,10 +41,10 @@ import java.io.IOException;
|
||||
* @param colorLut The {@link ColorLut} to apply to each frame in order.
|
||||
* @param useHdr Whether input textures come from an HDR source. If {@code true}, colors will be
|
||||
* in linear RGB BT.2020. If {@code false}, colors will be in linear RGB BT.709.
|
||||
* @throws FrameProcessingException If a problem occurs while reading shader files.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while reading shader files.
|
||||
*/
|
||||
public ColorLutShaderProgram(Context context, ColorLut colorLut, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
super(useHdr);
|
||||
// TODO(b/246315245): Add HDR support.
|
||||
checkArgument(!useHdr, "ColorLutShaderProgram does not support HDR colors.");
|
||||
@ -53,7 +53,7 @@ import java.io.IOException;
|
||||
try {
|
||||
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
|
||||
} catch (IOException | GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
|
||||
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
|
||||
@ -73,7 +73,8 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs) throws FrameProcessingException {
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs)
|
||||
throws VideoFrameProcessingException {
|
||||
try {
|
||||
glProgram.use();
|
||||
glProgram.setSamplerTexIdUniform("uTexSampler", inputTexId, /* texUnitIndex= */ 0);
|
||||
@ -84,18 +85,18 @@ import java.io.IOException;
|
||||
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws FrameProcessingException {
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
super.release();
|
||||
try {
|
||||
colorLut.release();
|
||||
glProgram.delete();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ package androidx.media3.effect;
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
/** A {@link GlEffect} to control the contrast of video frames. */
|
||||
@ -42,7 +42,7 @@ public class Contrast implements GlEffect {
|
||||
|
||||
@Override
|
||||
public SingleFrameGlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
return new ContrastShaderProgram(context, this, useHdr);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ package androidx.media3.effect;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLES20;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlProgram;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
@ -38,10 +38,10 @@ import java.io.IOException;
|
||||
* @param contrastEffect The {@link Contrast} to apply to each frame in order.
|
||||
* @param useHdr Whether input textures come from an HDR source. If {@code true}, colors will be
|
||||
* in linear RGB BT.2020. If {@code false}, colors will be in linear RGB BT.709.
|
||||
* @throws FrameProcessingException If a problem occurs while reading shader files.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while reading shader files.
|
||||
*/
|
||||
public ContrastShaderProgram(Context context, Contrast contrastEffect, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
super(useHdr);
|
||||
// Use 1.0001f to avoid division by zero issues.
|
||||
float contrastFactor = (1 + contrastEffect.contrast) / (1.0001f - contrastEffect.contrast);
|
||||
@ -49,7 +49,7 @@ import java.io.IOException;
|
||||
try {
|
||||
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
|
||||
} catch (IOException | GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
|
||||
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
|
||||
@ -70,7 +70,8 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs) throws FrameProcessingException {
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs)
|
||||
throws VideoFrameProcessingException {
|
||||
try {
|
||||
glProgram.use();
|
||||
glProgram.setSamplerTexIdUniform("uTexSampler", inputTexId, /* texUnitIndex= */ 0);
|
||||
@ -79,17 +80,17 @@ import java.io.IOException;
|
||||
// The four-vertex triangle strip forms a quad.
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e, presentationTimeUs);
|
||||
throw new VideoFrameProcessingException(e, presentationTimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws FrameProcessingException {
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
super.release();
|
||||
try {
|
||||
glProgram.delete();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,9 +37,9 @@ import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.DebugViewProvider;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.SurfaceInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
@ -54,14 +54,14 @@ import java.util.concurrent.Future;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/**
|
||||
* A {@link FrameProcessor} implementation that applies {@link GlEffect} instances using OpenGL on a
|
||||
* background thread.
|
||||
* A {@link VideoFrameProcessor} implementation that applies {@link GlEffect} instances using OpenGL
|
||||
* on a background thread.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
|
||||
|
||||
/** A factory for {@link GlEffectsFrameProcessor} instances. */
|
||||
public static class Factory implements FrameProcessor.Factory {
|
||||
/** A factory for {@link DefaultVideoFrameProcessor} instances. */
|
||||
public static class Factory implements VideoFrameProcessor.Factory {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
@ -88,11 +88,11 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
* be configured with {@link GlUtil#EGL_CONFIG_ATTRIBUTES_RGBA_1010102}. Otherwise, the context
|
||||
* will be configured with {@link GlUtil#EGL_CONFIG_ATTRIBUTES_RGBA_8888}.
|
||||
*
|
||||
* <p>If invoking the {@code listener} on {@link GlEffectsFrameProcessor}'s internal thread is
|
||||
* desired, pass a {@link MoreExecutors#directExecutor() direct listenerExecutor}.
|
||||
* <p>If invoking the {@code listener} on {@link DefaultVideoFrameProcessor}'s internal thread
|
||||
* is desired, pass a {@link MoreExecutors#directExecutor() direct listenerExecutor}.
|
||||
*/
|
||||
@Override
|
||||
public GlEffectsFrameProcessor create(
|
||||
public DefaultVideoFrameProcessor create(
|
||||
Context context,
|
||||
List<Effect> effects,
|
||||
DebugViewProvider debugViewProvider,
|
||||
@ -102,7 +102,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
boolean releaseFramesAutomatically,
|
||||
Executor listenerExecutor,
|
||||
Listener listener)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
// TODO(b/261188041) Add tests to verify the Listener is invoked on the given Executor.
|
||||
|
||||
checkArgument(inputColorInfo.isValid());
|
||||
@ -126,7 +126,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
|
||||
ExecutorService singleThreadExecutorService = Util.newSingleThreadExecutor(THREAD_NAME);
|
||||
|
||||
Future<GlEffectsFrameProcessor> glFrameProcessorFuture =
|
||||
Future<DefaultVideoFrameProcessor> glFrameProcessorFuture =
|
||||
singleThreadExecutorService.submit(
|
||||
() ->
|
||||
createOpenGlObjectsAndFrameProcessor(
|
||||
@ -144,10 +144,10 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
try {
|
||||
return glFrameProcessorFuture.get();
|
||||
} catch (ExecutionException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,7 +155,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
/**
|
||||
* Creates the OpenGL context, surfaces, textures, and frame buffers, initializes {@link
|
||||
* GlShaderProgram} instances corresponding to the {@link GlEffect} instances, and returns a new
|
||||
* {@code GlEffectsFrameProcessor}.
|
||||
* {@code DefaultVideoFrameProcessor}.
|
||||
*
|
||||
* <p>All {@link Effect} instances must be {@link GlEffect} instances.
|
||||
*
|
||||
@ -163,7 +163,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
* commands will be called on that thread.
|
||||
*/
|
||||
@WorkerThread
|
||||
private static GlEffectsFrameProcessor createOpenGlObjectsAndFrameProcessor(
|
||||
private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor(
|
||||
Context context,
|
||||
List<Effect> effects,
|
||||
DebugViewProvider debugViewProvider,
|
||||
@ -174,7 +174,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
ExecutorService singleThreadExecutorService,
|
||||
Executor executor,
|
||||
Listener listener)
|
||||
throws GlUtil.GlException, FrameProcessingException {
|
||||
throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
||||
|
||||
// TODO(b/237674316): Delay initialization of things requiring the colorInfo, to
|
||||
@ -198,7 +198,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||
// On API<33, the system cannot display PQ content correctly regardless of whether BT2020 PQ
|
||||
// GL extension is supported.
|
||||
throw new FrameProcessingException("BT.2020 PQ OpenGL output isn't supported.");
|
||||
throw new VideoFrameProcessingException("BT.2020 PQ OpenGL output isn't supported.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,16 +215,16 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
releaseFramesAutomatically,
|
||||
executor,
|
||||
listener);
|
||||
FrameProcessingTaskExecutor frameProcessingTaskExecutor =
|
||||
new FrameProcessingTaskExecutor(singleThreadExecutorService, listener);
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor =
|
||||
new VideoFrameProcessingTaskExecutor(singleThreadExecutorService, listener);
|
||||
chainShaderProgramsWithListeners(
|
||||
shaderPrograms, frameProcessingTaskExecutor, listener, executor);
|
||||
shaderPrograms, videoFrameProcessingTaskExecutor, listener, executor);
|
||||
|
||||
return new GlEffectsFrameProcessor(
|
||||
return new DefaultVideoFrameProcessor(
|
||||
eglDisplay,
|
||||
eglContext,
|
||||
isInputTextureExternal,
|
||||
frameProcessingTaskExecutor,
|
||||
videoFrameProcessingTaskExecutor,
|
||||
shaderPrograms,
|
||||
releaseFramesAutomatically);
|
||||
}
|
||||
@ -252,7 +252,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
boolean releaseFramesAutomatically,
|
||||
Executor executor,
|
||||
Listener listener)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
ImmutableList.Builder<GlShaderProgram> shaderProgramListBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<GlMatrixTransformation> matrixTransformationListBuilder =
|
||||
new ImmutableList.Builder<>();
|
||||
@ -266,7 +266,8 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
.build();
|
||||
for (int i = 0; i < effects.size(); i++) {
|
||||
Effect effect = effects.get(i);
|
||||
checkArgument(effect instanceof GlEffect, "GlEffectsFrameProcessor only supports GlEffects");
|
||||
checkArgument(
|
||||
effect instanceof GlEffect, "DefaultVideoFrameProcessor only supports GlEffects");
|
||||
GlEffect glEffect = (GlEffect) effect;
|
||||
// The following logic may change the order of the RgbMatrix and GlMatrixTransformation
|
||||
// effects. This does not influence the output since RgbMatrix only changes the individual
|
||||
@ -333,18 +334,18 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
*/
|
||||
private static void chainShaderProgramsWithListeners(
|
||||
ImmutableList<GlShaderProgram> shaderPrograms,
|
||||
FrameProcessingTaskExecutor frameProcessingTaskExecutor,
|
||||
Listener frameProcessorListener,
|
||||
Executor frameProcessorListenerExecutor) {
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor,
|
||||
Listener videoFrameProcessorListener,
|
||||
Executor videoFrameProcessorListenerExecutor) {
|
||||
for (int i = 0; i < shaderPrograms.size() - 1; i++) {
|
||||
GlShaderProgram producingGlShaderProgram = shaderPrograms.get(i);
|
||||
GlShaderProgram consumingGlShaderProgram = shaderPrograms.get(i + 1);
|
||||
ChainingGlShaderProgramListener chainingGlShaderProgramListener =
|
||||
new ChainingGlShaderProgramListener(
|
||||
producingGlShaderProgram, consumingGlShaderProgram, frameProcessingTaskExecutor);
|
||||
producingGlShaderProgram, consumingGlShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
producingGlShaderProgram.setOutputListener(chainingGlShaderProgramListener);
|
||||
producingGlShaderProgram.setErrorListener(
|
||||
frameProcessorListenerExecutor, frameProcessorListener::onFrameProcessingError);
|
||||
videoFrameProcessorListenerExecutor, videoFrameProcessorListener::onError);
|
||||
consumingGlShaderProgram.setInputListener(chainingGlShaderProgramListener);
|
||||
}
|
||||
}
|
||||
@ -354,7 +355,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
|
||||
private final EGLDisplay eglDisplay;
|
||||
private final EGLContext eglContext;
|
||||
private final FrameProcessingTaskExecutor frameProcessingTaskExecutor;
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
||||
private @MonotonicNonNull InternalTextureManager inputInternalTextureManager;
|
||||
private @MonotonicNonNull ExternalTextureManager inputExternalTextureManager;
|
||||
private final boolean releaseFramesAutomatically;
|
||||
@ -370,18 +371,18 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
private volatile @MonotonicNonNull FrameInfo nextInputFrameInfo;
|
||||
private volatile boolean inputStreamEnded;
|
||||
|
||||
private GlEffectsFrameProcessor(
|
||||
private DefaultVideoFrameProcessor(
|
||||
EGLDisplay eglDisplay,
|
||||
EGLContext eglContext,
|
||||
boolean isInputTextureExternal,
|
||||
FrameProcessingTaskExecutor frameProcessingTaskExecutor,
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor,
|
||||
ImmutableList<GlShaderProgram> shaderPrograms,
|
||||
boolean releaseFramesAutomatically)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
|
||||
this.eglDisplay = eglDisplay;
|
||||
this.eglContext = eglContext;
|
||||
this.frameProcessingTaskExecutor = frameProcessingTaskExecutor;
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
this.releaseFramesAutomatically = releaseFramesAutomatically;
|
||||
|
||||
checkState(!shaderPrograms.isEmpty());
|
||||
@ -393,11 +394,11 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
checkState(inputShaderProgram instanceof ExternalShaderProgram);
|
||||
inputExternalTextureManager =
|
||||
new ExternalTextureManager(
|
||||
(ExternalShaderProgram) inputShaderProgram, frameProcessingTaskExecutor);
|
||||
(ExternalShaderProgram) inputShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
inputShaderProgram.setInputListener(inputExternalTextureManager);
|
||||
} else {
|
||||
inputInternalTextureManager =
|
||||
new InternalTextureManager(inputShaderProgram, frameProcessingTaskExecutor);
|
||||
new InternalTextureManager(inputShaderProgram, videoFrameProcessingTaskExecutor);
|
||||
inputShaderProgram.setInputListener(inputInternalTextureManager);
|
||||
}
|
||||
|
||||
@ -406,10 +407,10 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
previousStreamOffsetUs = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
/** Returns the task executor that runs frame processing tasks. */
|
||||
/** Returns the task executor that runs video frame processing tasks. */
|
||||
@VisibleForTesting
|
||||
/* package */ FrameProcessingTaskExecutor getTaskExecutor() {
|
||||
return frameProcessingTaskExecutor;
|
||||
/* package */ VideoFrameProcessingTaskExecutor getTaskExecutor() {
|
||||
return videoFrameProcessingTaskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -421,7 +422,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
* call this method after instantiation to ensure that buffers are handled at full resolution. See
|
||||
* {@link SurfaceTexture#setDefaultBufferSize(int, int)} for more information.
|
||||
*
|
||||
* <p>This method should only be used for when the {@link FrameProcessor}'s {@code
|
||||
* <p>This method should only be used for when the {@link VideoFrameProcessor}'s {@code
|
||||
* isInputTextureExternal} parameter is set to {@code true}.
|
||||
*
|
||||
* @param width The default width for input buffers, in pixels.
|
||||
@ -476,7 +477,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
checkState(
|
||||
!releaseFramesAutomatically,
|
||||
"Calling this method is not allowed when releaseFramesAutomatically is enabled");
|
||||
frameProcessingTaskExecutor.submitWithHighPriority(
|
||||
videoFrameProcessingTaskExecutor.submitWithHighPriority(
|
||||
() -> finalShaderProgramWrapper.releaseOutputFrame(releaseTimeNs));
|
||||
}
|
||||
|
||||
@ -485,20 +486,20 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
checkState(!inputStreamEnded);
|
||||
inputStreamEnded = true;
|
||||
if (inputInternalTextureManager != null) {
|
||||
frameProcessingTaskExecutor.submit(inputInternalTextureManager::signalEndOfInput);
|
||||
videoFrameProcessingTaskExecutor.submit(inputInternalTextureManager::signalEndOfInput);
|
||||
}
|
||||
if (inputExternalTextureManager != null) {
|
||||
frameProcessingTaskExecutor.submit(inputExternalTextureManager::signalEndOfInput);
|
||||
videoFrameProcessingTaskExecutor.submit(inputExternalTextureManager::signalEndOfInput);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
try {
|
||||
frameProcessingTaskExecutor.flush();
|
||||
videoFrameProcessingTaskExecutor.flush();
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
checkNotNull(inputExternalTextureManager).setOnFlushCompleteListener(latch::countDown);
|
||||
frameProcessingTaskExecutor.submit(finalShaderProgramWrapper::flush);
|
||||
videoFrameProcessingTaskExecutor.submit(finalShaderProgramWrapper::flush);
|
||||
latch.await();
|
||||
inputExternalTextureManager.setOnFlushCompleteListener(null);
|
||||
} catch (InterruptedException e) {
|
||||
@ -509,7 +510,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
@Override
|
||||
public void release() {
|
||||
try {
|
||||
frameProcessingTaskExecutor.release(
|
||||
videoFrameProcessingTaskExecutor.release(
|
||||
/* releaseTask= */ this::releaseShaderProgramsAndDestroyGlContext, RELEASE_WAIT_TIME_MS);
|
||||
} catch (InterruptedException unexpected) {
|
||||
Thread.currentThread().interrupt();
|
||||
@ -548,7 +549,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor {
|
||||
*/
|
||||
@WorkerThread
|
||||
private void releaseShaderProgramsAndDestroyGlContext()
|
||||
throws GlUtil.GlException, FrameProcessingException {
|
||||
throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
for (int i = 0; i < allShaderPrograms.size(); i++) {
|
||||
allShaderPrograms.get(i).release();
|
||||
}
|
@ -23,8 +23,8 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.effect.GlShaderProgram.InputListener;
|
||||
import java.util.Queue;
|
||||
@ -37,7 +37,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
*/
|
||||
/* package */ final class ExternalTextureManager implements InputListener {
|
||||
|
||||
private final FrameProcessingTaskExecutor frameProcessingTaskExecutor;
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
||||
private final ExternalShaderProgram externalShaderProgram;
|
||||
private final int externalTexId;
|
||||
private final Surface surface;
|
||||
@ -61,7 +61,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
@Nullable private volatile FrameInfo currentFrame;
|
||||
|
||||
// TODO(b/238302341) Remove the use of after flush task, block the calling thread instead.
|
||||
@Nullable private volatile FrameProcessingTask onFlushCompleteTask;
|
||||
@Nullable private volatile VideoFrameProcessingTask onFlushCompleteTask;
|
||||
|
||||
private long previousStreamOffsetUs;
|
||||
|
||||
@ -70,21 +70,21 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
*
|
||||
* @param externalShaderProgram The {@link ExternalShaderProgram} for which this {@code
|
||||
* ExternalTextureManager} will be set as the {@link InputListener}.
|
||||
* @param frameProcessingTaskExecutor The {@link FrameProcessingTaskExecutor}.
|
||||
* @throws FrameProcessingException If a problem occurs while creating the external texture.
|
||||
* @param videoFrameProcessingTaskExecutor The {@link VideoFrameProcessingTaskExecutor}.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while creating the external texture.
|
||||
*/
|
||||
// The onFrameAvailableListener will not be invoked until the constructor returns.
|
||||
@SuppressWarnings("nullness:method.invocation.invalid")
|
||||
public ExternalTextureManager(
|
||||
ExternalShaderProgram externalShaderProgram,
|
||||
FrameProcessingTaskExecutor frameProcessingTaskExecutor)
|
||||
throws FrameProcessingException {
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor)
|
||||
throws VideoFrameProcessingException {
|
||||
this.externalShaderProgram = externalShaderProgram;
|
||||
this.frameProcessingTaskExecutor = frameProcessingTaskExecutor;
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
try {
|
||||
externalTexId = GlUtil.createExternalTexture();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
surfaceTexture = new SurfaceTexture(externalTexId);
|
||||
textureTransformMatrix = new float[16];
|
||||
@ -93,7 +93,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
previousStreamOffsetUs = C.TIME_UNSET;
|
||||
surfaceTexture.setOnFrameAvailableListener(
|
||||
unused ->
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() -> {
|
||||
if (numberOfFramesToDropOnBecomingAvailable > 0) {
|
||||
numberOfFramesToDropOnBecomingAvailable--;
|
||||
@ -119,7 +119,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Override
|
||||
public void onReadyToAcceptInputFrame() {
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() -> {
|
||||
externalShaderProgramInputCapacity.incrementAndGet();
|
||||
maybeQueueFrameToExternalShaderProgram();
|
||||
@ -128,7 +128,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Override
|
||||
public void onInputFrameProcessed(TextureInfo inputTexture) {
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() -> {
|
||||
currentFrame = null;
|
||||
maybeQueueFrameToExternalShaderProgram();
|
||||
@ -136,13 +136,13 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
}
|
||||
|
||||
/** Sets the task to run on completing flushing, or {@code null} to clear any task. */
|
||||
public void setOnFlushCompleteListener(@Nullable FrameProcessingTask task) {
|
||||
public void setOnFlushCompleteListener(@Nullable VideoFrameProcessingTask task) {
|
||||
onFlushCompleteTask = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFlush() {
|
||||
frameProcessingTaskExecutor.submit(this::flush);
|
||||
videoFrameProcessingTaskExecutor.submit(this::flush);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,10 +169,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
/**
|
||||
* Signals the end of the input.
|
||||
*
|
||||
* @see FrameProcessor#signalEndOfInput()
|
||||
* @see VideoFrameProcessor#signalEndOfInput()
|
||||
*/
|
||||
public void signalEndOfInput() {
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() -> {
|
||||
inputStreamEnded = true;
|
||||
if (pendingFrames.isEmpty() && currentFrame == null) {
|
||||
@ -204,7 +204,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
if (onFlushCompleteTask == null || numberOfFramesToDropOnBecomingAvailable > 0) {
|
||||
return;
|
||||
}
|
||||
frameProcessingTaskExecutor.submitWithHighPriority(onFlushCompleteTask);
|
||||
videoFrameProcessingTaskExecutor.submitWithHighPriority(onFlushCompleteTask);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
@ -36,9 +36,9 @@ import androidx.annotation.WorkerThread;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.DebugViewProvider;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.SurfaceInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.Size;
|
||||
@ -59,7 +59,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
* the frames to the dimensions specified by the provided {@link SurfaceInfo}.
|
||||
*
|
||||
* <p>This wrapper is used for the final {@link GlShaderProgram} instance in the chain of {@link
|
||||
* GlShaderProgram} instances used by {@link FrameProcessor}.
|
||||
* GlShaderProgram} instances used by {@link VideoFrameProcessor}.
|
||||
*/
|
||||
/* package */ final class FinalMatrixShaderProgramWrapper implements ExternalShaderProgram {
|
||||
|
||||
@ -76,8 +76,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
private final ColorInfo inputColorInfo;
|
||||
private final ColorInfo outputColorInfo;
|
||||
private final boolean releaseFramesAutomatically;
|
||||
private final Executor frameProcessorListenerExecutor;
|
||||
private final FrameProcessor.Listener frameProcessorListener;
|
||||
private final Executor videoFrameProcessorListenerExecutor;
|
||||
private final VideoFrameProcessor.Listener videoFrameProcessorListener;
|
||||
private final float[] textureTransformMatrix;
|
||||
private final Queue<Long> streamOffsetUsQueue;
|
||||
private final Queue<Pair<TextureInfo, Long>> availableFrames;
|
||||
@ -112,8 +112,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
boolean sampleFromInputTexture,
|
||||
boolean isInputTextureExternal,
|
||||
boolean releaseFramesAutomatically,
|
||||
Executor frameProcessorListenerExecutor,
|
||||
FrameProcessor.Listener frameProcessorListener) {
|
||||
Executor videoFrameProcessorListenerExecutor,
|
||||
VideoFrameProcessor.Listener videoFrameProcessorListener) {
|
||||
this.context = context;
|
||||
this.matrixTransformations = matrixTransformations;
|
||||
this.rgbMatrices = rgbMatrices;
|
||||
@ -125,8 +125,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
this.inputColorInfo = inputColorInfo;
|
||||
this.outputColorInfo = outputColorInfo;
|
||||
this.releaseFramesAutomatically = releaseFramesAutomatically;
|
||||
this.frameProcessorListenerExecutor = frameProcessorListenerExecutor;
|
||||
this.frameProcessorListener = frameProcessorListener;
|
||||
this.videoFrameProcessorListenerExecutor = videoFrameProcessorListenerExecutor;
|
||||
this.videoFrameProcessorListener = videoFrameProcessorListener;
|
||||
|
||||
textureTransformMatrix = GlUtil.create4x4IdentityMatrix();
|
||||
streamOffsetUsQueue = new ConcurrentLinkedQueue<>();
|
||||
@ -142,13 +142,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
@Override
|
||||
public void setOutputListener(OutputListener outputListener) {
|
||||
// The FrameProcessor.Listener passed to the constructor is used for output-related events.
|
||||
// The VideoFrameProcessor.Listener passed to the constructor is used for output-related events.
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorListener(Executor executor, ErrorListener errorListener) {
|
||||
// The FrameProcessor.Listener passed to the constructor is used for errors.
|
||||
// The VideoFrameProcessor.Listener passed to the constructor is used for errors.
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@ -157,8 +157,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
long streamOffsetUs =
|
||||
checkStateNotNull(streamOffsetUsQueue.peek(), "No input stream specified.");
|
||||
long offsetPresentationTimeUs = presentationTimeUs + streamOffsetUs;
|
||||
frameProcessorListenerExecutor.execute(
|
||||
() -> frameProcessorListener.onOutputFrameAvailable(offsetPresentationTimeUs));
|
||||
videoFrameProcessorListenerExecutor.execute(
|
||||
() -> videoFrameProcessorListener.onOutputFrameAvailable(offsetPresentationTimeUs));
|
||||
if (releaseFramesAutomatically) {
|
||||
renderFrameToSurfaces(
|
||||
inputTexture, presentationTimeUs, /* releaseTimeNs= */ offsetPresentationTimeUs * 1000);
|
||||
@ -189,7 +189,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
checkState(!streamOffsetUsQueue.isEmpty(), "No input stream to end.");
|
||||
streamOffsetUsQueue.remove();
|
||||
if (streamOffsetUsQueue.isEmpty()) {
|
||||
frameProcessorListenerExecutor.execute(frameProcessorListener::onFrameProcessingEnded);
|
||||
videoFrameProcessorListenerExecutor.execute(videoFrameProcessorListener::onEnded);
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,14 +206,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
@Override
|
||||
@WorkerThread
|
||||
public synchronized void release() throws FrameProcessingException {
|
||||
public synchronized void release() throws VideoFrameProcessingException {
|
||||
if (matrixShaderProgram != null) {
|
||||
matrixShaderProgram.release();
|
||||
}
|
||||
try {
|
||||
GlUtil.destroyEglSurface(eglDisplay, outputEglSurface);
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,7 +247,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
/**
|
||||
* Sets the output {@link SurfaceInfo}.
|
||||
*
|
||||
* @see FrameProcessor#setOutputSurfaceInfo(SurfaceInfo)
|
||||
* @see VideoFrameProcessor#setOutputSurfaceInfo(SurfaceInfo)
|
||||
*/
|
||||
public synchronized void setOutputSurfaceInfo(@Nullable SurfaceInfo outputSurfaceInfo) {
|
||||
if (!Util.areEqual(this.outputSurfaceInfo, outputSurfaceInfo)) {
|
||||
@ -257,9 +257,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
try {
|
||||
GlUtil.destroyEglSurface(eglDisplay, outputEglSurface);
|
||||
} catch (GlUtil.GlException e) {
|
||||
frameProcessorListenerExecutor.execute(
|
||||
() ->
|
||||
frameProcessorListener.onFrameProcessingError(FrameProcessingException.from(e)));
|
||||
videoFrameProcessorListenerExecutor.execute(
|
||||
() -> videoFrameProcessorListener.onError(VideoFrameProcessingException.from(e)));
|
||||
}
|
||||
this.outputEglSurface = null;
|
||||
}
|
||||
@ -277,11 +276,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
TextureInfo inputTexture, long presentationTimeUs, long releaseTimeNs) {
|
||||
try {
|
||||
maybeRenderFrameToOutputSurface(inputTexture, presentationTimeUs, releaseTimeNs);
|
||||
} catch (FrameProcessingException | GlUtil.GlException e) {
|
||||
frameProcessorListenerExecutor.execute(
|
||||
} catch (VideoFrameProcessingException | GlUtil.GlException e) {
|
||||
videoFrameProcessorListenerExecutor.execute(
|
||||
() ->
|
||||
frameProcessorListener.onFrameProcessingError(
|
||||
FrameProcessingException.from(e, presentationTimeUs)));
|
||||
videoFrameProcessorListener.onError(
|
||||
VideoFrameProcessingException.from(e, presentationTimeUs)));
|
||||
}
|
||||
maybeRenderFrameToDebugSurface(inputTexture, presentationTimeUs);
|
||||
inputListener.onInputFrameProcessed(inputTexture);
|
||||
@ -289,8 +288,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
private synchronized void maybeRenderFrameToOutputSurface(
|
||||
TextureInfo inputTexture, long presentationTimeUs, long releaseTimeNs)
|
||||
throws FrameProcessingException, GlUtil.GlException {
|
||||
if (releaseTimeNs == FrameProcessor.DROP_OUTPUT_FRAME
|
||||
throws VideoFrameProcessingException, GlUtil.GlException {
|
||||
if (releaseTimeNs == VideoFrameProcessor.DROP_OUTPUT_FRAME
|
||||
|| !ensureConfigured(inputTexture.width, inputTexture.height)) {
|
||||
return; // Drop frames when requested, or there is no output surface.
|
||||
}
|
||||
@ -311,7 +310,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
EGLExt.eglPresentationTimeANDROID(
|
||||
eglDisplay,
|
||||
outputEglSurface,
|
||||
releaseTimeNs == FrameProcessor.RELEASE_OUTPUT_FRAME_IMMEDIATELY
|
||||
releaseTimeNs == VideoFrameProcessor.RELEASE_OUTPUT_FRAME_IMMEDIATELY
|
||||
? System.nanoTime()
|
||||
: releaseTimeNs);
|
||||
EGL14.eglSwapBuffers(eglDisplay, outputEglSurface);
|
||||
@ -321,7 +320,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
expression = {"outputSurfaceInfo", "outputEglSurface", "matrixShaderProgram"},
|
||||
result = true)
|
||||
private synchronized boolean ensureConfigured(int inputWidth, int inputHeight)
|
||||
throws FrameProcessingException, GlUtil.GlException {
|
||||
throws VideoFrameProcessingException, GlUtil.GlException {
|
||||
|
||||
if (this.inputWidth != inputWidth
|
||||
|| this.inputHeight != inputHeight
|
||||
@ -333,9 +332,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
if (!Util.areEqual(
|
||||
this.outputSizeBeforeSurfaceTransformation, outputSizeBeforeSurfaceTransformation)) {
|
||||
this.outputSizeBeforeSurfaceTransformation = outputSizeBeforeSurfaceTransformation;
|
||||
frameProcessorListenerExecutor.execute(
|
||||
videoFrameProcessorListenerExecutor.execute(
|
||||
() ->
|
||||
frameProcessorListener.onOutputSizeChanged(
|
||||
videoFrameProcessorListener.onOutputSizeChanged(
|
||||
outputSizeBeforeSurfaceTransformation.getWidth(),
|
||||
outputSizeBeforeSurfaceTransformation.getHeight()));
|
||||
}
|
||||
@ -389,7 +388,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
}
|
||||
|
||||
private MatrixShaderProgram createMatrixShaderProgramForOutputSurface(
|
||||
SurfaceInfo outputSurfaceInfo) throws FrameProcessingException {
|
||||
SurfaceInfo outputSurfaceInfo) throws VideoFrameProcessingException {
|
||||
ImmutableList.Builder<GlMatrixTransformation> matrixTransformationListBuilder =
|
||||
new ImmutableList.Builder<GlMatrixTransformation>().addAll(matrixTransformations);
|
||||
if (outputSurfaceInfo.orientationDegrees != 0) {
|
||||
@ -453,7 +452,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
matrixShaderProgram.drawFrame(inputTexture.texId, presentationTimeUs);
|
||||
matrixShaderProgram.setOutputColorTransfer(configuredColorTransfer);
|
||||
});
|
||||
} catch (FrameProcessingException | GlUtil.GlException e) {
|
||||
} catch (VideoFrameProcessingException | GlUtil.GlException e) {
|
||||
Log.d(TAG, "Error rendering to debug preview", e);
|
||||
}
|
||||
}
|
||||
@ -502,8 +501,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
* otherwise.
|
||||
*/
|
||||
@WorkerThread
|
||||
public synchronized void maybeRenderToSurfaceView(FrameProcessingTask renderingTask)
|
||||
throws GlUtil.GlException, FrameProcessingException {
|
||||
public synchronized void maybeRenderToSurfaceView(VideoFrameProcessingTask renderingTask)
|
||||
throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
if (surface == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -19,14 +19,14 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
/**
|
||||
* Caches the input frames.
|
||||
*
|
||||
* <p>Example usage: cache the processed frames when presenting them on screen, to accommodate for
|
||||
* the possible fluctuation in frame processing time between frames.
|
||||
* the possible fluctuation in video frame processing time between frames.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class FrameCache implements GlEffect {
|
||||
@ -51,7 +51,7 @@ public final class FrameCache implements GlEffect {
|
||||
|
||||
@Override
|
||||
public GlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
return new FrameCacheShaderProgram(context, capacity, useHdr);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import static androidx.media3.common.util.Assertions.checkState;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLES20;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlProgram;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import com.google.common.collect.Iterables;
|
||||
@ -54,7 +54,7 @@ import java.util.concurrent.Executor;
|
||||
|
||||
/** Creates a new instance. */
|
||||
public FrameCacheShaderProgram(Context context, int capacity, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
freeOutputTextures = new ArrayDeque<>();
|
||||
inUseOutputTextures = new ArrayDeque<>();
|
||||
try {
|
||||
@ -64,7 +64,7 @@ import java.util.concurrent.Executor;
|
||||
VERTEX_SHADER_TRANSFORMATION_ES2_PATH,
|
||||
FRAGMENT_SHADER_TRANSFORMATION_ES2_PATH);
|
||||
} catch (IOException | GlUtil.GlException e) {
|
||||
throw FrameProcessingException.from(e);
|
||||
throw VideoFrameProcessingException.from(e);
|
||||
}
|
||||
this.capacity = capacity;
|
||||
this.useHdr = useHdr;
|
||||
@ -80,7 +80,7 @@ import java.util.concurrent.Executor;
|
||||
|
||||
inputListener = new InputListener() {};
|
||||
outputListener = new OutputListener() {};
|
||||
errorListener = frameProcessingException -> {};
|
||||
errorListener = videoFrameProcessingException -> {};
|
||||
errorListenerExecutor = MoreExecutors.directExecutor();
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ import java.util.concurrent.Executor;
|
||||
outputListener.onOutputFrameAvailable(outputTexture, presentationTimeUs);
|
||||
} catch (GlUtil.GlException | NoSuchElementException e) {
|
||||
errorListenerExecutor.execute(
|
||||
() -> errorListener.onFrameProcessingError(FrameProcessingException.from(e)));
|
||||
() -> errorListener.onError(VideoFrameProcessingException.from(e)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,11 +167,11 @@ import java.util.concurrent.Executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws FrameProcessingException {
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
try {
|
||||
deleteAllOutputTextures();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ package androidx.media3.effect;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
/**
|
||||
@ -36,10 +36,11 @@ public interface GlEffect extends Effect {
|
||||
* @param context A {@link Context}.
|
||||
* @param useHdr Whether input textures come from an HDR source. If {@code true}, colors will be
|
||||
* in linear RGB BT.2020. If {@code false}, colors will be in linear RGB BT.709.
|
||||
* @throws FrameProcessingException If an error occurs while creating the {@link GlShaderProgram}.
|
||||
* @throws VideoFrameProcessingException If an error occurs while creating the {@link
|
||||
* GlShaderProgram}.
|
||||
*/
|
||||
GlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException;
|
||||
throws VideoFrameProcessingException;
|
||||
|
||||
/**
|
||||
* Returns whether a {@link GlEffect} applies no change at every timestamp.
|
||||
|
@ -17,7 +17,7 @@ package androidx.media3.effect;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.Matrix;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@ -54,7 +54,7 @@ public interface GlMatrixTransformation extends GlEffect {
|
||||
|
||||
@Override
|
||||
default SingleFrameGlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
return MatrixShaderProgram.create(
|
||||
context,
|
||||
/* matrixTransformations= */ ImmutableList.of(this),
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package androidx.media3.effect;
|
||||
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@ -47,7 +47,7 @@ import java.util.concurrent.Executor;
|
||||
public interface GlShaderProgram {
|
||||
|
||||
/**
|
||||
* Listener for input-related frame processing events.
|
||||
* Listener for input-related video frame processing events.
|
||||
*
|
||||
* <p>This listener can be called from any thread.
|
||||
*/
|
||||
@ -81,7 +81,7 @@ public interface GlShaderProgram {
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for output-related frame processing events.
|
||||
* Listener for output-related video frame processing events.
|
||||
*
|
||||
* <p>This listener can be called from any thread.
|
||||
*/
|
||||
@ -108,26 +108,26 @@ public interface GlShaderProgram {
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for frame processing errors.
|
||||
* Listener for video frame processing errors.
|
||||
*
|
||||
* <p>This listener can be called from any thread.
|
||||
*/
|
||||
interface ErrorListener {
|
||||
/**
|
||||
* Called when an exception occurs during asynchronous frame processing.
|
||||
* Called when an exception occurs during asynchronous video frame processing.
|
||||
*
|
||||
* <p>If an error occurred, consuming and producing further frames will not work as expected and
|
||||
* the {@link GlShaderProgram} should be released.
|
||||
*/
|
||||
void onFrameProcessingError(FrameProcessingException e);
|
||||
void onError(VideoFrameProcessingException e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link InputListener}.
|
||||
*
|
||||
* <p>The {@link InputListener} should be invoked on the thread that owns the parent OpenGL
|
||||
* context. For example, {@link GlEffectsFrameProcessor} invokes the {@link InputListener} methods
|
||||
* on its internal thread.
|
||||
* context. For example, {@link DefaultVideoFrameProcessor} invokes the {@link InputListener}
|
||||
* methods on its internal thread.
|
||||
*/
|
||||
void setInputListener(InputListener inputListener);
|
||||
|
||||
@ -135,7 +135,7 @@ public interface GlShaderProgram {
|
||||
* Sets the {@link OutputListener}.
|
||||
*
|
||||
* <p>The {@link OutputListener} should be invoked on the thread that owns the parent OpenGL
|
||||
* context. For example, {@link GlEffectsFrameProcessor} invokes the {@link OutputListener}
|
||||
* context. For example, {@link DefaultVideoFrameProcessor} invokes the {@link OutputListener}
|
||||
* methods on its internal thread.
|
||||
*/
|
||||
void setOutputListener(OutputListener outputListener);
|
||||
@ -190,7 +190,7 @@ public interface GlShaderProgram {
|
||||
/**
|
||||
* Releases all resources.
|
||||
*
|
||||
* @throws FrameProcessingException If an error occurs while releasing resources.
|
||||
* @throws VideoFrameProcessingException If an error occurs while releasing resources.
|
||||
*/
|
||||
void release() throws FrameProcessingException;
|
||||
void release() throws VideoFrameProcessingException;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ package androidx.media3.effect;
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
|
||||
@ -114,7 +114,7 @@ public class HslAdjustment implements GlEffect {
|
||||
|
||||
@Override
|
||||
public SingleFrameGlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
return new HslShaderProgram(context, /* hslAdjustment= */ this, useHdr);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLES20;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlProgram;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
@ -40,10 +40,10 @@ import java.io.IOException;
|
||||
* @param hslAdjustment The {@link HslAdjustment} to apply to each frame in order.
|
||||
* @param useHdr Whether input textures come from an HDR source. If {@code true}, colors will be
|
||||
* in linear RGB BT.2020. If {@code false}, colors will be in linear RGB BT.709.
|
||||
* @throws FrameProcessingException If a problem occurs while reading shader files.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while reading shader files.
|
||||
*/
|
||||
public HslShaderProgram(Context context, HslAdjustment hslAdjustment, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
super(useHdr);
|
||||
// TODO(b/241241680): Check if HDR <-> HSL works the same or not.
|
||||
checkArgument(!useHdr, "HDR is not yet supported.");
|
||||
@ -51,7 +51,7 @@ import java.io.IOException;
|
||||
try {
|
||||
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
|
||||
} catch (IOException | GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
|
||||
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
|
||||
@ -78,7 +78,8 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs) throws FrameProcessingException {
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs)
|
||||
throws VideoFrameProcessingException {
|
||||
try {
|
||||
glProgram.use();
|
||||
glProgram.setSamplerTexIdUniform("uTexSampler", inputTexId, /* texUnitIndex= */ 0);
|
||||
@ -87,7 +88,7 @@ import java.io.IOException;
|
||||
// The four-vertex triangle strip forms a quad.
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e, presentationTimeUs);
|
||||
throw new VideoFrameProcessingException(e, presentationTimeUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,22 +23,23 @@ import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* Forwards a frame produced from a {@link Bitmap} to a {@link GlShaderProgram} for consumption.
|
||||
* Forwards a video frame produced from a {@link Bitmap} to a {@link GlShaderProgram} for
|
||||
* consumption.
|
||||
*
|
||||
* <p>Methods in this class can be called from any thread.
|
||||
*/
|
||||
@UnstableApi
|
||||
/* package */ final class InternalTextureManager implements GlShaderProgram.InputListener {
|
||||
private final GlShaderProgram shaderProgram;
|
||||
private final FrameProcessingTaskExecutor frameProcessingTaskExecutor;
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
|
||||
// The queue holds all bitmaps with one or more frames pending to be sent downstream.
|
||||
private final Queue<BitmapFrameSequenceInfo> pendingBitmaps;
|
||||
|
||||
@ -53,13 +54,14 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
*
|
||||
* @param shaderProgram The {@link GlShaderProgram} for which this {@code InternalTextureManager}
|
||||
* will be set as the {@link GlShaderProgram.InputListener}.
|
||||
* @param frameProcessingTaskExecutor The {@link FrameProcessingTaskExecutor} that the methods of
|
||||
* this class run on.
|
||||
* @param videoFrameProcessingTaskExecutor The {@link VideoFrameProcessingTaskExecutor} that the
|
||||
* methods of this class run on.
|
||||
*/
|
||||
public InternalTextureManager(
|
||||
GlShaderProgram shaderProgram, FrameProcessingTaskExecutor frameProcessingTaskExecutor) {
|
||||
GlShaderProgram shaderProgram,
|
||||
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor) {
|
||||
this.shaderProgram = shaderProgram;
|
||||
this.frameProcessingTaskExecutor = frameProcessingTaskExecutor;
|
||||
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
|
||||
pendingBitmaps = new LinkedBlockingQueue<>();
|
||||
}
|
||||
|
||||
@ -69,7 +71,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
// program and change to only allocate one texId at a time. A change to the
|
||||
// onInputFrameProcessed() method signature to include presentationTimeUs will probably be
|
||||
// needed to do this.
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() -> {
|
||||
downstreamShaderProgramCapacity++;
|
||||
maybeQueueToShaderProgram();
|
||||
@ -79,21 +81,21 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
/**
|
||||
* Provides an input {@link Bitmap} to put into the video frames.
|
||||
*
|
||||
* @see FrameProcessor#queueInputBitmap
|
||||
* @see VideoFrameProcessor#queueInputBitmap
|
||||
*/
|
||||
public void queueInputBitmap(
|
||||
Bitmap inputBitmap, long durationUs, float frameRate, boolean useHdr) {
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() -> setupBitmap(inputBitmap, durationUs, frameRate, useHdr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals the end of the input.
|
||||
*
|
||||
* @see FrameProcessor#signalEndOfInput()
|
||||
* @see VideoFrameProcessor#signalEndOfInput()
|
||||
*/
|
||||
public void signalEndOfInput() {
|
||||
frameProcessingTaskExecutor.submit(
|
||||
videoFrameProcessingTaskExecutor.submit(
|
||||
() -> {
|
||||
inputEnded = true;
|
||||
maybeSignalEndOfOutput();
|
||||
@ -102,7 +104,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
@WorkerThread
|
||||
private void setupBitmap(Bitmap bitmap, long durationUs, float frameRate, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
|
||||
if (inputEnded) {
|
||||
return;
|
||||
@ -116,7 +118,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, /* level= */ 0, bitmap, /* border= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw FrameProcessingException.from(e);
|
||||
throw VideoFrameProcessingException.from(e);
|
||||
}
|
||||
TextureInfo textureInfo =
|
||||
new TextureInfo(
|
||||
|
@ -24,7 +24,7 @@ import android.opengl.Matrix;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlProgram;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
@ -143,15 +143,15 @@ import java.util.List;
|
||||
* @param rgbMatrices The {@link RgbMatrix RgbMatrices} to apply to each frame in order. Can be
|
||||
* empty to apply no color transformations.
|
||||
* @param useHdr Whether input and output colors are HDR.
|
||||
* @throws FrameProcessingException If a problem occurs while reading shader files or an OpenGL
|
||||
* operation fails or is unsupported.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while reading shader files or an
|
||||
* OpenGL operation fails or is unsupported.
|
||||
*/
|
||||
public static MatrixShaderProgram create(
|
||||
Context context,
|
||||
List<GlMatrixTransformation> matrixTransformations,
|
||||
List<RgbMatrix> rgbMatrices,
|
||||
boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
GlProgram glProgram =
|
||||
createGlProgram(
|
||||
context, VERTEX_SHADER_TRANSFORMATION_PATH, FRAGMENT_SHADER_TRANSFORMATION_PATH);
|
||||
@ -185,8 +185,8 @@ import java.util.List;
|
||||
* @param outputColorInfo The output electrical (nonlinear) or optical (linear) {@link ColorInfo}.
|
||||
* If this is an optical color, it must be BT.2020 if {@code inputColorInfo} is {@linkplain
|
||||
* ColorInfo#isTransferHdr(ColorInfo) HDR}, and RGB BT.709 if not.
|
||||
* @throws FrameProcessingException If a problem occurs while reading shader files or an OpenGL
|
||||
* operation fails or is unsupported.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while reading shader files or an
|
||||
* OpenGL operation fails or is unsupported.
|
||||
*/
|
||||
public static MatrixShaderProgram createWithInternalSampler(
|
||||
Context context,
|
||||
@ -194,7 +194,7 @@ import java.util.List;
|
||||
List<RgbMatrix> rgbMatrices,
|
||||
ColorInfo inputColorInfo,
|
||||
ColorInfo outputColorInfo)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
checkState(
|
||||
!ColorInfo.isTransferHdr(inputColorInfo),
|
||||
"MatrixShaderProgram doesn't support HDR internal sampler input yet.");
|
||||
@ -229,8 +229,8 @@ import java.util.List;
|
||||
* @param outputColorInfo The output electrical (nonlinear) or optical (linear) {@link ColorInfo}.
|
||||
* If this is an optical color, it must be BT.2020 if {@code inputColorInfo} is {@linkplain
|
||||
* ColorInfo#isTransferHdr(ColorInfo) HDR}, and RGB BT.709 if not.
|
||||
* @throws FrameProcessingException If a problem occurs while reading shader files or an OpenGL
|
||||
* operation fails or is unsupported.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while reading shader files or an
|
||||
* OpenGL operation fails or is unsupported.
|
||||
*/
|
||||
public static MatrixShaderProgram createWithExternalSampler(
|
||||
Context context,
|
||||
@ -238,7 +238,7 @@ import java.util.List;
|
||||
List<RgbMatrix> rgbMatrices,
|
||||
ColorInfo inputColorInfo,
|
||||
ColorInfo outputColorInfo)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
boolean isInputTransferHdr = ColorInfo.isTransferHdr(inputColorInfo);
|
||||
String vertexShaderFilePath =
|
||||
isInputTransferHdr
|
||||
@ -272,15 +272,15 @@ import java.util.List;
|
||||
* @param rgbMatrices The {@link RgbMatrix RgbMatrices} to apply to each frame in order. Can be
|
||||
* empty to apply no color transformations.
|
||||
* @param outputColorInfo The electrical (non-linear) {@link ColorInfo} describing output colors.
|
||||
* @throws FrameProcessingException If a problem occurs while reading shader files or an OpenGL
|
||||
* operation fails or is unsupported.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while reading shader files or an
|
||||
* OpenGL operation fails or is unsupported.
|
||||
*/
|
||||
public static MatrixShaderProgram createApplyingOetf(
|
||||
Context context,
|
||||
List<GlMatrixTransformation> matrixTransformations,
|
||||
List<RgbMatrix> rgbMatrices,
|
||||
ColorInfo outputColorInfo)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
boolean outputIsHdr = ColorInfo.isTransferHdr(outputColorInfo);
|
||||
String vertexShaderFilePath =
|
||||
outputIsHdr ? VERTEX_SHADER_TRANSFORMATION_ES3_PATH : VERTEX_SHADER_TRANSFORMATION_PATH;
|
||||
@ -317,7 +317,7 @@ import java.util.List;
|
||||
List<RgbMatrix> rgbMatrices,
|
||||
ColorInfo inputColorInfo,
|
||||
ColorInfo outputColorInfo)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
boolean isInputTransferHdr = ColorInfo.isTransferHdr(inputColorInfo);
|
||||
@C.ColorTransfer int outputColorTransfer = outputColorInfo.colorTransfer;
|
||||
if (isInputTransferHdr) {
|
||||
@ -325,7 +325,7 @@ import java.util.List;
|
||||
|
||||
// In HDR editing mode the decoder output is sampled in YUV.
|
||||
if (!GlUtil.isYuvTargetExtensionSupported()) {
|
||||
throw new FrameProcessingException(
|
||||
throw new VideoFrameProcessingException(
|
||||
"The EXT_YUV_target extension is required for HDR editing input.");
|
||||
}
|
||||
glProgram.setFloatsUniform(
|
||||
@ -398,13 +398,13 @@ import java.util.List;
|
||||
|
||||
private static GlProgram createGlProgram(
|
||||
Context context, String vertexShaderFilePath, String fragmentShaderFilePath)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
|
||||
GlProgram glProgram;
|
||||
try {
|
||||
glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath);
|
||||
} catch (IOException | GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
|
||||
float[] identityMatrix = GlUtil.create4x4IdentityMatrix();
|
||||
@ -423,7 +423,8 @@ import java.util.List;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs) throws FrameProcessingException {
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs)
|
||||
throws VideoFrameProcessingException {
|
||||
updateCompositeRgbaMatrixArray(presentationTimeUs);
|
||||
updateCompositeTransformationMatrixAndVisiblePolygon(presentationTimeUs);
|
||||
if (visiblePolygon.size() < 3) {
|
||||
@ -444,17 +445,17 @@ import java.util.List;
|
||||
GLES20.GL_TRIANGLE_FAN, /* first= */ 0, /* count= */ visiblePolygon.size());
|
||||
GlUtil.checkGlError();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e, presentationTimeUs);
|
||||
throw new VideoFrameProcessingException(e, presentationTimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws FrameProcessingException {
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
super.release();
|
||||
try {
|
||||
glProgram.delete();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
package androidx.media3.effect;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@ -40,7 +40,7 @@ public final class OverlayEffect implements GlEffect {
|
||||
|
||||
@Override
|
||||
public SingleFrameGlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
return new OverlayShaderProgram(context, useHdr, overlays);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import android.content.Context;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.Matrix;
|
||||
import android.util.Pair;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlProgram;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
@ -49,11 +49,11 @@ import com.google.common.collect.ImmutableList;
|
||||
* @param context The {@link Context}.
|
||||
* @param useHdr Whether input textures come from an HDR source. If {@code true}, colors will be
|
||||
* in linear RGB BT.2020. If {@code false}, colors will be in linear RGB BT.709.
|
||||
* @throws FrameProcessingException If a problem occurs while reading shader files.
|
||||
* @throws VideoFrameProcessingException If a problem occurs while reading shader files.
|
||||
*/
|
||||
public OverlayShaderProgram(
|
||||
Context context, boolean useHdr, ImmutableList<TextureOverlay> overlays)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
super(useHdr);
|
||||
checkArgument(!useHdr, "OverlayShaderProgram does not support HDR colors yet.");
|
||||
// The maximum number of samplers allowed in a single GL program is 16.
|
||||
@ -70,7 +70,7 @@ import com.google.common.collect.ImmutableList;
|
||||
glProgram =
|
||||
new GlProgram(createVertexShader(overlays.size()), createFragmentShader(overlays.size()));
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
|
||||
glProgram.setBufferAttribute(
|
||||
@ -91,7 +91,8 @@ import com.google.common.collect.ImmutableList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs) throws FrameProcessingException {
|
||||
public void drawFrame(int inputTexId, long presentationTimeUs)
|
||||
throws VideoFrameProcessingException {
|
||||
try {
|
||||
glProgram.use();
|
||||
if (!overlays.isEmpty()) {
|
||||
@ -155,17 +156,17 @@ import com.google.common.collect.ImmutableList;
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||
GlUtil.checkGlError();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e, presentationTimeUs);
|
||||
throw new VideoFrameProcessingException(e, presentationTimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws FrameProcessingException {
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
super.release();
|
||||
try {
|
||||
glProgram.delete();
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ package androidx.media3.effect;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
@ -92,7 +92,7 @@ public class RgbFilter implements RgbMatrix {
|
||||
|
||||
@Override
|
||||
public SingleFrameGlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
checkForConsistentHdrSetting(useHdr);
|
||||
return RgbMatrix.super.toGlShaderProgram(context, useHdr);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
package androidx.media3.effect;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@ -41,7 +41,7 @@ public interface RgbMatrix extends GlEffect {
|
||||
|
||||
@Override
|
||||
default SingleFrameGlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
return MatrixShaderProgram.create(
|
||||
context,
|
||||
/* matrixTransformations= */ ImmutableList.of(),
|
||||
|
@ -24,7 +24,7 @@ import android.graphics.Bitmap;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
@ -150,13 +150,13 @@ public class SingleColorLut implements ColorLut {
|
||||
|
||||
@Override
|
||||
public SingleFrameGlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
checkState(!useHdr, "HDR is currently not supported.");
|
||||
|
||||
try {
|
||||
lutTextureId = storeLutAsTexture(lut);
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException("Could not store the LUT as a texture.", e);
|
||||
throw new VideoFrameProcessingException("Could not store the LUT as a texture.", e);
|
||||
}
|
||||
|
||||
return new ColorLutShaderProgram(context, /* colorLut= */ this, useHdr);
|
||||
|
@ -18,7 +18,7 @@ package androidx.media3.effect;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
@ -61,7 +61,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
||||
this.useHdr = useHdr;
|
||||
inputListener = new InputListener() {};
|
||||
outputListener = new OutputListener() {};
|
||||
errorListener = (frameProcessingException) -> {};
|
||||
errorListener = (videoFrameProcessingException) -> {};
|
||||
errorListenerExecutor = MoreExecutors.directExecutor();
|
||||
}
|
||||
|
||||
@ -74,9 +74,10 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
||||
* @param inputWidth The input width, in pixels.
|
||||
* @param inputHeight The input height, in pixels.
|
||||
* @return The output width and height of frames processed through {@link #drawFrame(int, long)}.
|
||||
* @throws FrameProcessingException If an error occurs while configuring.
|
||||
* @throws VideoFrameProcessingException If an error occurs while configuring.
|
||||
*/
|
||||
public abstract Size configure(int inputWidth, int inputHeight) throws FrameProcessingException;
|
||||
public abstract Size configure(int inputWidth, int inputHeight)
|
||||
throws VideoFrameProcessingException;
|
||||
|
||||
/**
|
||||
* Draws one frame.
|
||||
@ -90,10 +91,10 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
||||
*
|
||||
* @param inputTexId Identifier of a 2D OpenGL texture containing the input frame.
|
||||
* @param presentationTimeUs The presentation timestamp of the current frame, in microseconds.
|
||||
* @throws FrameProcessingException If an error occurs while processing or drawing the frame.
|
||||
* @throws VideoFrameProcessingException If an error occurs while processing or drawing the frame.
|
||||
*/
|
||||
public abstract void drawFrame(int inputTexId, long presentationTimeUs)
|
||||
throws FrameProcessingException;
|
||||
throws VideoFrameProcessingException;
|
||||
|
||||
@Override
|
||||
public final void setInputListener(InputListener inputListener) {
|
||||
@ -134,19 +135,19 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
||||
drawFrame(inputTexture.texId, presentationTimeUs);
|
||||
inputListener.onInputFrameProcessed(inputTexture);
|
||||
outputListener.onOutputFrameAvailable(outputTexture, presentationTimeUs);
|
||||
} catch (FrameProcessingException | GlUtil.GlException | RuntimeException e) {
|
||||
} catch (VideoFrameProcessingException | GlUtil.GlException | RuntimeException e) {
|
||||
errorListenerExecutor.execute(
|
||||
() ->
|
||||
errorListener.onFrameProcessingError(
|
||||
e instanceof FrameProcessingException
|
||||
? (FrameProcessingException) e
|
||||
: new FrameProcessingException(e)));
|
||||
errorListener.onError(
|
||||
e instanceof VideoFrameProcessingException
|
||||
? (VideoFrameProcessingException) e
|
||||
: new VideoFrameProcessingException(e)));
|
||||
}
|
||||
}
|
||||
|
||||
@EnsuresNonNull("outputTexture")
|
||||
private void configureOutputTexture(int inputWidth, int inputHeight)
|
||||
throws GlUtil.GlException, FrameProcessingException {
|
||||
throws GlUtil.GlException, VideoFrameProcessingException {
|
||||
this.inputWidth = inputWidth;
|
||||
this.inputHeight = inputHeight;
|
||||
Size outputSize = configure(inputWidth, inputHeight);
|
||||
@ -184,12 +185,12 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram {
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void release() throws FrameProcessingException {
|
||||
public void release() throws VideoFrameProcessingException {
|
||||
if (outputTexture != null) {
|
||||
try {
|
||||
GlUtil.deleteTexture(outputTexture.texId);
|
||||
} catch (GlUtil.GlException e) {
|
||||
throw new FrameProcessingException(e);
|
||||
throw new VideoFrameProcessingException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package androidx.media3.effect;
|
||||
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
@ -26,9 +26,9 @@ public abstract class TextureOverlay {
|
||||
* Returns the overlay texture identifier displayed at the specified timestamp.
|
||||
*
|
||||
* @param presentationTimeUs The presentation timestamp of the current frame, in microseconds.
|
||||
* @throws FrameProcessingException If an error occurs while processing or drawing the frame.
|
||||
* @throws VideoFrameProcessingException If an error occurs while processing or drawing the frame.
|
||||
*/
|
||||
public abstract int getTextureId(long presentationTimeUs) throws FrameProcessingException;
|
||||
public abstract int getTextureId(long presentationTimeUs) throws VideoFrameProcessingException;
|
||||
|
||||
// This method is required to find the size of a texture given a texture identifier using OpenGL
|
||||
// ES 2.0. OpenGL ES 3.1 can do this with glGetTexLevelParameteriv().
|
||||
|
@ -15,14 +15,14 @@
|
||||
*/
|
||||
package androidx.media3.effect;
|
||||
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
|
||||
/**
|
||||
* Interface for tasks that may throw a {@link GlUtil.GlException} or {@link
|
||||
* FrameProcessingException}.
|
||||
* VideoFrameProcessingException}.
|
||||
*/
|
||||
/* package */ interface FrameProcessingTask {
|
||||
/* package */ interface VideoFrameProcessingTask {
|
||||
/** Runs the task. */
|
||||
void run() throws FrameProcessingException, GlUtil.GlException;
|
||||
void run() throws VideoFrameProcessingException, GlUtil.GlException;
|
||||
}
|
@ -19,8 +19,8 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@ -29,36 +29,36 @@ import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Wrapper around a single thread {@link ExecutorService} for executing {@link FrameProcessingTask}
|
||||
* instances.
|
||||
* Wrapper around a single thread {@link ExecutorService} for executing {@link
|
||||
* VideoFrameProcessingTask} instances.
|
||||
*
|
||||
* <p>Public methods can be called from any thread.
|
||||
*
|
||||
* <p>The wrapper handles calling {@link
|
||||
* FrameProcessor.Listener#onFrameProcessingError(FrameProcessingException)} for errors that occur
|
||||
* during these tasks. The listener is invoked from the {@link ExecutorService}. Errors are assumed
|
||||
* to be non-recoverable, so the {@code FrameProcessingTaskExecutor} should be released if an error
|
||||
* VideoFrameProcessor.Listener#onError(VideoFrameProcessingException)} for errors that occur during
|
||||
* these tasks. The listener is invoked from the {@link ExecutorService}. Errors are assumed to be
|
||||
* non-recoverable, so the {@code VideoFrameProcessingTaskExecutor} should be released if an error
|
||||
* occurs.
|
||||
*
|
||||
* <p>{@linkplain #submitWithHighPriority(FrameProcessingTask) High priority tasks} are always
|
||||
* executed before {@linkplain #submit(FrameProcessingTask) default priority tasks}. Tasks with
|
||||
* <p>{@linkplain #submitWithHighPriority(VideoFrameProcessingTask) High priority tasks} are always
|
||||
* executed before {@linkplain #submit(VideoFrameProcessingTask) default priority tasks}. Tasks with
|
||||
* equal priority are executed in FIFO order.
|
||||
*/
|
||||
/* package */ final class FrameProcessingTaskExecutor {
|
||||
/* package */ final class VideoFrameProcessingTaskExecutor {
|
||||
|
||||
private final ExecutorService singleThreadExecutorService;
|
||||
private final FrameProcessor.Listener listener;
|
||||
private final VideoFrameProcessor.Listener listener;
|
||||
private final Object lock;
|
||||
|
||||
@GuardedBy("lock")
|
||||
private final ArrayDeque<FrameProcessingTask> highPriorityTasks;
|
||||
private final ArrayDeque<VideoFrameProcessingTask> highPriorityTasks;
|
||||
|
||||
@GuardedBy("lock")
|
||||
private boolean shouldCancelTasks;
|
||||
|
||||
/** Creates a new instance. */
|
||||
public FrameProcessingTaskExecutor(
|
||||
ExecutorService singleThreadExecutorService, FrameProcessor.Listener listener) {
|
||||
public VideoFrameProcessingTaskExecutor(
|
||||
ExecutorService singleThreadExecutorService, VideoFrameProcessor.Listener listener) {
|
||||
this.singleThreadExecutorService = singleThreadExecutorService;
|
||||
this.listener = listener;
|
||||
lock = new Object();
|
||||
@ -66,11 +66,11 @@ import java.util.concurrent.RejectedExecutionException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the given {@link FrameProcessingTask} to be executed after all pending tasks have
|
||||
* Submits the given {@link VideoFrameProcessingTask} to be executed after all pending tasks have
|
||||
* completed.
|
||||
*/
|
||||
@SuppressWarnings("FutureReturnValueIgnored")
|
||||
public void submit(FrameProcessingTask task) {
|
||||
public void submit(VideoFrameProcessingTask task) {
|
||||
@Nullable RejectedExecutionException executionException = null;
|
||||
synchronized (lock) {
|
||||
if (shouldCancelTasks) {
|
||||
@ -89,13 +89,13 @@ import java.util.concurrent.RejectedExecutionException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the given {@link FrameProcessingTask} to be executed after the currently running task
|
||||
* and all previously submitted high-priority tasks have completed.
|
||||
* Submits the given {@link VideoFrameProcessingTask} to be executed after the currently running
|
||||
* task and all previously submitted high-priority tasks have completed.
|
||||
*
|
||||
* <p>Tasks that were previously {@linkplain #submit(FrameProcessingTask) submitted} without
|
||||
* <p>Tasks that were previously {@linkplain #submit(VideoFrameProcessingTask) submitted} without
|
||||
* high-priority and have not started executing will be executed after this task is complete.
|
||||
*/
|
||||
public void submitWithHighPriority(FrameProcessingTask task) {
|
||||
public void submitWithHighPriority(VideoFrameProcessingTask task) {
|
||||
synchronized (lock) {
|
||||
if (shouldCancelTasks) {
|
||||
return;
|
||||
@ -111,7 +111,7 @@ import java.util.concurrent.RejectedExecutionException;
|
||||
/**
|
||||
* Flushes all scheduled tasks.
|
||||
*
|
||||
* <p>During flush, the {@code FrameProcessingTaskExecutor} ignores the {@linkplain #submit
|
||||
* <p>During flush, the {@code VideoFrameProcessingTaskExecutor} ignores the {@linkplain #submit
|
||||
* submission of new tasks}. The tasks that are submitted before flushing are either executed or
|
||||
* canceled when this method returns.
|
||||
*/
|
||||
@ -137,12 +137,12 @@ import java.util.concurrent.RejectedExecutionException;
|
||||
/**
|
||||
* Cancels remaining tasks, runs the given release task, and shuts down the background thread.
|
||||
*
|
||||
* @param releaseTask A {@link FrameProcessingTask} to execute before shutting down the background
|
||||
* thread.
|
||||
* @param releaseTask A {@link VideoFrameProcessingTask} to execute before shutting down the
|
||||
* background thread.
|
||||
* @param releaseWaitTimeMs How long to wait for the release task to terminate, in milliseconds.
|
||||
* @throws InterruptedException If interrupted while releasing resources.
|
||||
*/
|
||||
public void release(FrameProcessingTask releaseTask, long releaseWaitTimeMs)
|
||||
public void release(VideoFrameProcessingTask releaseTask, long releaseWaitTimeMs)
|
||||
throws InterruptedException {
|
||||
synchronized (lock) {
|
||||
shouldCancelTasks = true;
|
||||
@ -153,16 +153,16 @@ import java.util.concurrent.RejectedExecutionException;
|
||||
singleThreadExecutorService.shutdown();
|
||||
try {
|
||||
if (!singleThreadExecutorService.awaitTermination(releaseWaitTimeMs, MILLISECONDS)) {
|
||||
listener.onFrameProcessingError(new FrameProcessingException("Release timed out"));
|
||||
listener.onError(new VideoFrameProcessingException("Release timed out"));
|
||||
}
|
||||
releaseFuture.get();
|
||||
} catch (ExecutionException e) {
|
||||
listener.onFrameProcessingError(new FrameProcessingException(e));
|
||||
listener.onError(new VideoFrameProcessingException(e));
|
||||
}
|
||||
}
|
||||
|
||||
private Future<?> wrapTaskAndSubmitToExecutorService(
|
||||
FrameProcessingTask defaultPriorityTask, boolean isFlushOrReleaseTask) {
|
||||
VideoFrameProcessingTask defaultPriorityTask, boolean isFlushOrReleaseTask) {
|
||||
return singleThreadExecutorService.submit(
|
||||
() -> {
|
||||
try {
|
||||
@ -172,7 +172,7 @@ import java.util.concurrent.RejectedExecutionException;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable FrameProcessingTask nextHighPriorityTask;
|
||||
@Nullable VideoFrameProcessingTask nextHighPriorityTask;
|
||||
while (true) {
|
||||
synchronized (lock) {
|
||||
// Lock only polling to prevent blocking the public method calls.
|
||||
@ -199,6 +199,6 @@ import java.util.concurrent.RejectedExecutionException;
|
||||
}
|
||||
shouldCancelTasks = true;
|
||||
}
|
||||
listener.onFrameProcessingError(FrameProcessingException.from(exception));
|
||||
listener.onError(VideoFrameProcessingException.from(exception));
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ package androidx.media3.effect;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.After;
|
||||
@ -30,20 +30,22 @@ import org.junit.runner.RunWith;
|
||||
public final class ChainingGlShaderProgramListenerTest {
|
||||
private static final long EXECUTOR_WAIT_TIME_MS = 100;
|
||||
|
||||
private final FrameProcessor.Listener mockFrameProcessorListener =
|
||||
mock(FrameProcessor.Listener.class);
|
||||
private final FrameProcessingTaskExecutor frameProcessingTaskExecutor =
|
||||
new FrameProcessingTaskExecutor(
|
||||
private final VideoFrameProcessor.Listener mockFrameProcessorListener =
|
||||
mock(VideoFrameProcessor.Listener.class);
|
||||
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor =
|
||||
new VideoFrameProcessingTaskExecutor(
|
||||
Util.newSingleThreadExecutor("Test"), mockFrameProcessorListener);
|
||||
private final GlShaderProgram mockProducingGlShaderProgram = mock(GlShaderProgram.class);
|
||||
private final GlShaderProgram mockConsumingGlShaderProgram = mock(GlShaderProgram.class);
|
||||
private final ChainingGlShaderProgramListener chainingGlShaderProgramListener =
|
||||
new ChainingGlShaderProgramListener(
|
||||
mockProducingGlShaderProgram, mockConsumingGlShaderProgram, frameProcessingTaskExecutor);
|
||||
mockProducingGlShaderProgram,
|
||||
mockConsumingGlShaderProgram,
|
||||
videoFrameProcessingTaskExecutor);
|
||||
|
||||
@After
|
||||
public void release() throws InterruptedException {
|
||||
frameProcessingTaskExecutor.release(/* releaseTask= */ () -> {}, EXECUTOR_WAIT_TIME_MS);
|
||||
videoFrameProcessingTaskExecutor.release(/* releaseTask= */ () -> {}, EXECUTOR_WAIT_TIME_MS);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -53,11 +53,11 @@ import androidx.media3.common.DrmInitData;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.PlaybackException;
|
||||
import androidx.media3.common.SurfaceInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.VideoSize;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.MediaFormatUtil;
|
||||
@ -148,7 +148,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
private final Context context;
|
||||
private final VideoFrameReleaseHelper frameReleaseHelper;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final FrameProcessorManager frameProcessorManager;
|
||||
private final VideoFrameProcessorManager videoFrameProcessorManager;
|
||||
private final long allowedJoiningTimeMs;
|
||||
private final int maxDroppedFramesToNotify;
|
||||
private final boolean deviceNeedsNoPostProcessWorkaround;
|
||||
@ -352,7 +352,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
this.context = context.getApplicationContext();
|
||||
frameReleaseHelper = new VideoFrameReleaseHelper(this.context);
|
||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
frameProcessorManager = new FrameProcessorManager(frameReleaseHelper, /* renderer= */ this);
|
||||
videoFrameProcessorManager =
|
||||
new VideoFrameProcessorManager(frameReleaseHelper, /* renderer= */ this);
|
||||
deviceNeedsNoPostProcessWorkaround = deviceNeedsNoPostProcessWorkaround();
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
scalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
|
||||
@ -563,8 +564,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
@Override
|
||||
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
|
||||
super.onPositionReset(positionUs, joining);
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
frameProcessorManager.flush();
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
videoFrameProcessorManager.flush();
|
||||
}
|
||||
clearRenderedFirstFrame();
|
||||
frameReleaseHelper.onPositionReset();
|
||||
@ -581,8 +582,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
@Override
|
||||
public boolean isEnded() {
|
||||
boolean isEnded = super.isEnded();
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
isEnded &= frameProcessorManager.releasedLastFrame();
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
isEnded &= videoFrameProcessorManager.releasedLastFrame();
|
||||
}
|
||||
return isEnded;
|
||||
}
|
||||
@ -590,7 +591,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
if (super.isReady()
|
||||
&& (!frameProcessorManager.isEnabled() || frameProcessorManager.isReady())
|
||||
&& (!videoFrameProcessorManager.isEnabled() || videoFrameProcessorManager.isReady())
|
||||
&& (renderedFirstFrameAfterReset
|
||||
|| (placeholderSurface != null && displaySurface == placeholderSurface)
|
||||
|| getCodec() == null
|
||||
@ -650,8 +651,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
try {
|
||||
super.onReset();
|
||||
} finally {
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
frameProcessorManager.reset();
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
videoFrameProcessorManager.reset();
|
||||
}
|
||||
if (placeholderSurface != null) {
|
||||
releasePlaceholderSurface();
|
||||
@ -691,14 +692,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
case MSG_SET_VIDEO_EFFECTS:
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Effect> videoEffects = (List<Effect>) checkNotNull(message);
|
||||
frameProcessorManager.setVideoEffects(videoEffects);
|
||||
videoFrameProcessorManager.setVideoEffects(videoEffects);
|
||||
break;
|
||||
case MSG_SET_VIDEO_OUTPUT_RESOLUTION:
|
||||
Size outputResolution = (Size) checkNotNull(message);
|
||||
if (outputResolution.getWidth() != 0
|
||||
&& outputResolution.getHeight() != 0
|
||||
&& displaySurface != null) {
|
||||
frameProcessorManager.setOutputSurfaceInfo(displaySurface, outputResolution);
|
||||
videoFrameProcessorManager.setOutputSurfaceInfo(displaySurface, outputResolution);
|
||||
}
|
||||
break;
|
||||
case MSG_SET_AUDIO_ATTRIBUTES:
|
||||
@ -737,7 +738,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
|
||||
@State int state = getState();
|
||||
@Nullable MediaCodecAdapter codec = getCodec();
|
||||
if (codec != null && !frameProcessorManager.isEnabled()) {
|
||||
if (codec != null && !videoFrameProcessorManager.isEnabled()) {
|
||||
if (Util.SDK_INT >= 23 && displaySurface != null && !codecNeedsSetOutputSurfaceWorkaround) {
|
||||
setOutputSurfaceV23(codec, displaySurface);
|
||||
} else {
|
||||
@ -754,17 +755,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
// Set joining deadline to report MediaCodecVideoRenderer is ready.
|
||||
setJoiningDeadlineMs();
|
||||
}
|
||||
// When FrameProcessorManager is enabled, set FrameProcessorManager's display surface and an
|
||||
// unknown size.
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
frameProcessorManager.setOutputSurfaceInfo(displaySurface, Size.UNKNOWN);
|
||||
// When VideoFrameProcessorManager is enabled, set VideoFrameProcessorManager's display
|
||||
// surface and an unknown size.
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
videoFrameProcessorManager.setOutputSurfaceInfo(displaySurface, Size.UNKNOWN);
|
||||
}
|
||||
} else {
|
||||
// The display surface has been removed.
|
||||
clearReportedVideoSize();
|
||||
clearRenderedFirstFrame();
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
frameProcessorManager.clearOutputSurfaceInfo();
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
videoFrameProcessorManager.clearOutputSurfaceInfo();
|
||||
}
|
||||
}
|
||||
} else if (displaySurface != null && displaySurface != placeholderSurface) {
|
||||
@ -817,16 +818,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
displaySurface = placeholderSurface;
|
||||
}
|
||||
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
mediaFormat = frameProcessorManager.amendMediaFormatKeys(mediaFormat);
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
mediaFormat = videoFrameProcessorManager.amendMediaFormatKeys(mediaFormat);
|
||||
}
|
||||
|
||||
return MediaCodecAdapter.Configuration.createForVideoDecoding(
|
||||
codecInfo,
|
||||
mediaFormat,
|
||||
format,
|
||||
frameProcessorManager.isEnabled()
|
||||
? frameProcessorManager.getInputSurface()
|
||||
videoFrameProcessorManager.isEnabled()
|
||||
? videoFrameProcessorManager.getInputSurface()
|
||||
: displaySurface,
|
||||
crypto);
|
||||
}
|
||||
@ -856,8 +857,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
@Override
|
||||
public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
super.render(positionUs, elapsedRealtimeUs);
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
frameProcessorManager.releaseProcessedFrames(positionUs, elapsedRealtimeUs);
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
videoFrameProcessorManager.releaseProcessedFrames(positionUs, elapsedRealtimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -964,8 +965,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
@CallSuper
|
||||
@Override
|
||||
protected void onReadyToInitializeCodec(Format format) throws ExoPlaybackException {
|
||||
if (!frameProcessorManager.isEnabled()) {
|
||||
frameProcessorManager.maybeEnable(format);
|
||||
if (!videoFrameProcessorManager.isEnabled()) {
|
||||
videoFrameProcessorManager.maybeEnable(format);
|
||||
}
|
||||
}
|
||||
|
||||
@ -982,7 +983,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
if (Util.SDK_INT >= 23 && tunneling) {
|
||||
tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(checkNotNull(getCodec()));
|
||||
}
|
||||
frameProcessorManager.onCodecInitialized(name);
|
||||
videoFrameProcessorManager.onCodecInitialized(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1070,16 +1071,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
height = rotatedHeight;
|
||||
pixelWidthHeightRatio = 1 / pixelWidthHeightRatio;
|
||||
}
|
||||
} else if (!frameProcessorManager.isEnabled()) {
|
||||
// Neither the codec nor the FrameProcessor applies the rotation.
|
||||
} else if (!videoFrameProcessorManager.isEnabled()) {
|
||||
// Neither the codec nor the VideoFrameProcessor applies the rotation.
|
||||
unappliedRotationDegrees = format.rotationDegrees;
|
||||
}
|
||||
decodedVideoSize =
|
||||
new VideoSize(width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
|
||||
frameReleaseHelper.onFormatChanged(format.frameRate);
|
||||
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
frameProcessorManager.setInputFormat(
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
videoFrameProcessorManager.setInputFormat(
|
||||
format
|
||||
.buildUpon()
|
||||
.setWidth(width)
|
||||
@ -1142,7 +1143,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
|
||||
if (bufferPresentationTimeUs != lastBufferPresentationTimeUs) {
|
||||
if (!frameProcessorManager.isEnabled()) {
|
||||
if (!videoFrameProcessorManager.isEnabled()) {
|
||||
frameReleaseHelper.onNextFrame(bufferPresentationTimeUs);
|
||||
} // else, update the frameReleaseHelper when releasing the processed frames.
|
||||
this.lastBufferPresentationTimeUs = bufferPresentationTimeUs;
|
||||
@ -1180,9 +1181,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
boolean forceRenderOutputBuffer = shouldForceRender(positionUs, earlyUs);
|
||||
if (forceRenderOutputBuffer) {
|
||||
boolean notifyFrameMetaDataListener;
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
notifyFrameMetaDataListener = false;
|
||||
if (!frameProcessorManager.maybeRegisterFrame(format, presentationTimeUs, isLastBuffer)) {
|
||||
if (!videoFrameProcessorManager.maybeRegisterFrame(
|
||||
format, presentationTimeUs, isLastBuffer)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@ -1204,7 +1206,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
|
||||
// Apply a timestamp adjustment, if there is one.
|
||||
long adjustedReleaseTimeNs = frameReleaseHelper.adjustReleaseTime(unadjustedFrameReleaseTimeNs);
|
||||
if (!frameProcessorManager.isEnabled()) {
|
||||
if (!videoFrameProcessorManager.isEnabled()) {
|
||||
earlyUs = (adjustedReleaseTimeNs - systemTimeNs) / 1000;
|
||||
} // else, use the unadjusted earlyUs in previewing use cases.
|
||||
|
||||
@ -1222,9 +1224,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
frameProcessorManager.releaseProcessedFrames(positionUs, elapsedRealtimeUs);
|
||||
if (frameProcessorManager.maybeRegisterFrame(format, presentationTimeUs, isLastBuffer)) {
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
videoFrameProcessorManager.releaseProcessedFrames(positionUs, elapsedRealtimeUs);
|
||||
if (videoFrameProcessorManager.maybeRegisterFrame(format, presentationTimeUs, isLastBuffer)) {
|
||||
renderOutputBufferNow(
|
||||
codec,
|
||||
format,
|
||||
@ -1457,8 +1459,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
droppedSourceBufferCount, /* droppedDecoderBufferCount= */ buffersInCodecCount);
|
||||
}
|
||||
flushOrReinitializeCodec();
|
||||
if (frameProcessorManager.isEnabled()) {
|
||||
frameProcessorManager.flush();
|
||||
if (videoFrameProcessorManager.isEnabled()) {
|
||||
videoFrameProcessorManager.flush();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1513,11 +1515,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
boolean notifyFrameMetadataListener) {
|
||||
// In previewing mode, use the presentation time as release time so that the SurfaceTexture is
|
||||
// accompanied by the rendered frame's presentation time. Setting a realtime based release time
|
||||
// is only relevant when rendering to a SurfaceView (that is when not using FrameProcessor) for
|
||||
// better frame release. In previewing mode MediaCodec renders to FrameProcessor's input
|
||||
// surface, which is not a SurfaceView.
|
||||
// is only relevant when rendering to a SurfaceView (that is when not using VideoFrameProcessor)
|
||||
// for better frame release. In previewing mode MediaCodec renders to VideoFrameProcessor's
|
||||
// input surface, which is not a SurfaceView.
|
||||
long releaseTimeNs =
|
||||
frameProcessorManager.isEnabled()
|
||||
videoFrameProcessorManager.isEnabled()
|
||||
? (presentationTimeUs + getOutputStreamOffsetUs()) * 1000
|
||||
: System.nanoTime();
|
||||
if (notifyFrameMetadataListener) {
|
||||
@ -1534,9 +1536,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
* Renders the output buffer with the specified index. This method is only called if the platform
|
||||
* API version of the device is less than 21.
|
||||
*
|
||||
* <p>When frame processing is {@linkplain FrameProcessorManager#isEnabled()} enabled}, this
|
||||
* method renders to {@link FrameProcessorManager}'s {@linkplain
|
||||
* FrameProcessorManager#getInputSurface() input surface}.
|
||||
* <p>When video frame processing is {@linkplain VideoFrameProcessorManager#isEnabled()} enabled},
|
||||
* this method renders to {@link VideoFrameProcessorManager}'s {@linkplain
|
||||
* VideoFrameProcessorManager#getInputSurface() input surface}.
|
||||
*
|
||||
* @param codec The codec that owns the output buffer.
|
||||
* @param index The index of the output buffer to drop.
|
||||
@ -1548,7 +1550,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
TraceUtil.endSection();
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
if (!frameProcessorManager.isEnabled()) {
|
||||
if (!videoFrameProcessorManager.isEnabled()) {
|
||||
lastRenderRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||
maybeNotifyVideoSizeChanged(decodedVideoSize);
|
||||
maybeNotifyRenderedFirstFrame();
|
||||
@ -1559,9 +1561,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
* Renders the output buffer with the specified index. This method is only called if the platform
|
||||
* API version of the device is 21 or later.
|
||||
*
|
||||
* <p>When frame processing is {@linkplain FrameProcessorManager#isEnabled()} enabled}, this
|
||||
* method renders to {@link FrameProcessorManager}'s {@linkplain
|
||||
* FrameProcessorManager#getInputSurface() input surface}.
|
||||
* <p>When video frame processing is {@linkplain VideoFrameProcessorManager#isEnabled()} enabled},
|
||||
* this method renders to {@link VideoFrameProcessorManager}'s {@linkplain
|
||||
* VideoFrameProcessorManager#getInputSurface() input surface}.
|
||||
*
|
||||
* @param codec The codec that owns the output buffer.
|
||||
* @param index The index of the output buffer to drop.
|
||||
@ -1576,7 +1578,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
TraceUtil.endSection();
|
||||
decoderCounters.renderedOutputBufferCount++;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
if (!frameProcessorManager.isEnabled()) {
|
||||
if (!videoFrameProcessorManager.isEnabled()) {
|
||||
lastRenderRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||
maybeNotifyVideoSizeChanged(decodedVideoSize);
|
||||
maybeNotifyRenderedFirstFrame();
|
||||
@ -1834,8 +1836,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
return new MediaCodecVideoDecoderException(cause, codecInfo, displaySurface);
|
||||
}
|
||||
|
||||
/** Manages {@link FrameProcessor} interactions. */
|
||||
private static final class FrameProcessorManager {
|
||||
/** Manages {@link VideoFrameProcessor} interactions. */
|
||||
private static final class VideoFrameProcessorManager {
|
||||
|
||||
/** The threshold for releasing a processed frame. */
|
||||
private static final long EARLY_THRESHOLD_US = 50_000;
|
||||
@ -1848,7 +1850,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
private final ArrayDeque<Pair<Long, Format>> pendingFrameFormats;
|
||||
|
||||
private @MonotonicNonNull Handler handler;
|
||||
@Nullable private FrameProcessor frameProcessor;
|
||||
@Nullable private VideoFrameProcessor videoFrameProcessor;
|
||||
@Nullable private CopyOnWriteArrayList<Effect> videoEffects;
|
||||
@Nullable private Format inputFormat;
|
||||
/**
|
||||
@ -1858,16 +1860,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
|
||||
@Nullable private Pair<Surface, Size> currentSurfaceAndSize;
|
||||
|
||||
private int frameProcessorMaxPendingFrameCount;
|
||||
private int videoFrameProcessorMaxPendingFrameCount;
|
||||
private boolean canEnableFrameProcessing;
|
||||
|
||||
/**
|
||||
* Whether the last frame of the current stream is decoded and registered to {@link
|
||||
* FrameProcessor}.
|
||||
* VideoFrameProcessor}.
|
||||
*/
|
||||
private boolean registeredLastFrame;
|
||||
|
||||
/** Whether the last frame of the current stream is processed by the {@link FrameProcessor}. */
|
||||
/**
|
||||
* Whether the last frame of the current stream is processed by the {@link VideoFrameProcessor}.
|
||||
*/
|
||||
private boolean processedLastFrame;
|
||||
|
||||
/** Whether the last frame of the current stream is released to the output {@link Surface}. */
|
||||
@ -1880,14 +1884,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
private long pendingOutputSizeChangeNotificationTimeUs;
|
||||
|
||||
/** Creates a new instance. */
|
||||
public FrameProcessorManager(
|
||||
public VideoFrameProcessorManager(
|
||||
VideoFrameReleaseHelper frameReleaseHelper,
|
||||
@UnderInitialization MediaCodecVideoRenderer renderer) {
|
||||
this.frameReleaseHelper = frameReleaseHelper;
|
||||
this.renderer = renderer;
|
||||
processedFramesTimestampsUs = new ArrayDeque<>();
|
||||
pendingFrameFormats = new ArrayDeque<>();
|
||||
frameProcessorMaxPendingFrameCount = C.LENGTH_UNSET;
|
||||
videoFrameProcessorMaxPendingFrameCount = C.LENGTH_UNSET;
|
||||
canEnableFrameProcessing = true;
|
||||
lastCodecBufferPresentationTimestampUs = C.TIME_UNSET;
|
||||
processedFrameSize = VideoSize.UNKNOWN;
|
||||
@ -1904,30 +1908,32 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
this.videoEffects.addAll(videoEffects);
|
||||
}
|
||||
|
||||
/** Returns whether frame processing is enabled. */
|
||||
/** Returns whether video frame processing is enabled. */
|
||||
public boolean isEnabled() {
|
||||
return frameProcessor != null;
|
||||
return videoFrameProcessor != null;
|
||||
}
|
||||
|
||||
/** Returns whether {@code FrameProcessorManager} is ready to accept input frames. */
|
||||
/** Returns whether {@code VideoFrameProcessorManager} is ready to accept input frames. */
|
||||
public boolean isReady() {
|
||||
return currentSurfaceAndSize == null || !currentSurfaceAndSize.second.equals(Size.UNKNOWN);
|
||||
}
|
||||
|
||||
/** Whether the {@link FrameProcessor} has released the last frame in the current stream. */
|
||||
/**
|
||||
* Whether the {@link VideoFrameProcessor} has released the last frame in the current stream.
|
||||
*/
|
||||
public boolean releasedLastFrame() {
|
||||
return releasedLastFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the {@link FrameProcessor}.
|
||||
* Flushes the {@link VideoFrameProcessor}.
|
||||
*
|
||||
* <p>Caller must ensure frame processing {@linkplain #isEnabled() is enabled} before calling
|
||||
* this method.
|
||||
* <p>Caller must ensure video frame processing {@linkplain #isEnabled() is enabled} before
|
||||
* calling this method.
|
||||
*/
|
||||
public void flush() {
|
||||
checkStateNotNull(frameProcessor);
|
||||
frameProcessor.flush();
|
||||
checkStateNotNull(videoFrameProcessor);
|
||||
videoFrameProcessor.flush();
|
||||
processedFramesTimestampsUs.clear();
|
||||
handler.removeCallbacksAndMessages(/* token= */ null);
|
||||
|
||||
@ -1939,14 +1945,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to enable frame processing.
|
||||
* Tries to enable video frame processing.
|
||||
*
|
||||
* <p>Caller must ensure frame processing {@linkplain #isEnabled() is not enabled} before
|
||||
* <p>Caller must ensure video frame processing {@linkplain #isEnabled() is not enabled} before
|
||||
* calling this method.
|
||||
*
|
||||
* @param inputFormat The {@link Format} that is input into the {@link FrameProcessor}.
|
||||
* @return Whether frame processing is enabled.
|
||||
* @throws ExoPlaybackException When enabling the {@link FrameProcessor} failed.
|
||||
* @param inputFormat The {@link Format} that is input into the {@link VideoFrameProcessor}.
|
||||
* @return Whether video frame processing is enabled.
|
||||
* @throws ExoPlaybackException When enabling the {@link VideoFrameProcessor} failed.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public boolean maybeEnable(Format inputFormat) throws ExoPlaybackException {
|
||||
@ -1981,11 +1987,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
// Insert as the first effect as if the decoder has applied the rotation.
|
||||
videoEffects.add(
|
||||
/* index= */ 0,
|
||||
FrameProcessorAccessor.createRotationEffect(inputFormat.rotationDegrees));
|
||||
VideoFrameProcessorAccessor.createRotationEffect(inputFormat.rotationDegrees));
|
||||
}
|
||||
|
||||
frameProcessor =
|
||||
FrameProcessorAccessor.getFrameProcessorFactory()
|
||||
videoFrameProcessor =
|
||||
VideoFrameProcessorAccessor.getFrameProcessorFactory()
|
||||
.create(
|
||||
renderer.context,
|
||||
checkNotNull(videoEffects),
|
||||
@ -1995,19 +2001,20 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
/* isInputTextureExternal= */ true,
|
||||
/* releaseFramesAutomatically= */ false,
|
||||
/* executor= */ handler::post,
|
||||
new FrameProcessor.Listener() {
|
||||
new VideoFrameProcessor.Listener() {
|
||||
@Override
|
||||
public void onOutputSizeChanged(int width, int height) {
|
||||
@Nullable Format inputFormat = FrameProcessorManager.this.inputFormat;
|
||||
@Nullable Format inputFormat = VideoFrameProcessorManager.this.inputFormat;
|
||||
checkStateNotNull(inputFormat);
|
||||
// TODO(b/264889146): Handle Effect that changes output size based on pts.
|
||||
processedFrameSize =
|
||||
new VideoSize(
|
||||
width,
|
||||
height,
|
||||
// FrameProcessor is configured to produce rotation free frames.
|
||||
// VideoFrameProcessor is configured to produce rotation free
|
||||
// frames.
|
||||
/* unappliedRotationDegrees= */ 0,
|
||||
// FrameProcessor always outputs pixelWidthHeightRatio 1.
|
||||
// VideoFrameProcessor always outputs pixelWidthHeightRatio 1.
|
||||
/* pixelWidthHeightRatio= */ 1.f);
|
||||
pendingOutputSizeChange = true;
|
||||
}
|
||||
@ -2031,27 +2038,27 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameProcessingError(FrameProcessingException exception) {
|
||||
public void onError(VideoFrameProcessingException exception) {
|
||||
renderer.setPendingPlaybackException(
|
||||
renderer.createRendererException(
|
||||
exception,
|
||||
inputFormat,
|
||||
PlaybackException.ERROR_CODE_FRAME_PROCESSING_FAILED));
|
||||
PlaybackException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameProcessingEnded() {
|
||||
public void onEnded() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw renderer.createRendererException(
|
||||
e, inputFormat, PlaybackException.ERROR_CODE_FRAME_PROCESSOR_INIT_FAILED);
|
||||
e, inputFormat, PlaybackException.ERROR_CODE_VIDEO_FRAME_PROCESSOR_INIT_FAILED);
|
||||
}
|
||||
|
||||
if (currentSurfaceAndSize != null) {
|
||||
Size outputSurfaceSize = currentSurfaceAndSize.second;
|
||||
frameProcessor.setOutputSurfaceInfo(
|
||||
videoFrameProcessor.setOutputSurfaceInfo(
|
||||
new SurfaceInfo(
|
||||
currentSurfaceAndSize.first,
|
||||
outputSurfaceSize.getWidth(),
|
||||
@ -2063,20 +2070,20 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@linkplain FrameProcessor#getInputSurface input surface} of the {@link
|
||||
* FrameProcessor}.
|
||||
* Returns the {@linkplain VideoFrameProcessor#getInputSurface input surface} of the {@link
|
||||
* VideoFrameProcessor}.
|
||||
*
|
||||
* <p>Caller must ensure the {@code FrameProcessorManager} {@link #isEnabled()} before calling
|
||||
* this method.
|
||||
* <p>Caller must ensure the {@code VideoFrameProcessorManager} {@link #isEnabled()} before
|
||||
* calling this method.
|
||||
*/
|
||||
public Surface getInputSurface() {
|
||||
return checkNotNull(frameProcessor).getInputSurface();
|
||||
return checkNotNull(videoFrameProcessor).getInputSurface();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the output surface info.
|
||||
*
|
||||
* @param outputSurface The {@link Surface} to which {@link FrameProcessor} outputs.
|
||||
* @param outputSurface The {@link Surface} to which {@link VideoFrameProcessor} outputs.
|
||||
* @param outputResolution The {@link Size} of the output resolution.
|
||||
*/
|
||||
public void setOutputSurfaceInfo(Surface outputSurface, Size outputResolution) {
|
||||
@ -2087,7 +2094,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
currentSurfaceAndSize = Pair.create(outputSurface, outputResolution);
|
||||
if (isEnabled()) {
|
||||
checkNotNull(frameProcessor)
|
||||
checkNotNull(videoFrameProcessor)
|
||||
.setOutputSurfaceInfo(
|
||||
new SurfaceInfo(
|
||||
outputSurface, outputResolution.getWidth(), outputResolution.getHeight()));
|
||||
@ -2097,22 +2104,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
/**
|
||||
* Clears the set output surface info.
|
||||
*
|
||||
* <p>Caller must ensure the {@code FrameProcessorManager} {@link #isEnabled()} before calling
|
||||
* this method.
|
||||
* <p>Caller must ensure the {@code VideoFrameProcessorManager} {@link #isEnabled()} before
|
||||
* calling this method.
|
||||
*/
|
||||
public void clearOutputSurfaceInfo() {
|
||||
checkNotNull(frameProcessor).setOutputSurfaceInfo(null);
|
||||
checkNotNull(videoFrameProcessor).setOutputSurfaceInfo(null);
|
||||
currentSurfaceAndSize = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input surface info.
|
||||
*
|
||||
* <p>Caller must ensure the {@code FrameProcessorManager} {@link #isEnabled()} before calling
|
||||
* this method.
|
||||
* <p>Caller must ensure the {@code VideoFrameProcessorManager} {@link #isEnabled()} before
|
||||
* calling this method.
|
||||
*/
|
||||
public void setInputFormat(Format inputFormat) {
|
||||
checkNotNull(frameProcessor)
|
||||
checkNotNull(videoFrameProcessor)
|
||||
.setInputFrameInfo(
|
||||
new FrameInfo.Builder(inputFormat.width, inputFormat.height)
|
||||
.setPixelWidthHeightRatio(inputFormat.pixelWidthHeightRatio)
|
||||
@ -2127,7 +2134,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the necessary {@link MediaFormat} keys for frame processing. */
|
||||
/** Sets the necessary {@link MediaFormat} keys for video frame processing. */
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public MediaFormat amendMediaFormatKeys(MediaFormat mediaFormat) {
|
||||
if (Util.SDK_INT >= 29
|
||||
@ -2140,31 +2147,32 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
/**
|
||||
* Must be called when the codec is initialized.
|
||||
*
|
||||
* <p>Sets the {@code frameProcessorMaxPendingFrameCount} based on the {@code codecName}.
|
||||
* <p>Sets the {@code videoFrameProcessorMaxPendingFrameCount} based on the {@code codecName}.
|
||||
*/
|
||||
public void onCodecInitialized(String codecName) {
|
||||
frameProcessorMaxPendingFrameCount =
|
||||
videoFrameProcessorMaxPendingFrameCount =
|
||||
Util.getMaxPendingFramesCountForMediaCodecEncoders(
|
||||
renderer.context, codecName, /* requestedHdrToneMapping= */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to {@linkplain FrameProcessor#registerInputFrame register an input frame}.
|
||||
* Tries to {@linkplain VideoFrameProcessor#registerInputFrame register an input frame}.
|
||||
*
|
||||
* <p>Caller must ensure the {@code FrameProcessorManager} {@link #isEnabled()} before calling
|
||||
* this method.
|
||||
* <p>Caller must ensure the {@code VideoFrameProcessorManager} {@link #isEnabled()} before
|
||||
* calling this method.
|
||||
*
|
||||
* @param format The {@link Format} associated with the frame.
|
||||
* @param isLastBuffer Whether the buffer is the last from the decoder to register.
|
||||
* @return Whether {@link MediaCodec} should render the frame to {@link FrameProcessor}.
|
||||
* @return Whether {@link MediaCodec} should render the frame to {@link VideoFrameProcessor}.
|
||||
*/
|
||||
public boolean maybeRegisterFrame(
|
||||
Format format, long presentationTimestampUs, boolean isLastBuffer) {
|
||||
checkStateNotNull(frameProcessor);
|
||||
checkState(frameProcessorMaxPendingFrameCount != C.LENGTH_UNSET);
|
||||
checkStateNotNull(videoFrameProcessor);
|
||||
checkState(videoFrameProcessorMaxPendingFrameCount != C.LENGTH_UNSET);
|
||||
checkState(!registeredLastFrame);
|
||||
if (frameProcessor.getPendingInputFrameCount() < frameProcessorMaxPendingFrameCount) {
|
||||
frameProcessor.registerInputFrame();
|
||||
if (videoFrameProcessor.getPendingInputFrameCount()
|
||||
< videoFrameProcessorMaxPendingFrameCount) {
|
||||
videoFrameProcessor.registerInputFrame();
|
||||
|
||||
if (currentFrameFormat == null) {
|
||||
currentFrameFormat = Pair.create(presentationTimestampUs, format);
|
||||
@ -2185,11 +2193,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
/**
|
||||
* Releases the processed frames to the {@linkplain #setOutputSurfaceInfo output surface}.
|
||||
*
|
||||
* <p>Caller must ensure the {@code FrameProcessorManager} {@link #isEnabled()} before calling
|
||||
* this method.
|
||||
* <p>Caller must ensure the {@code VideoFrameProcessorManager} {@link #isEnabled()} before
|
||||
* calling this method.
|
||||
*/
|
||||
public void releaseProcessedFrames(long positionUs, long elapsedRealtimeUs) {
|
||||
checkStateNotNull(frameProcessor);
|
||||
checkStateNotNull(videoFrameProcessor);
|
||||
while (!processedFramesTimestampsUs.isEmpty()) {
|
||||
boolean isStarted = renderer.getState() == STATE_STARTED;
|
||||
long bufferPresentationTimeUs = checkNotNull(processedFramesTimestampsUs.peek());
|
||||
@ -2205,7 +2213,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
boolean shouldReleaseFrameImmediately = renderer.shouldForceRender(positionUs, earlyUs);
|
||||
if (shouldReleaseFrameImmediately) {
|
||||
releaseProcessedFrameInternal(
|
||||
FrameProcessor.RELEASE_OUTPUT_FRAME_IMMEDIATELY, isLastFrame);
|
||||
VideoFrameProcessor.RELEASE_OUTPUT_FRAME_IMMEDIATELY, isLastFrame);
|
||||
break;
|
||||
} else if (!isStarted || positionUs == renderer.initialPositionUs) {
|
||||
return;
|
||||
@ -2224,9 +2232,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
earlyUs = (adjustedFrameReleaseTimeNs - System.nanoTime()) / 1000;
|
||||
|
||||
// TODO(b/238302341) Handle very late buffers and drop to key frame. Need to flush
|
||||
// FrameProcessor input frames in this case.
|
||||
// VideoFrameProcessor input frames in this case.
|
||||
if (renderer.shouldDropOutputBuffer(earlyUs, elapsedRealtimeUs, isLastFrame)) {
|
||||
releaseProcessedFrameInternal(FrameProcessor.DROP_OUTPUT_FRAME, isLastFrame);
|
||||
releaseProcessedFrameInternal(VideoFrameProcessor.DROP_OUTPUT_FRAME, isLastFrame);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -2249,12 +2257,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
/**
|
||||
* Releases the resources.
|
||||
*
|
||||
* <p>Caller must ensure frame processing {@linkplain #isEnabled() is not enabled} before
|
||||
* <p>Caller must ensure video frame processing {@linkplain #isEnabled() is not enabled} before
|
||||
* calling this method.
|
||||
*/
|
||||
public void reset() {
|
||||
checkNotNull(frameProcessor).release();
|
||||
frameProcessor = null;
|
||||
checkNotNull(videoFrameProcessor).release();
|
||||
videoFrameProcessor = null;
|
||||
if (handler != null) {
|
||||
handler.removeCallbacksAndMessages(/* token= */ null);
|
||||
}
|
||||
@ -2266,11 +2274,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
|
||||
private void releaseProcessedFrameInternal(long releaseTimeNs, boolean isLastFrame) {
|
||||
checkStateNotNull(frameProcessor);
|
||||
frameProcessor.releaseOutputFrame(releaseTimeNs);
|
||||
checkStateNotNull(videoFrameProcessor);
|
||||
videoFrameProcessor.releaseOutputFrame(releaseTimeNs);
|
||||
processedFramesTimestampsUs.remove();
|
||||
renderer.lastRenderRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||
if (releaseTimeNs != FrameProcessor.DROP_OUTPUT_FRAME) {
|
||||
if (releaseTimeNs != VideoFrameProcessor.DROP_OUTPUT_FRAME) {
|
||||
renderer.maybeNotifyRenderedFirstFrame();
|
||||
}
|
||||
if (isLastFrame) {
|
||||
@ -2278,12 +2286,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FrameProcessorAccessor {
|
||||
private static final class VideoFrameProcessorAccessor {
|
||||
|
||||
private static @MonotonicNonNull Constructor<?> scaleToFitTransformationBuilderConstructor;
|
||||
private static @MonotonicNonNull Method setRotationMethod;
|
||||
private static @MonotonicNonNull Method buildScaleToFitTransformationMethod;
|
||||
private static @MonotonicNonNull Constructor<?> frameProcessorFactorConstructor;
|
||||
private static @MonotonicNonNull Constructor<?> videoFrameProcessorFactoryConstructor;
|
||||
|
||||
public static Effect createRotationEffect(float rotationDegrees) throws Exception {
|
||||
prepare();
|
||||
@ -2292,16 +2300,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
return (Effect) checkNotNull(buildScaleToFitTransformationMethod.invoke(builder));
|
||||
}
|
||||
|
||||
public static FrameProcessor.Factory getFrameProcessorFactory() throws Exception {
|
||||
public static VideoFrameProcessor.Factory getFrameProcessorFactory() throws Exception {
|
||||
prepare();
|
||||
return (FrameProcessor.Factory) frameProcessorFactorConstructor.newInstance();
|
||||
return (VideoFrameProcessor.Factory) videoFrameProcessorFactoryConstructor.newInstance();
|
||||
}
|
||||
|
||||
@EnsuresNonNull({
|
||||
"ScaleToFitEffectBuilder",
|
||||
"SetRotationMethod",
|
||||
"SetRotationMethod",
|
||||
"FrameProcessorFactoryClass"
|
||||
"VideoFrameProcessorFactoryClass"
|
||||
})
|
||||
private static void prepare() throws Exception {
|
||||
if (scaleToFitTransformationBuilderConstructor == null
|
||||
@ -2316,9 +2324,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||
buildScaleToFitTransformationMethod =
|
||||
scaleToFitTransformationBuilderClass.getMethod("build");
|
||||
}
|
||||
if (frameProcessorFactorConstructor == null) {
|
||||
frameProcessorFactorConstructor =
|
||||
Class.forName("androidx.media3.effect.GlEffectsFrameProcessor$Factory")
|
||||
if (videoFrameProcessorFactoryConstructor == null) {
|
||||
videoFrameProcessorFactoryConstructor =
|
||||
Class.forName("androidx.media3.effect.DefaultVideoFrameProcessor$Factory")
|
||||
.getConstructor();
|
||||
}
|
||||
}
|
||||
|
@ -33,9 +33,9 @@ import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.DebugViewProvider;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.SurfaceInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
@ -44,18 +44,18 @@ import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** A test runner for {@link FrameProcessor} tests. */
|
||||
/** A test runner for {@link VideoFrameProcessor} tests. */
|
||||
@UnstableApi
|
||||
@RequiresApi(19)
|
||||
public final class FrameProcessorTestRunner {
|
||||
public final class VideoFrameProcessorTestRunner {
|
||||
|
||||
/** A builder for {@link FrameProcessorTestRunner} instances. */
|
||||
/** A builder for {@link VideoFrameProcessorTestRunner} instances. */
|
||||
public static final class Builder {
|
||||
/** The ratio of width over height, for each pixel in a frame. */
|
||||
private static final float DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO = 1;
|
||||
|
||||
private @MonotonicNonNull String testId;
|
||||
private FrameProcessor.@MonotonicNonNull Factory frameProcessorFactory;
|
||||
private VideoFrameProcessor.@MonotonicNonNull Factory videoFrameProcessorFactory;
|
||||
private @MonotonicNonNull String videoAssetPath;
|
||||
private @MonotonicNonNull String outputFileLabel;
|
||||
private @MonotonicNonNull ImmutableList<Effect> effects;
|
||||
@ -82,13 +82,14 @@ public final class FrameProcessorTestRunner {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link FrameProcessor.Factory}.
|
||||
* Sets the {@link VideoFrameProcessor.Factory}.
|
||||
*
|
||||
* <p>This is a required value.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setFrameProcessorFactory(FrameProcessor.Factory frameProcessorFactory) {
|
||||
this.frameProcessorFactory = frameProcessorFactory;
|
||||
public Builder setVideoFrameProcessorFactory(
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory) {
|
||||
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -171,7 +172,7 @@ public final class FrameProcessorTestRunner {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Sets the input track type. See {@link FrameProcessor.Factory#create}.
|
||||
* Sets the input track type. See {@link VideoFrameProcessor.Factory#create}.
|
||||
*
|
||||
* <p>The default value is {@code true}.
|
||||
*/
|
||||
@ -181,14 +182,14 @@ public final class FrameProcessorTestRunner {
|
||||
return this;
|
||||
}
|
||||
|
||||
public FrameProcessorTestRunner build() throws FrameProcessingException {
|
||||
public VideoFrameProcessorTestRunner build() throws VideoFrameProcessingException {
|
||||
checkStateNotNull(testId, "testId must be set.");
|
||||
checkStateNotNull(frameProcessorFactory, "frameProcessorFactory must be set.");
|
||||
checkStateNotNull(videoFrameProcessorFactory, "videoFrameProcessorFactory must be set.");
|
||||
checkStateNotNull(videoAssetPath, "videoAssetPath must be set.");
|
||||
|
||||
return new FrameProcessorTestRunner(
|
||||
return new VideoFrameProcessorTestRunner(
|
||||
testId,
|
||||
frameProcessorFactory,
|
||||
videoFrameProcessorFactory,
|
||||
videoAssetPath,
|
||||
outputFileLabel == null ? "" : outputFileLabel,
|
||||
effects == null ? ImmutableList.of() : effects,
|
||||
@ -200,25 +201,25 @@ public final class FrameProcessorTestRunner {
|
||||
}
|
||||
|
||||
/**
|
||||
* Time to wait for the decoded frame to populate the {@link FrameProcessor} instance's input
|
||||
* surface and the {@link FrameProcessor} to finish processing the frame, in milliseconds.
|
||||
* Time to wait for the decoded frame to populate the {@link VideoFrameProcessor} instance's input
|
||||
* surface and the {@link VideoFrameProcessor} to finish processing the frame, in milliseconds.
|
||||
*/
|
||||
private static final int FRAME_PROCESSING_WAIT_MS = 5000;
|
||||
private static final int VIDEO_FRAME_PROCESSING_WAIT_MS = 5000;
|
||||
|
||||
private final String testId;
|
||||
private final String videoAssetPath;
|
||||
private final String outputFileLabel;
|
||||
private final float pixelWidthHeightRatio;
|
||||
private final AtomicReference<FrameProcessingException> frameProcessingException;
|
||||
private final AtomicReference<VideoFrameProcessingException> videoFrameProcessingException;
|
||||
|
||||
private final FrameProcessor frameProcessor;
|
||||
private final VideoFrameProcessor videoFrameProcessor;
|
||||
|
||||
private volatile @MonotonicNonNull ImageReader outputImageReader;
|
||||
private volatile boolean frameProcessingEnded;
|
||||
private volatile boolean videoFrameProcessingEnded;
|
||||
|
||||
private FrameProcessorTestRunner(
|
||||
private VideoFrameProcessorTestRunner(
|
||||
String testId,
|
||||
FrameProcessor.Factory frameProcessorFactory,
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||
String videoAssetPath,
|
||||
String outputFileLabel,
|
||||
ImmutableList<Effect> effects,
|
||||
@ -226,15 +227,15 @@ public final class FrameProcessorTestRunner {
|
||||
ColorInfo inputColorInfo,
|
||||
ColorInfo outputColorInfo,
|
||||
boolean isInputTextureExternal)
|
||||
throws FrameProcessingException {
|
||||
throws VideoFrameProcessingException {
|
||||
this.testId = testId;
|
||||
this.videoAssetPath = videoAssetPath;
|
||||
this.outputFileLabel = outputFileLabel;
|
||||
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||
frameProcessingException = new AtomicReference<>();
|
||||
videoFrameProcessingException = new AtomicReference<>();
|
||||
|
||||
frameProcessor =
|
||||
frameProcessorFactory.create(
|
||||
videoFrameProcessor =
|
||||
videoFrameProcessorFactory.create(
|
||||
getApplicationContext(),
|
||||
effects,
|
||||
DebugViewProvider.NONE,
|
||||
@ -243,13 +244,13 @@ public final class FrameProcessorTestRunner {
|
||||
isInputTextureExternal,
|
||||
/* releaseFramesAutomatically= */ true,
|
||||
MoreExecutors.directExecutor(),
|
||||
new FrameProcessor.Listener() {
|
||||
new VideoFrameProcessor.Listener() {
|
||||
@Override
|
||||
public void onOutputSizeChanged(int width, int height) {
|
||||
outputImageReader =
|
||||
ImageReader.newInstance(
|
||||
width, height, PixelFormat.RGBA_8888, /* maxImages= */ 1);
|
||||
checkNotNull(frameProcessor)
|
||||
checkNotNull(videoFrameProcessor)
|
||||
.setOutputSurfaceInfo(
|
||||
new SurfaceInfo(outputImageReader.getSurface(), width, height));
|
||||
}
|
||||
@ -260,13 +261,13 @@ public final class FrameProcessorTestRunner {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameProcessingError(FrameProcessingException exception) {
|
||||
frameProcessingException.set(exception);
|
||||
public void onError(VideoFrameProcessingException exception) {
|
||||
videoFrameProcessingException.set(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameProcessingEnded() {
|
||||
frameProcessingEnded = true;
|
||||
public void onEnded() {
|
||||
videoFrameProcessingEnded = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -277,13 +278,13 @@ public final class FrameProcessorTestRunner {
|
||||
new DecodeOneFrameUtil.Listener() {
|
||||
@Override
|
||||
public void onContainerExtracted(MediaFormat mediaFormat) {
|
||||
frameProcessor.setInputFrameInfo(
|
||||
videoFrameProcessor.setInputFrameInfo(
|
||||
new FrameInfo.Builder(
|
||||
mediaFormat.getInteger(MediaFormat.KEY_WIDTH),
|
||||
mediaFormat.getInteger(MediaFormat.KEY_HEIGHT))
|
||||
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
|
||||
.build());
|
||||
frameProcessor.registerInputFrame();
|
||||
videoFrameProcessor.registerInputFrame();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -291,36 +292,36 @@ public final class FrameProcessorTestRunner {
|
||||
// Do nothing.
|
||||
}
|
||||
},
|
||||
frameProcessor.getInputSurface());
|
||||
videoFrameProcessor.getInputSurface());
|
||||
return endFrameProcessingAndGetImage();
|
||||
}
|
||||
|
||||
public Bitmap processImageFrameAndEnd(Bitmap inputBitmap) throws Exception {
|
||||
frameProcessor.setInputFrameInfo(
|
||||
videoFrameProcessor.setInputFrameInfo(
|
||||
new FrameInfo.Builder(inputBitmap.getWidth(), inputBitmap.getHeight())
|
||||
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
|
||||
.build());
|
||||
frameProcessor.queueInputBitmap(inputBitmap, C.MICROS_PER_SECOND, /* frameRate= */ 1);
|
||||
videoFrameProcessor.queueInputBitmap(inputBitmap, C.MICROS_PER_SECOND, /* frameRate= */ 1);
|
||||
return endFrameProcessingAndGetImage();
|
||||
}
|
||||
|
||||
private Bitmap endFrameProcessingAndGetImage() throws Exception {
|
||||
frameProcessor.signalEndOfInput();
|
||||
Thread.sleep(FRAME_PROCESSING_WAIT_MS);
|
||||
videoFrameProcessor.signalEndOfInput();
|
||||
Thread.sleep(VIDEO_FRAME_PROCESSING_WAIT_MS);
|
||||
|
||||
assertThat(frameProcessingEnded).isTrue();
|
||||
assertThat(frameProcessingException.get()).isNull();
|
||||
assertThat(videoFrameProcessingEnded).isTrue();
|
||||
assertThat(videoFrameProcessingException.get()).isNull();
|
||||
|
||||
Image frameProcessorOutputImage = checkNotNull(outputImageReader).acquireLatestImage();
|
||||
Bitmap actualBitmap = createArgb8888BitmapFromRgba8888Image(frameProcessorOutputImage);
|
||||
frameProcessorOutputImage.close();
|
||||
Image videoFrameProcessorOutputImage = checkNotNull(outputImageReader).acquireLatestImage();
|
||||
Bitmap actualBitmap = createArgb8888BitmapFromRgba8888Image(videoFrameProcessorOutputImage);
|
||||
videoFrameProcessorOutputImage.close();
|
||||
maybeSaveTestBitmapToCacheDirectory(testId, /* bitmapLabel= */ outputFileLabel, actualBitmap);
|
||||
return actualBitmap;
|
||||
}
|
||||
|
||||
public void release() {
|
||||
if (frameProcessor != null) {
|
||||
frameProcessor.release();
|
||||
if (videoFrameProcessor != null) {
|
||||
videoFrameProcessor.release();
|
||||
}
|
||||
}
|
||||
}
|
@ -30,9 +30,9 @@ import androidx.media3.common.C;
|
||||
import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.effect.GlEffectsFrameProcessor;
|
||||
import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||
import androidx.media3.test.utils.DecodeOneFrameUtil;
|
||||
import androidx.media3.test.utils.FrameProcessorTestRunner;
|
||||
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.junit.After;
|
||||
@ -40,10 +40,10 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Instrumentation pixel-test for HDR to SDR tone-mapping via {@link GlEffectsFrameProcessor}.
|
||||
* Instrumentation pixel-test for HDR to SDR tone-mapping via {@link DefaultVideoFrameProcessor}.
|
||||
*
|
||||
* <p>Uses a {@link GlEffectsFrameProcessor} to process one frame, and checks that the actual output
|
||||
* matches expected output, either from a golden file or from another edit.
|
||||
* <p>Uses a {@link DefaultVideoFrameProcessor} to process one frame, and checks that the actual
|
||||
* output matches expected output, either from a golden file or from another edit.
|
||||
*/
|
||||
// TODO(b/263395272): Move this test to effects/mh tests.
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@ -75,12 +75,12 @@ public final class ToneMapHdrToSdrUsingOpenGlPixelTest {
|
||||
"OpenGL-based HDR to SDR tone mapping is unsupported below API 29.";
|
||||
private static final String SKIP_REASON_NO_YUV = "Device lacks YUV extension support.";
|
||||
|
||||
private @MonotonicNonNull FrameProcessorTestRunner frameProcessorTestRunner;
|
||||
private @MonotonicNonNull VideoFrameProcessorTestRunner videoFrameProcessorTestRunner;
|
||||
|
||||
@After
|
||||
public void release() {
|
||||
if (frameProcessorTestRunner != null) {
|
||||
frameProcessorTestRunner.release();
|
||||
if (videoFrameProcessorTestRunner != null) {
|
||||
videoFrameProcessorTestRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ public final class ToneMapHdrToSdrUsingOpenGlPixelTest {
|
||||
.setColorRange(C.COLOR_RANGE_LIMITED)
|
||||
.setColorTransfer(C.COLOR_TRANSFER_GAMMA_2_2)
|
||||
.build();
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setVideoAssetPath(INPUT_HLG_MP4_ASSET_STRING)
|
||||
.setInputColorInfo(hlgColor)
|
||||
@ -124,7 +124,7 @@ public final class ToneMapHdrToSdrUsingOpenGlPixelTest {
|
||||
|
||||
Bitmap actualBitmap;
|
||||
try {
|
||||
actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
if (e.getMessage() != null
|
||||
&& e.getMessage().equals(DecodeOneFrameUtil.NO_DECODER_SUPPORT_ERROR_STRING)) {
|
||||
@ -177,7 +177,7 @@ public final class ToneMapHdrToSdrUsingOpenGlPixelTest {
|
||||
.setColorRange(C.COLOR_RANGE_LIMITED)
|
||||
.setColorTransfer(C.COLOR_TRANSFER_GAMMA_2_2)
|
||||
.build();
|
||||
frameProcessorTestRunner =
|
||||
videoFrameProcessorTestRunner =
|
||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||
.setVideoAssetPath(INPUT_PQ_MP4_ASSET_STRING)
|
||||
.setInputColorInfo(pqColor)
|
||||
@ -187,7 +187,7 @@ public final class ToneMapHdrToSdrUsingOpenGlPixelTest {
|
||||
|
||||
Bitmap actualBitmap;
|
||||
try {
|
||||
actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
if (e.getMessage() != null
|
||||
&& e.getMessage().equals(DecodeOneFrameUtil.NO_DECODER_SUPPORT_ERROR_STRING)) {
|
||||
@ -209,10 +209,10 @@ public final class ToneMapHdrToSdrUsingOpenGlPixelTest {
|
||||
.isAtMost(MAXIMUM_DEVICE_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
||||
}
|
||||
|
||||
private FrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder(
|
||||
private VideoFrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder(
|
||||
String testId) {
|
||||
return new FrameProcessorTestRunner.Builder()
|
||||
return new VideoFrameProcessorTestRunner.Builder()
|
||||
.setTestId(testId)
|
||||
.setFrameProcessorFactory(new GlEffectsFrameProcessor.Factory());
|
||||
.setVideoFrameProcessorFactory(new DefaultVideoFrameProcessor.Factory());
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,11 @@
|
||||
package androidx.media3.transformer;
|
||||
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.audio.AudioProcessor;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.effect.GlEffectsFrameProcessor;
|
||||
import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
|
||||
@ -45,19 +45,19 @@ public final class Effects {
|
||||
*/
|
||||
public final ImmutableList<Effect> videoEffects;
|
||||
/**
|
||||
* The {@link FrameProcessor.Factory} for the {@link FrameProcessor} to use when applying the
|
||||
* {@code videoEffects} to the video frames.
|
||||
* The {@link VideoFrameProcessor.Factory} for the {@link VideoFrameProcessor} to use when
|
||||
* applying the {@code videoEffects} to the video frames.
|
||||
*/
|
||||
public final FrameProcessor.Factory frameProcessorFactory;
|
||||
public final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||
|
||||
/**
|
||||
* Creates an instance using a {@link GlEffectsFrameProcessor.Factory}.
|
||||
* Creates an instance using a {@link DefaultVideoFrameProcessor.Factory}.
|
||||
*
|
||||
* <p>This is equivalent to calling {@link Effects#Effects(List, List, FrameProcessor.Factory)}
|
||||
* with a {@link GlEffectsFrameProcessor.Factory}.
|
||||
* <p>This is equivalent to calling {@link Effects#Effects(List, List,
|
||||
* VideoFrameProcessor.Factory)} with a {@link DefaultVideoFrameProcessor.Factory}.
|
||||
*/
|
||||
public Effects(List<AudioProcessor> audioProcessors, List<Effect> videoEffects) {
|
||||
this(audioProcessors, videoEffects, new GlEffectsFrameProcessor.Factory());
|
||||
this(audioProcessors, videoEffects, new DefaultVideoFrameProcessor.Factory());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,14 +65,14 @@ public final class Effects {
|
||||
*
|
||||
* @param audioProcessors The {@link #audioProcessors}.
|
||||
* @param videoEffects The {@link #videoEffects}.
|
||||
* @param frameProcessorFactory The {@link #frameProcessorFactory}.
|
||||
* @param videoFrameProcessorFactory The {@link #videoFrameProcessorFactory}.
|
||||
*/
|
||||
public Effects(
|
||||
List<AudioProcessor> audioProcessors,
|
||||
List<Effect> videoEffects,
|
||||
FrameProcessor.Factory frameProcessorFactory) {
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory) {
|
||||
this.audioProcessors = ImmutableList.copyOf(audioProcessors);
|
||||
this.videoEffects = ImmutableList.copyOf(videoEffects);
|
||||
this.frameProcessorFactory = frameProcessorFactory;
|
||||
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ import android.os.SystemClock;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.audio.AudioProcessor.AudioFormat;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
@ -66,7 +66,7 @@ public final class TransformationException extends Exception {
|
||||
ERROR_CODE_ENCODER_INIT_FAILED,
|
||||
ERROR_CODE_ENCODING_FAILED,
|
||||
ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED,
|
||||
ERROR_CODE_FRAME_PROCESSING_FAILED,
|
||||
ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED,
|
||||
ERROR_CODE_AUDIO_PROCESSING_FAILED,
|
||||
ERROR_CODE_MUXING_FAILED,
|
||||
})
|
||||
@ -151,8 +151,8 @@ public final class TransformationException extends Exception {
|
||||
|
||||
// Video editing errors (5xxx).
|
||||
|
||||
/** Caused by a frame processing failure. */
|
||||
public static final int ERROR_CODE_FRAME_PROCESSING_FAILED = 5001;
|
||||
/** Caused by a video frame processing failure. */
|
||||
public static final int ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED = 5001;
|
||||
|
||||
// Audio processing errors (6xxx).
|
||||
|
||||
@ -182,7 +182,7 @@ public final class TransformationException extends Exception {
|
||||
.put("ERROR_CODE_ENCODER_INIT_FAILED", ERROR_CODE_ENCODER_INIT_FAILED)
|
||||
.put("ERROR_CODE_ENCODING_FAILED", ERROR_CODE_ENCODING_FAILED)
|
||||
.put("ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED", ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED)
|
||||
.put("ERROR_CODE_FRAME_PROCESSING_FAILED", ERROR_CODE_FRAME_PROCESSING_FAILED)
|
||||
.put("ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED", ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED)
|
||||
.put("ERROR_CODE_AUDIO_PROCESSING_FAILED", ERROR_CODE_AUDIO_PROCESSING_FAILED)
|
||||
.put("ERROR_CODE_MUXING_FAILED", ERROR_CODE_MUXING_FAILED)
|
||||
.buildOrThrow();
|
||||
@ -271,15 +271,15 @@ public final class TransformationException extends Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance for a {@link FrameProcessor} related exception.
|
||||
* Creates an instance for a {@link VideoFrameProcessor} related exception.
|
||||
*
|
||||
* @param cause The cause of the failure.
|
||||
* @param errorCode See {@link #errorCode}.
|
||||
* @return The created instance.
|
||||
*/
|
||||
/* package */ static TransformationException createForFrameProcessingException(
|
||||
FrameProcessingException cause, int errorCode) {
|
||||
return new TransformationException("Frame processing error", cause, errorCode);
|
||||
/* package */ static TransformationException createForVideoFrameProcessingException(
|
||||
VideoFrameProcessingException cause, int errorCode) {
|
||||
return new TransformationException("Video frame processing error", cause, errorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,10 +28,10 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.DebugViewProvider;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaLibraryInfo;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.audio.AudioProcessor;
|
||||
import androidx.media3.common.audio.SonicAudioProcessor;
|
||||
import androidx.media3.common.util.Clock;
|
||||
@ -39,7 +39,7 @@ import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.common.util.ListenerSet;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.effect.GlEffectsFrameProcessor;
|
||||
import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
@ -89,7 +89,7 @@ public final class Transformer {
|
||||
private boolean generateSilentAudio;
|
||||
private ListenerSet<Transformer.Listener> listeners;
|
||||
private AssetLoader.@MonotonicNonNull Factory assetLoaderFactory;
|
||||
private FrameProcessor.Factory frameProcessorFactory;
|
||||
private VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||
private Codec.EncoderFactory encoderFactory;
|
||||
private Muxer.Factory muxerFactory;
|
||||
private Looper looper;
|
||||
@ -106,7 +106,7 @@ public final class Transformer {
|
||||
transformationRequest = new TransformationRequest.Builder().build();
|
||||
audioProcessors = ImmutableList.of();
|
||||
videoEffects = ImmutableList.of();
|
||||
frameProcessorFactory = new GlEffectsFrameProcessor.Factory();
|
||||
videoFrameProcessorFactory = new DefaultVideoFrameProcessor.Factory();
|
||||
encoderFactory = new DefaultEncoderFactory.Builder(this.context).build();
|
||||
muxerFactory = new DefaultMuxer.Factory();
|
||||
looper = Util.getCurrentOrMainLooper();
|
||||
@ -126,7 +126,7 @@ public final class Transformer {
|
||||
this.generateSilentAudio = transformer.generateSilentAudio;
|
||||
this.listeners = transformer.listeners;
|
||||
this.assetLoaderFactory = transformer.assetLoaderFactory;
|
||||
this.frameProcessorFactory = transformer.frameProcessorFactory;
|
||||
this.videoFrameProcessorFactory = transformer.videoFrameProcessorFactory;
|
||||
this.encoderFactory = transformer.encoderFactory;
|
||||
this.muxerFactory = transformer.muxerFactory;
|
||||
this.looper = transformer.looper;
|
||||
@ -298,13 +298,14 @@ public final class Transformer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Set the {@link FrameProcessor.Factory} in an {@link EditedMediaItem}, and pass it
|
||||
* to {@link #start(EditedMediaItem, String)} instead.
|
||||
* @deprecated Set the {@link VideoFrameProcessor.Factory} in an {@link EditedMediaItem}, and
|
||||
* pass it to {@link #start(EditedMediaItem, String)} instead.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
@Deprecated
|
||||
public Builder setFrameProcessorFactory(FrameProcessor.Factory frameProcessorFactory) {
|
||||
this.frameProcessorFactory = frameProcessorFactory;
|
||||
public Builder setFrameProcessorFactory(
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory) {
|
||||
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -450,7 +451,7 @@ public final class Transformer {
|
||||
generateSilentAudio,
|
||||
listeners,
|
||||
assetLoaderFactory,
|
||||
frameProcessorFactory,
|
||||
videoFrameProcessorFactory,
|
||||
encoderFactory,
|
||||
muxerFactory,
|
||||
looper,
|
||||
@ -608,7 +609,7 @@ public final class Transformer {
|
||||
private final boolean generateSilentAudio;
|
||||
private final ListenerSet<Transformer.Listener> listeners;
|
||||
private final AssetLoader.Factory assetLoaderFactory;
|
||||
private final FrameProcessor.Factory frameProcessorFactory;
|
||||
private final VideoFrameProcessor.Factory videoFrameProcessorFactory;
|
||||
private final Codec.EncoderFactory encoderFactory;
|
||||
private final Muxer.Factory muxerFactory;
|
||||
private final Looper looper;
|
||||
@ -629,7 +630,7 @@ public final class Transformer {
|
||||
boolean generateSilentAudio,
|
||||
ListenerSet<Listener> listeners,
|
||||
AssetLoader.Factory assetLoaderFactory,
|
||||
FrameProcessor.Factory frameProcessorFactory,
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
Muxer.Factory muxerFactory,
|
||||
Looper looper,
|
||||
@ -647,7 +648,7 @@ public final class Transformer {
|
||||
this.generateSilentAudio = generateSilentAudio;
|
||||
this.listeners = listeners;
|
||||
this.assetLoaderFactory = assetLoaderFactory;
|
||||
this.frameProcessorFactory = frameProcessorFactory;
|
||||
this.videoFrameProcessorFactory = videoFrameProcessorFactory;
|
||||
this.encoderFactory = encoderFactory;
|
||||
this.muxerFactory = muxerFactory;
|
||||
this.looper = looper;
|
||||
@ -844,7 +845,7 @@ public final class Transformer {
|
||||
.setRemoveAudio(removeAudio)
|
||||
.setRemoveVideo(removeVideo)
|
||||
.setFlattenForSlowMotion(flattenForSlowMotion)
|
||||
.setEffects(new Effects(audioProcessors, videoEffects, frameProcessorFactory))
|
||||
.setEffects(new Effects(audioProcessors, videoEffects, videoFrameProcessorFactory))
|
||||
.build();
|
||||
start(editedMediaItem, path);
|
||||
}
|
||||
|
@ -498,7 +498,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
streamOffsetUs,
|
||||
transformationRequest,
|
||||
firstEditedMediaItem.effects.videoEffects,
|
||||
firstEditedMediaItem.effects.frameProcessorFactory,
|
||||
firstEditedMediaItem.effects.videoFrameProcessorFactory,
|
||||
encoderFactory,
|
||||
muxerWrapper,
|
||||
/* errorConsumer= */ this::onError,
|
||||
|
@ -37,10 +37,10 @@ import androidx.media3.common.DebugViewProvider;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.FrameInfo;
|
||||
import androidx.media3.common.FrameProcessingException;
|
||||
import androidx.media3.common.FrameProcessor;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.SurfaceInfo;
|
||||
import androidx.media3.common.VideoFrameProcessingException;
|
||||
import androidx.media3.common.VideoFrameProcessor;
|
||||
import androidx.media3.common.util.Consumer;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.Util;
|
||||
@ -58,8 +58,8 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
/** MIME type to use for output video if the input type is not a video. */
|
||||
private static final String DEFAULT_OUTPUT_MIME_TYPE = MimeTypes.VIDEO_H265;
|
||||
|
||||
private final FrameProcessor frameProcessor;
|
||||
private final ColorInfo frameProcessorInputColor;
|
||||
private final VideoFrameProcessor videoFrameProcessor;
|
||||
private final ColorInfo videoFrameProcessorInputColor;
|
||||
private final FrameInfo firstFrameInfo;
|
||||
|
||||
private final EncoderWrapper encoderWrapper;
|
||||
@ -67,7 +67,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
|
||||
/**
|
||||
* The timestamp of the last buffer processed before {@linkplain
|
||||
* FrameProcessor.Listener#onFrameProcessingEnded() frame processing has ended}.
|
||||
* VideoFrameProcessor.Listener#onEnded() frame processing has ended}.
|
||||
*/
|
||||
private volatile long finalFramePresentationTimeUs;
|
||||
|
||||
@ -78,7 +78,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
long streamOffsetUs,
|
||||
TransformationRequest transformationRequest,
|
||||
ImmutableList<Effect> effects,
|
||||
FrameProcessor.Factory frameProcessorFactory,
|
||||
VideoFrameProcessor.Factory videoFrameProcessorFactory,
|
||||
Codec.EncoderFactory encoderFactory,
|
||||
MuxerWrapper muxerWrapper,
|
||||
Consumer<TransformationException> errorConsumer,
|
||||
@ -122,12 +122,12 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
ColorInfo encoderInputColor = encoderWrapper.getSupportedInputColor();
|
||||
// If not tone mapping using OpenGL, the decoder will output the encoderInputColor,
|
||||
// possibly by tone mapping.
|
||||
frameProcessorInputColor =
|
||||
videoFrameProcessorInputColor =
|
||||
isGlToneMapping ? checkNotNull(firstInputFormat.colorInfo) : encoderInputColor;
|
||||
// For consistency with the Android platform, OpenGL tone mapping outputs colors with
|
||||
// C.COLOR_TRANSFER_GAMMA_2_2 instead of C.COLOR_TRANSFER_SDR, and outputs this as
|
||||
// C.COLOR_TRANSFER_SDR to the encoder.
|
||||
ColorInfo frameProcessorOutputColor =
|
||||
ColorInfo videoFrameProcessorOutputColor =
|
||||
isGlToneMapping
|
||||
? new ColorInfo.Builder()
|
||||
.setColorSpace(C.COLOR_SPACE_BT709)
|
||||
@ -136,23 +136,23 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
.build()
|
||||
: encoderInputColor;
|
||||
try {
|
||||
frameProcessor =
|
||||
frameProcessorFactory.create(
|
||||
videoFrameProcessor =
|
||||
videoFrameProcessorFactory.create(
|
||||
context,
|
||||
effects,
|
||||
debugViewProvider,
|
||||
frameProcessorInputColor,
|
||||
frameProcessorOutputColor,
|
||||
videoFrameProcessorInputColor,
|
||||
videoFrameProcessorOutputColor,
|
||||
MimeTypes.isVideo(firstInputFormat.sampleMimeType),
|
||||
/* releaseFramesAutomatically= */ true,
|
||||
MoreExecutors.directExecutor(),
|
||||
new FrameProcessor.Listener() {
|
||||
new VideoFrameProcessor.Listener() {
|
||||
private long lastProcessedFramePresentationTimeUs;
|
||||
|
||||
@Override
|
||||
public void onOutputSizeChanged(int width, int height) {
|
||||
try {
|
||||
checkNotNull(frameProcessor)
|
||||
checkNotNull(videoFrameProcessor)
|
||||
.setOutputSurfaceInfo(encoderWrapper.getSurfaceInfo(width, height));
|
||||
} catch (TransformationException exception) {
|
||||
errorConsumer.accept(exception);
|
||||
@ -166,14 +166,15 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameProcessingError(FrameProcessingException exception) {
|
||||
public void onError(VideoFrameProcessingException exception) {
|
||||
errorConsumer.accept(
|
||||
TransformationException.createForFrameProcessingException(
|
||||
exception, TransformationException.ERROR_CODE_FRAME_PROCESSING_FAILED));
|
||||
TransformationException.createForVideoFrameProcessingException(
|
||||
exception,
|
||||
TransformationException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameProcessingEnded() {
|
||||
public void onEnded() {
|
||||
VideoSamplePipeline.this.finalFramePresentationTimeUs =
|
||||
lastProcessedFramePresentationTimeUs;
|
||||
try {
|
||||
@ -183,9 +184,9 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (FrameProcessingException e) {
|
||||
throw TransformationException.createForFrameProcessingException(
|
||||
e, TransformationException.ERROR_CODE_FRAME_PROCESSING_FAILED);
|
||||
} catch (VideoFrameProcessingException e) {
|
||||
throw TransformationException.createForVideoFrameProcessingException(
|
||||
e, TransformationException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED);
|
||||
}
|
||||
// The decoder rotates encoded frames for display by firstInputFormat.rotationDegrees.
|
||||
int decodedWidth =
|
||||
@ -206,43 +207,43 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
@Override
|
||||
public void onMediaItemChanged(
|
||||
EditedMediaItem editedMediaItem, Format trackFormat, long mediaItemOffsetUs) {
|
||||
frameProcessor.setInputFrameInfo(
|
||||
videoFrameProcessor.setInputFrameInfo(
|
||||
new FrameInfo.Builder(firstFrameInfo).setOffsetToAddUs(mediaItemOffsetUs).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queueInputBitmap(Bitmap inputBitmap, long durationUs, int frameRate) {
|
||||
frameProcessor.queueInputBitmap(inputBitmap, durationUs, frameRate);
|
||||
videoFrameProcessor.queueInputBitmap(inputBitmap, durationUs, frameRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Surface getInputSurface() {
|
||||
return frameProcessor.getInputSurface();
|
||||
return videoFrameProcessor.getInputSurface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorInfo getExpectedInputColorInfo() {
|
||||
return frameProcessorInputColor;
|
||||
return videoFrameProcessorInputColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerVideoFrame() {
|
||||
frameProcessor.registerInputFrame();
|
||||
videoFrameProcessor.registerInputFrame();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingVideoFrameCount() {
|
||||
return frameProcessor.getPendingInputFrameCount();
|
||||
return videoFrameProcessor.getPendingInputFrameCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void signalEndOfVideoInput() {
|
||||
frameProcessor.signalEndOfInput();
|
||||
videoFrameProcessor.signalEndOfInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
frameProcessor.release();
|
||||
videoFrameProcessor.release();
|
||||
encoderWrapper.release();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user