Move FrameProcessorChain OpenGL setup to factory method.
The encoder surface is no longer needed for the OpenGL setup and frame processor initialization, as a placeholder surface is used instead. So all of the setup can now be done in the factory method. PiperOrigin-RevId: 438844450
This commit is contained in:
parent
f8f8b75500
commit
2a66c7b8f5
@ -260,7 +260,7 @@ public final class FrameProcessorChainPixelTest {
|
|||||||
outputSize.getHeight(),
|
outputSize.getHeight(),
|
||||||
PixelFormat.RGBA_8888,
|
PixelFormat.RGBA_8888,
|
||||||
/* maxImages= */ 1);
|
/* maxImages= */ 1);
|
||||||
frameProcessorChain.configure(
|
frameProcessorChain.setOutputSurface(
|
||||||
outputImageReader.getSurface(),
|
outputImageReader.getSurface(),
|
||||||
outputSize.getWidth(),
|
outputSize.getWidth(),
|
||||||
outputSize.getHeight(),
|
outputSize.getHeight(),
|
||||||
|
@ -28,10 +28,9 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Robolectric tests for {@link FrameProcessorChain}.
|
* Tests for creating and configuring a {@link FrameProcessorChain}.
|
||||||
*
|
*
|
||||||
* <p>See {@code FrameProcessorChainPixelTest} in the androidTest directory for instrumentation
|
* <p>See {@link FrameProcessorChainPixelTest} for data processing tests.
|
||||||
* tests.
|
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class FrameProcessorChainTest {
|
public final class FrameProcessorChainTest {
|
@ -54,7 +54,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
* and is processed on a background thread as it becomes available. All input frames should be
|
* and is processed on a background thread as it becomes available. All input frames should be
|
||||||
* {@linkplain #registerInputFrame() registered} before they are rendered to the input surface.
|
* {@linkplain #registerInputFrame() registered} before they are rendered to the input surface.
|
||||||
* {@link #getPendingFrameCount()} can be used to check whether there are frames that have not been
|
* {@link #getPendingFrameCount()} can be used to check whether there are frames that have not been
|
||||||
* fully processed yet. Output is written to its {@linkplain #configure(Surface, int, int,
|
* fully processed yet. Output is written to its {@linkplain #setOutputSurface(Surface, int, int,
|
||||||
* SurfaceView) output surface}.
|
* SurfaceView) output surface}.
|
||||||
*/
|
*/
|
||||||
/* package */ final class FrameProcessorChain {
|
/* package */ final class FrameProcessorChain {
|
||||||
@ -73,7 +73,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
* @param frameProcessors The {@link GlFrameProcessor GlFrameProcessors} to apply to each frame.
|
* @param frameProcessors The {@link GlFrameProcessor GlFrameProcessors} to apply to each frame.
|
||||||
* @param enableExperimentalHdrEditing Whether to attempt to process the input as an HDR signal.
|
* @param enableExperimentalHdrEditing Whether to attempt to process the input as an HDR signal.
|
||||||
* @return A new instance.
|
* @return A new instance.
|
||||||
* @throws TransformationException If the {@code pixelWidthHeightRatio} isn't 1.
|
* @throws TransformationException If the {@code pixelWidthHeightRatio} isn't 1, reading shader
|
||||||
|
* files fails, or an OpenGL error occurs while creating and configuring the OpenGL
|
||||||
|
* components.
|
||||||
*/
|
*/
|
||||||
public static FrameProcessorChain create(
|
public static FrameProcessorChain create(
|
||||||
Context context,
|
Context context,
|
||||||
@ -93,42 +95,104 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
TransformationException.ERROR_CODE_GL_INIT_FAILED);
|
TransformationException.ERROR_CODE_GL_INIT_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExecutorService singleThreadExecutorService = Util.newSingleThreadExecutor(THREAD_NAME);
|
||||||
ExternalCopyFrameProcessor externalCopyFrameProcessor =
|
ExternalCopyFrameProcessor externalCopyFrameProcessor =
|
||||||
new ExternalCopyFrameProcessor(context, enableExperimentalHdrEditing);
|
new ExternalCopyFrameProcessor(context, enableExperimentalHdrEditing);
|
||||||
externalCopyFrameProcessor.setInputSize(inputWidth, inputHeight);
|
|
||||||
Size inputSize = externalCopyFrameProcessor.getOutputSize();
|
try {
|
||||||
for (int i = 0; i < frameProcessors.size(); i++) {
|
return singleThreadExecutorService
|
||||||
frameProcessors.get(i).setInputSize(inputSize.getWidth(), inputSize.getHeight());
|
.submit(
|
||||||
inputSize = frameProcessors.get(i).getOutputSize();
|
() ->
|
||||||
|
createOpenGlObjectsAndFrameProcessorChain(
|
||||||
|
inputWidth,
|
||||||
|
inputHeight,
|
||||||
|
frameProcessors,
|
||||||
|
enableExperimentalHdrEditing,
|
||||||
|
singleThreadExecutorService,
|
||||||
|
externalCopyFrameProcessor))
|
||||||
|
.get();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw TransformationException.createForFrameProcessorChain(
|
||||||
|
e, TransformationException.ERROR_CODE_GL_INIT_FAILED);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw TransformationException.createForFrameProcessorChain(
|
||||||
|
e, TransformationException.ERROR_CODE_GL_INIT_FAILED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the OpenGL textures, framebuffers, initializes the {@link GlFrameProcessor
|
||||||
|
* GlFrameProcessors} and returns a new {@code FrameProcessorChain}.
|
||||||
|
*
|
||||||
|
* <p>This method must by executed using the {@code singleThreadExecutorService}.
|
||||||
|
*/
|
||||||
|
private static FrameProcessorChain createOpenGlObjectsAndFrameProcessorChain(
|
||||||
|
int inputWidth,
|
||||||
|
int inputHeight,
|
||||||
|
List<GlFrameProcessor> frameProcessors,
|
||||||
|
boolean enableExperimentalHdrEditing,
|
||||||
|
ExecutorService singleThreadExecutorService,
|
||||||
|
ExternalCopyFrameProcessor externalCopyFrameProcessor)
|
||||||
|
throws IOException {
|
||||||
|
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
||||||
|
|
||||||
|
EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
||||||
|
EGLContext eglContext =
|
||||||
|
enableExperimentalHdrEditing
|
||||||
|
? GlUtil.createEglContextEs3Rgba1010102(eglDisplay)
|
||||||
|
: GlUtil.createEglContext(eglDisplay);
|
||||||
|
|
||||||
|
if (GlUtil.isSurfacelessContextExtensionSupported()) {
|
||||||
|
GlUtil.focusEglSurface(
|
||||||
|
eglDisplay, eglContext, EGL14.EGL_NO_SURFACE, /* width= */ 1, /* height= */ 1);
|
||||||
|
} else if (enableExperimentalHdrEditing) {
|
||||||
|
// TODO(b/209404935): Don't assume BT.2020 PQ input/output.
|
||||||
|
GlUtil.focusPlaceholderEglSurfaceBt2020Pq(eglContext, eglDisplay);
|
||||||
|
} else {
|
||||||
|
GlUtil.focusPlaceholderEglSurface(eglContext, eglDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
int inputExternalTexId = GlUtil.createExternalTexture();
|
||||||
|
externalCopyFrameProcessor.setInputSize(inputWidth, inputHeight);
|
||||||
|
externalCopyFrameProcessor.initialize(inputExternalTexId);
|
||||||
|
|
||||||
|
int[] framebuffers = new int[frameProcessors.size()];
|
||||||
|
Size inputSize = externalCopyFrameProcessor.getOutputSize();
|
||||||
|
for (int i = 0; i < frameProcessors.size(); i++) {
|
||||||
|
int inputTexId = GlUtil.createTexture(inputSize.getWidth(), inputSize.getHeight());
|
||||||
|
framebuffers[i] = GlUtil.createFboForTexture(inputTexId);
|
||||||
|
frameProcessors.get(i).setInputSize(inputSize.getWidth(), inputSize.getHeight());
|
||||||
|
frameProcessors.get(i).initialize(inputTexId);
|
||||||
|
inputSize = frameProcessors.get(i).getOutputSize();
|
||||||
|
}
|
||||||
return new FrameProcessorChain(
|
return new FrameProcessorChain(
|
||||||
externalCopyFrameProcessor, frameProcessors, enableExperimentalHdrEditing);
|
eglDisplay,
|
||||||
|
eglContext,
|
||||||
|
singleThreadExecutorService,
|
||||||
|
inputExternalTexId,
|
||||||
|
externalCopyFrameProcessor,
|
||||||
|
framebuffers,
|
||||||
|
ImmutableList.copyOf(frameProcessors),
|
||||||
|
enableExperimentalHdrEditing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String THREAD_NAME = "Transformer:FrameProcessorChain";
|
private static final String THREAD_NAME = "Transformer:FrameProcessorChain";
|
||||||
|
|
||||||
private final boolean enableExperimentalHdrEditing;
|
private final boolean enableExperimentalHdrEditing;
|
||||||
private @MonotonicNonNull EGLDisplay eglDisplay;
|
private final EGLDisplay eglDisplay;
|
||||||
private @MonotonicNonNull EGLContext eglContext;
|
private final EGLContext eglContext;
|
||||||
/** Some OpenGL commands may block, so all OpenGL commands are run on a background thread. */
|
/** Some OpenGL commands may block, so all OpenGL commands are run on a background thread. */
|
||||||
private final ExecutorService singleThreadExecutorService;
|
private final ExecutorService singleThreadExecutorService;
|
||||||
/** Futures corresponding to the executor service's pending tasks. */
|
/** Futures corresponding to the executor service's pending tasks. */
|
||||||
private final ConcurrentLinkedQueue<Future<?>> futures;
|
private final ConcurrentLinkedQueue<Future<?>> futures;
|
||||||
/** Number of frames {@linkplain #registerInputFrame() registered} but not fully processed. */
|
/** Number of frames {@linkplain #registerInputFrame() registered} but not fully processed. */
|
||||||
private final AtomicInteger pendingFrameCount;
|
private final AtomicInteger pendingFrameCount;
|
||||||
/** Prevents further frame processing tasks from being scheduled after {@link #release()}. */
|
|
||||||
private volatile boolean releaseRequested;
|
|
||||||
|
|
||||||
private boolean inputStreamEnded;
|
|
||||||
/** Wraps the {@link #inputSurfaceTexture}. */
|
/** Wraps the {@link #inputSurfaceTexture}. */
|
||||||
private @MonotonicNonNull Surface inputSurface;
|
private final Surface inputSurface;
|
||||||
/** Associated with an OpenGL external texture. */
|
/** Associated with an OpenGL external texture. */
|
||||||
private @MonotonicNonNull SurfaceTexture inputSurfaceTexture;
|
private final SurfaceTexture inputSurfaceTexture;
|
||||||
/**
|
|
||||||
* Identifier of the external texture the {@link ExternalCopyFrameProcessor} reads its input from.
|
|
||||||
*/
|
|
||||||
private int inputExternalTexId;
|
|
||||||
/** Transformation matrix associated with the {@link #inputSurfaceTexture}. */
|
/** Transformation matrix associated with the {@link #inputSurfaceTexture}. */
|
||||||
private final float[] textureTransformMatrix;
|
private final float[] textureTransformMatrix;
|
||||||
|
|
||||||
@ -158,19 +222,33 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
*/
|
*/
|
||||||
private @MonotonicNonNull EGLSurface debugPreviewEglSurface;
|
private @MonotonicNonNull EGLSurface debugPreviewEglSurface;
|
||||||
|
|
||||||
|
private boolean inputStreamEnded;
|
||||||
|
/** Prevents further frame processing tasks from being scheduled after {@link #release()}. */
|
||||||
|
private volatile boolean releaseRequested;
|
||||||
|
|
||||||
private FrameProcessorChain(
|
private FrameProcessorChain(
|
||||||
|
EGLDisplay eglDisplay,
|
||||||
|
EGLContext eglContext,
|
||||||
|
ExecutorService singleThreadExecutorService,
|
||||||
|
int inputExternalTexId,
|
||||||
ExternalCopyFrameProcessor externalCopyFrameProcessor,
|
ExternalCopyFrameProcessor externalCopyFrameProcessor,
|
||||||
List<GlFrameProcessor> frameProcessors,
|
int[] framebuffers,
|
||||||
|
ImmutableList<GlFrameProcessor> frameProcessors,
|
||||||
boolean enableExperimentalHdrEditing) {
|
boolean enableExperimentalHdrEditing) {
|
||||||
|
|
||||||
|
this.eglDisplay = eglDisplay;
|
||||||
|
this.eglContext = eglContext;
|
||||||
|
this.singleThreadExecutorService = singleThreadExecutorService;
|
||||||
this.externalCopyFrameProcessor = externalCopyFrameProcessor;
|
this.externalCopyFrameProcessor = externalCopyFrameProcessor;
|
||||||
this.frameProcessors = ImmutableList.copyOf(frameProcessors);
|
this.framebuffers = framebuffers;
|
||||||
|
this.frameProcessors = frameProcessors;
|
||||||
this.enableExperimentalHdrEditing = enableExperimentalHdrEditing;
|
this.enableExperimentalHdrEditing = enableExperimentalHdrEditing;
|
||||||
|
|
||||||
singleThreadExecutorService = Util.newSingleThreadExecutor(THREAD_NAME);
|
|
||||||
futures = new ConcurrentLinkedQueue<>();
|
futures = new ConcurrentLinkedQueue<>();
|
||||||
pendingFrameCount = new AtomicInteger();
|
pendingFrameCount = new AtomicInteger();
|
||||||
|
inputSurfaceTexture = new SurfaceTexture(inputExternalTexId);
|
||||||
|
inputSurface = new Surface(inputSurfaceTexture);
|
||||||
textureTransformMatrix = new float[16];
|
textureTransformMatrix = new float[16];
|
||||||
framebuffers = new int[frameProcessors.size()];
|
|
||||||
outputSize =
|
outputSize =
|
||||||
frameProcessors.isEmpty()
|
frameProcessors.isEmpty()
|
||||||
? externalCopyFrameProcessor.getOutputSize()
|
? externalCopyFrameProcessor.getOutputSize()
|
||||||
@ -185,26 +263,20 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@code FrameProcessorChain} to process frames to the specified output targets.
|
* Sets the output {@link Surface}.
|
||||||
*
|
*
|
||||||
* <p>This method may only be called once and may override the {@linkplain
|
* <p>This method may override the output size of the final {@link GlFrameProcessor}.
|
||||||
* GlFrameProcessor#setInputSize(int, int) output size} of the final {@link GlFrameProcessor}.
|
|
||||||
*
|
*
|
||||||
* @param outputSurface The output {@link Surface}.
|
* @param outputSurface The output {@link Surface}.
|
||||||
* @param outputWidth The output width, in pixels.
|
* @param outputWidth The output width, in pixels.
|
||||||
* @param outputHeight The output height, in pixels.
|
* @param outputHeight The output height, in pixels.
|
||||||
* @param debugSurfaceView Optional debug {@link SurfaceView} to show output.
|
* @param debugSurfaceView Optional debug {@link SurfaceView} to show output.
|
||||||
* @throws IllegalStateException If the {@code FrameProcessorChain} has already been configured.
|
|
||||||
* @throws TransformationException If reading shader files fails, or an OpenGL error occurs while
|
|
||||||
* creating and configuring the OpenGL components.
|
|
||||||
*/
|
*/
|
||||||
public void configure(
|
public void setOutputSurface(
|
||||||
Surface outputSurface,
|
Surface outputSurface,
|
||||||
int outputWidth,
|
int outputWidth,
|
||||||
int outputHeight,
|
int outputHeight,
|
||||||
@Nullable SurfaceView debugSurfaceView)
|
@Nullable SurfaceView debugSurfaceView) {
|
||||||
throws TransformationException {
|
|
||||||
checkState(inputSurface == null, "The FrameProcessorChain has already been configured.");
|
|
||||||
// TODO(b/218488308): Don't override output size for encoder fallback. Instead allow the final
|
// TODO(b/218488308): Don't override output size for encoder fallback. Instead allow the final
|
||||||
// GlFrameProcessor to be re-configured or append another GlFrameProcessor.
|
// GlFrameProcessor to be re-configured or append another GlFrameProcessor.
|
||||||
outputSize = new Size(outputWidth, outputHeight);
|
outputSize = new Size(outputWidth, outputHeight);
|
||||||
@ -214,21 +286,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
debugPreviewHeight = debugSurfaceView.getHeight();
|
debugPreviewHeight = debugSurfaceView.getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
futures.add(
|
||||||
// Wait for task to finish to be able to use inputExternalTexId to create the SurfaceTexture.
|
singleThreadExecutorService.submit(
|
||||||
singleThreadExecutorService
|
() -> createOpenGlSurfaces(outputSurface, debugSurfaceView)));
|
||||||
.submit(this::createOpenGlObjectsAndInitializeFrameProcessors)
|
|
||||||
.get();
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
throw TransformationException.createForFrameProcessorChain(
|
|
||||||
e, TransformationException.ERROR_CODE_GL_INIT_FAILED);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
throw TransformationException.createForFrameProcessorChain(
|
|
||||||
e, TransformationException.ERROR_CODE_GL_INIT_FAILED);
|
|
||||||
}
|
|
||||||
|
|
||||||
inputSurfaceTexture = new SurfaceTexture(inputExternalTexId);
|
|
||||||
inputSurfaceTexture.setOnFrameAvailableListener(
|
inputSurfaceTexture.setOnFrameAvailableListener(
|
||||||
surfaceTexture -> {
|
surfaceTexture -> {
|
||||||
if (releaseRequested) {
|
if (releaseRequested) {
|
||||||
@ -244,21 +305,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
inputSurface = new Surface(inputSurfaceTexture);
|
|
||||||
|
|
||||||
futures.add(
|
|
||||||
singleThreadExecutorService.submit(
|
|
||||||
() -> createOpenGlSurfaces(outputSurface, debugSurfaceView)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the input {@link Surface}. */
|
||||||
* Returns the input {@link Surface}.
|
|
||||||
*
|
|
||||||
* <p>The {@code FrameProcessorChain} must be {@linkplain #configure(Surface, int, int,
|
|
||||||
* SurfaceView) configured}.
|
|
||||||
*/
|
|
||||||
public Surface getInputSurface() {
|
public Surface getInputSurface() {
|
||||||
checkStateNotNull(inputSurface, "The FrameProcessorChain must be configured.");
|
|
||||||
return inputSurface;
|
return inputSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,9 +397,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
/**
|
/**
|
||||||
* Creates the OpenGL surfaces.
|
* Creates the OpenGL surfaces.
|
||||||
*
|
*
|
||||||
* <p>This method should only be called after {@link
|
* <p>This method should only be called on the {@linkplain #THREAD_NAME background thread}.
|
||||||
* #createOpenGlObjectsAndInitializeFrameProcessors()} and must be called on the background
|
|
||||||
* thread.
|
|
||||||
*/
|
*/
|
||||||
private void createOpenGlSurfaces(Surface outputSurface, @Nullable SurfaceView debugSurfaceView) {
|
private void createOpenGlSurfaces(Surface outputSurface, @Nullable SurfaceView debugSurfaceView) {
|
||||||
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
||||||
@ -371,57 +419,15 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the OpenGL textures and framebuffers, and initializes the {@link GlFrameProcessor
|
|
||||||
* GlFrameProcessors}.
|
|
||||||
*
|
|
||||||
* <p>This method should only be called on the background thread.
|
|
||||||
*/
|
|
||||||
private Void createOpenGlObjectsAndInitializeFrameProcessors() throws IOException {
|
|
||||||
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
|
||||||
|
|
||||||
eglDisplay = GlUtil.createEglDisplay();
|
|
||||||
eglContext =
|
|
||||||
enableExperimentalHdrEditing
|
|
||||||
? GlUtil.createEglContextEs3Rgba1010102(eglDisplay)
|
|
||||||
: GlUtil.createEglContext(eglDisplay);
|
|
||||||
|
|
||||||
if (GlUtil.isSurfacelessContextExtensionSupported()) {
|
|
||||||
GlUtil.focusEglSurface(
|
|
||||||
eglDisplay, eglContext, EGL14.EGL_NO_SURFACE, /* width= */ 1, /* height= */ 1);
|
|
||||||
} else if (enableExperimentalHdrEditing) {
|
|
||||||
// TODO(b/209404935): Don't assume BT.2020 PQ input/output.
|
|
||||||
GlUtil.focusPlaceholderEglSurfaceBt2020Pq(eglContext, eglDisplay);
|
|
||||||
} else {
|
|
||||||
GlUtil.focusPlaceholderEglSurface(eglContext, eglDisplay);
|
|
||||||
}
|
|
||||||
|
|
||||||
inputExternalTexId = GlUtil.createExternalTexture();
|
|
||||||
externalCopyFrameProcessor.initialize(inputExternalTexId);
|
|
||||||
|
|
||||||
Size intermediateSize = externalCopyFrameProcessor.getOutputSize();
|
|
||||||
for (int i = 0; i < frameProcessors.size(); i++) {
|
|
||||||
int inputTexId =
|
|
||||||
GlUtil.createTexture(intermediateSize.getWidth(), intermediateSize.getHeight());
|
|
||||||
framebuffers[i] = GlUtil.createFboForTexture(inputTexId);
|
|
||||||
frameProcessors.get(i).initialize(inputTexId);
|
|
||||||
intermediateSize = frameProcessors.get(i).getOutputSize();
|
|
||||||
}
|
|
||||||
// Return something because only Callables not Runnables can throw checked exceptions.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes an input frame.
|
* Processes an input frame.
|
||||||
*
|
*
|
||||||
* <p>This method should only be called on the background thread.
|
* <p>This method should only be called on the {@linkplain #THREAD_NAME background thread}.
|
||||||
*/
|
*/
|
||||||
@RequiresNonNull("inputSurfaceTexture")
|
@RequiresNonNull("inputSurfaceTexture")
|
||||||
private void processFrame() {
|
private void processFrame() {
|
||||||
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
checkState(Thread.currentThread().getName().equals(THREAD_NAME));
|
||||||
checkStateNotNull(eglSurface);
|
checkStateNotNull(eglSurface, "No output surface set.");
|
||||||
checkStateNotNull(eglContext);
|
|
||||||
checkStateNotNull(eglDisplay);
|
|
||||||
|
|
||||||
if (frameProcessors.isEmpty()) {
|
if (frameProcessors.isEmpty()) {
|
||||||
GlUtil.focusEglSurface(
|
GlUtil.focusEglSurface(
|
||||||
|
@ -116,7 +116,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
requestedEncoderFormat,
|
requestedEncoderFormat,
|
||||||
encoderSupportedFormat));
|
encoderSupportedFormat));
|
||||||
|
|
||||||
frameProcessorChain.configure(
|
frameProcessorChain.setOutputSurface(
|
||||||
/* outputSurface= */ encoder.getInputSurface(),
|
/* outputSurface= */ encoder.getInputSurface(),
|
||||||
/* outputWidth= */ encoderSupportedFormat.width,
|
/* outputWidth= */ encoderSupportedFormat.width,
|
||||||
/* outputHeight= */ encoderSupportedFormat.height,
|
/* outputHeight= */ encoderSupportedFormat.height,
|
||||||
|
@ -114,6 +114,8 @@ public class DefaultEncoderFactoryTest {
|
|||||||
@Test
|
@Test
|
||||||
public void createForVideoEncoding_withNoSupportedEncoder_throws() {
|
public void createForVideoEncoding_withNoSupportedEncoder_throws() {
|
||||||
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
|
||||||
|
|
||||||
|
TransformationException exception =
|
||||||
assertThrows(
|
assertThrows(
|
||||||
TransformationException.class,
|
TransformationException.class,
|
||||||
() ->
|
() ->
|
||||||
@ -121,6 +123,10 @@ public class DefaultEncoderFactoryTest {
|
|||||||
.createForVideoEncoding(
|
.createForVideoEncoding(
|
||||||
requestedVideoFormat,
|
requestedVideoFormat,
|
||||||
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H265)));
|
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H265)));
|
||||||
|
|
||||||
|
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
|
||||||
|
assertThat(exception.errorCode)
|
||||||
|
.isEqualTo(TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -30,7 +30,6 @@ import static org.mockito.Mockito.times;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.MediaCodecInfo;
|
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -405,26 +404,6 @@ public final class TransformerEndToEndTest {
|
|||||||
.isEqualTo(TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED);
|
.isEqualTo(TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void startTransformation_withVideoEncoderFormatUnsupported_completesWithError()
|
|
||||||
throws Exception {
|
|
||||||
Transformer transformer =
|
|
||||||
createTransformerBuilder(/* enableFallback= */ false)
|
|
||||||
.setTransformationRequest(
|
|
||||||
new TransformationRequest.Builder()
|
|
||||||
.setVideoMimeType(MimeTypes.VIDEO_H263) // unsupported encoder MIME type
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
MediaItem mediaItem = MediaItem.fromUri(URI_PREFIX + FILE_VIDEO_ONLY);
|
|
||||||
|
|
||||||
transformer.startTransformation(mediaItem, outputPath);
|
|
||||||
TransformationException exception = TransformerTestRunner.runUntilError(transformer);
|
|
||||||
|
|
||||||
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
|
|
||||||
assertThat(exception.errorCode)
|
|
||||||
.isEqualTo(TransformationException.ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void startTransformation_withIoError_completesWithError() throws Exception {
|
public void startTransformation_withIoError_completesWithError() throws Exception {
|
||||||
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
|
||||||
@ -801,11 +780,6 @@ public final class TransformerEndToEndTest {
|
|||||||
throwingCodecConfig,
|
throwingCodecConfig,
|
||||||
/* colorFormats= */ ImmutableList.of(),
|
/* colorFormats= */ ImmutableList.of(),
|
||||||
/* isDecoder= */ true);
|
/* isDecoder= */ true);
|
||||||
addCodec(
|
|
||||||
MimeTypes.VIDEO_H263,
|
|
||||||
throwingCodecConfig,
|
|
||||||
ImmutableList.of(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
|
|
||||||
/* isDecoder= */ false);
|
|
||||||
addCodec(
|
addCodec(
|
||||||
MimeTypes.AUDIO_AMR_NB,
|
MimeTypes.AUDIO_AMR_NB,
|
||||||
throwingCodecConfig,
|
throwingCodecConfig,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user