mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Transformer GL: Create setTransformationMatrix().
Allows a transformation matrix to be input into Transformer, to apply vertex transformations like cropping, rotation, and other transformations built into android.graphics.Matrix. Not building out into a VertexTransformation class yet, as that class structure wouldn't make sense until we can modify resolution, per TODOs. PiperOrigin-RevId: 413384409
This commit is contained in:
parent
a803604605
commit
73ed482094
@ -24,6 +24,7 @@ import static java.lang.Math.max;
|
|||||||
import android.content.res.AssetFileDescriptor;
|
import android.content.res.AssetFileDescriptor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.media.Image;
|
import android.media.Image;
|
||||||
import android.media.ImageReader;
|
import android.media.ImageReader;
|
||||||
@ -84,9 +85,14 @@ public final class FrameEditorTest {
|
|||||||
int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
|
int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
|
||||||
frameEditorOutputImageReader =
|
frameEditorOutputImageReader =
|
||||||
ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, /* maxImages= */ 1);
|
ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, /* maxImages= */ 1);
|
||||||
|
Matrix identityMatrix = new Matrix();
|
||||||
frameEditor =
|
frameEditor =
|
||||||
FrameEditor.create(
|
FrameEditor.create(
|
||||||
getApplicationContext(), width, height, frameEditorOutputImageReader.getSurface());
|
getApplicationContext(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
identityMatrix,
|
||||||
|
frameEditorOutputImageReader.getSurface());
|
||||||
|
|
||||||
// Queue the first video frame from the extractor.
|
// Queue the first video frame from the extractor.
|
||||||
String mimeType = checkNotNull(mediaFormat.getString(MediaFormat.KEY_MIME));
|
String mimeType = checkNotNull(mediaFormat.getString(MediaFormat.KEY_MIME));
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.media3.transformer.mh;
|
||||||
|
|
||||||
|
import static androidx.media3.transformer.mh.AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING;
|
||||||
|
import static androidx.media3.transformer.mh.AndroidTestUtil.runTransformer;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import androidx.media3.transformer.Transformer;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** {@link Transformer} instrumentation test for setting a transformation matrix. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class SetTransformationMatrixTransformationTest {
|
||||||
|
@Test
|
||||||
|
public void setTransformationMatrixTransform() throws Exception {
|
||||||
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
Matrix transformationMatrix = new Matrix();
|
||||||
|
transformationMatrix.postTranslate(/* dx= */ .2f, /* dy= */ .1f);
|
||||||
|
Transformer transformer =
|
||||||
|
new Transformer.Builder(context).setTransformationMatrix(transformationMatrix).build();
|
||||||
|
|
||||||
|
runTransformer(
|
||||||
|
context,
|
||||||
|
/* testId= */ "setTransformationMatrixTransform",
|
||||||
|
transformer,
|
||||||
|
REMOTE_MP4_10_SECONDS_URI_STRING,
|
||||||
|
/* timeoutSeconds= */ 120);
|
||||||
|
}
|
||||||
|
}
|
@ -14,8 +14,9 @@
|
|||||||
attribute vec4 a_position;
|
attribute vec4 a_position;
|
||||||
attribute vec4 a_texcoord;
|
attribute vec4 a_texcoord;
|
||||||
uniform mat4 tex_transform;
|
uniform mat4 tex_transform;
|
||||||
|
uniform mat4 transformation_matrix;
|
||||||
varying vec2 v_texcoord;
|
varying vec2 v_texcoord;
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = a_position;
|
gl_Position = a_position;
|
||||||
v_texcoord = (tex_transform * a_texcoord).xy;
|
v_texcoord = (transformation_matrix * tex_transform * a_texcoord).xy;
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
import android.opengl.EGL14;
|
import android.opengl.EGL14;
|
||||||
import android.opengl.EGLContext;
|
import android.opengl.EGLContext;
|
||||||
@ -27,10 +28,7 @@ import android.view.Surface;
|
|||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/** FrameEditor applies changes to individual video frames. */
|
||||||
* FrameEditor applies changes to individual video frames. Changes include just resolution for now,
|
|
||||||
* but may later include brightness, cropping, rotation, etc.
|
|
||||||
*/
|
|
||||||
/* package */ final class FrameEditor {
|
/* package */ final class FrameEditor {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -43,11 +41,16 @@ import java.io.IOException;
|
|||||||
* @param context A {@link Context}.
|
* @param context A {@link Context}.
|
||||||
* @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 transformationMatrix The transformation matrix to apply to each frame.
|
||||||
* @param outputSurface The {@link Surface}.
|
* @param outputSurface The {@link Surface}.
|
||||||
* @return A configured {@code FrameEditor}.
|
* @return A configured {@code FrameEditor}.
|
||||||
*/
|
*/
|
||||||
public static FrameEditor create(
|
public static FrameEditor create(
|
||||||
Context context, int outputWidth, int outputHeight, Surface outputSurface) {
|
Context context,
|
||||||
|
int outputWidth,
|
||||||
|
int outputHeight,
|
||||||
|
Matrix transformationMatrix,
|
||||||
|
Surface outputSurface) {
|
||||||
EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
EGLDisplay eglDisplay = GlUtil.createEglDisplay();
|
||||||
EGLContext eglContext;
|
EGLContext eglContext;
|
||||||
try {
|
try {
|
||||||
@ -58,15 +61,16 @@ import java.io.IOException;
|
|||||||
EGLSurface eglSurface = GlUtil.getEglSurface(eglDisplay, outputSurface);
|
EGLSurface eglSurface = GlUtil.getEglSurface(eglDisplay, outputSurface);
|
||||||
GlUtil.focusSurface(eglDisplay, eglContext, eglSurface, outputWidth, outputHeight);
|
GlUtil.focusSurface(eglDisplay, eglContext, eglSurface, outputWidth, outputHeight);
|
||||||
int textureId = GlUtil.createExternalTexture();
|
int textureId = GlUtil.createExternalTexture();
|
||||||
GlUtil.Program copyProgram;
|
GlUtil.Program glProgram;
|
||||||
try {
|
try {
|
||||||
// TODO(internal b/205002913): check the loaded program is consistent with the attributes
|
// TODO(internal b/205002913): check the loaded program is consistent with the attributes
|
||||||
// and uniforms expected in the code.
|
// and uniforms expected in the code.
|
||||||
copyProgram = new GlUtil.Program(context, VERTEX_SHADER_FILE_PATH, FRAGMENT_SHADER_FILE_PATH);
|
glProgram = new GlUtil.Program(context, VERTEX_SHADER_FILE_PATH, FRAGMENT_SHADER_FILE_PATH);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
copyProgram.setBufferAttribute(
|
|
||||||
|
glProgram.setBufferAttribute(
|
||||||
"a_position",
|
"a_position",
|
||||||
new float[] {
|
new float[] {
|
||||||
-1.0f, -1.0f, 0.0f, 1.0f,
|
-1.0f, -1.0f, 0.0f, 1.0f,
|
||||||
@ -75,7 +79,7 @@ import java.io.IOException;
|
|||||||
1.0f, 1.0f, 0.0f, 1.0f,
|
1.0f, 1.0f, 0.0f, 1.0f,
|
||||||
},
|
},
|
||||||
/* size= */ 4);
|
/* size= */ 4);
|
||||||
copyProgram.setBufferAttribute(
|
glProgram.setBufferAttribute(
|
||||||
"a_texcoord",
|
"a_texcoord",
|
||||||
new float[] {
|
new float[] {
|
||||||
0.0f, 0.0f, 0.0f, 1.0f,
|
0.0f, 0.0f, 0.0f, 1.0f,
|
||||||
@ -84,14 +88,57 @@ import java.io.IOException;
|
|||||||
1.0f, 1.0f, 0.0f, 1.0f,
|
1.0f, 1.0f, 0.0f, 1.0f,
|
||||||
},
|
},
|
||||||
/* size= */ 4);
|
/* size= */ 4);
|
||||||
copyProgram.setSamplerTexIdUniform("tex_sampler", textureId, /* unit= */ 0);
|
glProgram.setSamplerTexIdUniform("tex_sampler", textureId, /* unit= */ 0);
|
||||||
return new FrameEditor(eglDisplay, eglContext, eglSurface, textureId, copyProgram);
|
|
||||||
|
float[] transformationMatrixArray = getGlMatrixArray(transformationMatrix);
|
||||||
|
glProgram.setFloatsUniform("transformation_matrix", transformationMatrixArray);
|
||||||
|
|
||||||
|
return new FrameEditor(eglDisplay, eglContext, eglSurface, textureId, glProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a 4x4, column-major Matrix float array, from an input {@link Matrix}. This is useful
|
||||||
|
* for converting to the 4x4 column-major format commonly used in OpenGL.
|
||||||
|
*/
|
||||||
|
private static final float[] getGlMatrixArray(Matrix matrix) {
|
||||||
|
float[] matrix3x3Array = new float[9];
|
||||||
|
matrix.getValues(matrix3x3Array);
|
||||||
|
float[] matrix4x4Array = getMatrix4x4Array(matrix3x3Array);
|
||||||
|
|
||||||
|
// Transpose from row-major to column-major representations.
|
||||||
|
float[] transposedMatrix4x4Array = new float[16];
|
||||||
|
android.opengl.Matrix.transposeM(
|
||||||
|
transposedMatrix4x4Array, /* mTransOffset= */ 0, matrix4x4Array, /* mOffset= */ 0);
|
||||||
|
|
||||||
|
return transposedMatrix4x4Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a 4x4 matrix array containing the 3x3 matrix array's contents.
|
||||||
|
*
|
||||||
|
* <p>The 3x3 matrix array is expected to be in 2 dimensions, and the 4x4 matrix array is expected
|
||||||
|
* to be in 3 dimensions. The output will have the third row/column's values be an identity
|
||||||
|
* matrix's values, so that vertex transformations using this matrix will not affect the z axis.
|
||||||
|
* <br>
|
||||||
|
* Input format: [a, b, c, d, e, f, g, h, i] <br>
|
||||||
|
* Output format: [a, b, 0, c, d, e, 0, f, 0, 0, 1, 0, g, h, 0, i]
|
||||||
|
*/
|
||||||
|
private static final float[] getMatrix4x4Array(float[] matrix3x3Array) {
|
||||||
|
float[] matrix4x4Array = new float[16];
|
||||||
|
matrix4x4Array[10] = 1;
|
||||||
|
for (int inputRow = 0; inputRow < 3; inputRow++) {
|
||||||
|
for (int inputColumn = 0; inputColumn < 3; inputColumn++) {
|
||||||
|
int outputRow = (inputRow == 2) ? 3 : inputRow;
|
||||||
|
int outputColumn = (inputColumn == 2) ? 3 : inputColumn;
|
||||||
|
matrix4x4Array[outputRow * 4 + outputColumn] = matrix3x3Array[inputRow * 3 + inputColumn];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matrix4x4Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Predefined shader values.
|
// Predefined shader values.
|
||||||
private static final String VERTEX_SHADER_FILE_PATH = "shaders/blit_vertex_shader.glsl";
|
private static final String VERTEX_SHADER_FILE_PATH = "shaders/vertex_shader.glsl";
|
||||||
private static final String FRAGMENT_SHADER_FILE_PATH =
|
private static final String FRAGMENT_SHADER_FILE_PATH = "shaders/fragment_shader.glsl";
|
||||||
"shaders/copy_external_fragment_shader.glsl";
|
|
||||||
|
|
||||||
private final float[] textureTransformMatrix;
|
private final float[] textureTransformMatrix;
|
||||||
private final EGLDisplay eglDisplay;
|
private final EGLDisplay eglDisplay;
|
||||||
@ -101,7 +148,7 @@ import java.io.IOException;
|
|||||||
private final SurfaceTexture inputSurfaceTexture;
|
private final SurfaceTexture inputSurfaceTexture;
|
||||||
private final Surface inputSurface;
|
private final Surface inputSurface;
|
||||||
|
|
||||||
private final GlUtil.Program copyProgram;
|
private final GlUtil.Program glProgram;
|
||||||
|
|
||||||
private volatile boolean hasInputData;
|
private volatile boolean hasInputData;
|
||||||
|
|
||||||
@ -110,12 +157,12 @@ import java.io.IOException;
|
|||||||
EGLContext eglContext,
|
EGLContext eglContext,
|
||||||
EGLSurface eglSurface,
|
EGLSurface eglSurface,
|
||||||
int textureId,
|
int textureId,
|
||||||
GlUtil.Program copyProgram) {
|
GlUtil.Program glProgram) {
|
||||||
this.eglDisplay = eglDisplay;
|
this.eglDisplay = eglDisplay;
|
||||||
this.eglContext = eglContext;
|
this.eglContext = eglContext;
|
||||||
this.eglSurface = eglSurface;
|
this.eglSurface = eglSurface;
|
||||||
this.textureId = textureId;
|
this.textureId = textureId;
|
||||||
this.copyProgram = copyProgram;
|
this.glProgram = glProgram;
|
||||||
textureTransformMatrix = new float[16];
|
textureTransformMatrix = new float[16];
|
||||||
inputSurfaceTexture = new SurfaceTexture(textureId);
|
inputSurfaceTexture = new SurfaceTexture(textureId);
|
||||||
inputSurfaceTexture.setOnFrameAvailableListener(surfaceTexture -> hasInputData = true);
|
inputSurfaceTexture.setOnFrameAvailableListener(surfaceTexture -> hasInputData = true);
|
||||||
@ -135,12 +182,12 @@ import java.io.IOException;
|
|||||||
return hasInputData;
|
return hasInputData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Processes pending input data. */
|
/** Processes pending input frame. */
|
||||||
public void processData() {
|
public void processData() {
|
||||||
inputSurfaceTexture.updateTexImage();
|
inputSurfaceTexture.updateTexImage();
|
||||||
inputSurfaceTexture.getTransformMatrix(textureTransformMatrix);
|
inputSurfaceTexture.getTransformMatrix(textureTransformMatrix);
|
||||||
copyProgram.setFloatsUniform("tex_transform", textureTransformMatrix);
|
glProgram.setFloatsUniform("tex_transform", textureTransformMatrix);
|
||||||
copyProgram.bindAttributesAndUniforms();
|
glProgram.bindAttributesAndUniforms();
|
||||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||||
long surfaceTextureTimestampNs = inputSurfaceTexture.getTimestamp();
|
long surfaceTextureTimestampNs = inputSurfaceTexture.getTimestamp();
|
||||||
EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, surfaceTextureTimestampNs);
|
EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, surfaceTextureTimestampNs);
|
||||||
@ -150,7 +197,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
/** Releases all resources. */
|
/** Releases all resources. */
|
||||||
public void release() {
|
public void release() {
|
||||||
copyProgram.delete();
|
glProgram.delete();
|
||||||
GlUtil.deleteTexture(textureId);
|
GlUtil.deleteTexture(textureId);
|
||||||
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
GlUtil.destroyEglContext(eglDisplay, eglContext);
|
||||||
inputSurfaceTexture.release();
|
inputSurfaceTexture.release();
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
|
import android.graphics.Matrix;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
/** A media transformation configuration. */
|
/** A media transformation configuration. */
|
||||||
@ -25,6 +26,7 @@ import androidx.annotation.Nullable;
|
|||||||
public final boolean removeVideo;
|
public final boolean removeVideo;
|
||||||
public final boolean flattenForSlowMotion;
|
public final boolean flattenForSlowMotion;
|
||||||
public final int outputHeight;
|
public final int outputHeight;
|
||||||
|
public final Matrix transformationMatrix;
|
||||||
public final String containerMimeType;
|
public final String containerMimeType;
|
||||||
@Nullable public final String audioMimeType;
|
@Nullable public final String audioMimeType;
|
||||||
@Nullable public final String videoMimeType;
|
@Nullable public final String videoMimeType;
|
||||||
@ -34,6 +36,7 @@ import androidx.annotation.Nullable;
|
|||||||
boolean removeVideo,
|
boolean removeVideo,
|
||||||
boolean flattenForSlowMotion,
|
boolean flattenForSlowMotion,
|
||||||
int outputHeight,
|
int outputHeight,
|
||||||
|
Matrix transformationMatrix,
|
||||||
String containerMimeType,
|
String containerMimeType,
|
||||||
@Nullable String audioMimeType,
|
@Nullable String audioMimeType,
|
||||||
@Nullable String videoMimeType) {
|
@Nullable String videoMimeType) {
|
||||||
@ -41,6 +44,7 @@ import androidx.annotation.Nullable;
|
|||||||
this.removeVideo = removeVideo;
|
this.removeVideo = removeVideo;
|
||||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||||
this.outputHeight = outputHeight;
|
this.outputHeight = outputHeight;
|
||||||
|
this.transformationMatrix = transformationMatrix;
|
||||||
this.containerMimeType = containerMimeType;
|
this.containerMimeType = containerMimeType;
|
||||||
this.audioMimeType = audioMimeType;
|
this.audioMimeType = audioMimeType;
|
||||||
this.videoMimeType = videoMimeType;
|
this.videoMimeType = videoMimeType;
|
||||||
|
@ -25,6 +25,7 @@ import static androidx.media3.exoplayer.DefaultLoadControl.DEFAULT_MIN_BUFFER_MS
|
|||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.media.MediaMuxer;
|
import android.media.MediaMuxer;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -102,6 +103,7 @@ public final class Transformer {
|
|||||||
private boolean removeVideo;
|
private boolean removeVideo;
|
||||||
private boolean flattenForSlowMotion;
|
private boolean flattenForSlowMotion;
|
||||||
private int outputHeight;
|
private int outputHeight;
|
||||||
|
private Matrix transformationMatrix;
|
||||||
private String containerMimeType;
|
private String containerMimeType;
|
||||||
@Nullable private String audioMimeType;
|
@Nullable private String audioMimeType;
|
||||||
@Nullable private String videoMimeType;
|
@Nullable private String videoMimeType;
|
||||||
@ -114,6 +116,7 @@ public final class Transformer {
|
|||||||
public Builder() {
|
public Builder() {
|
||||||
muxerFactory = new FrameworkMuxer.Factory();
|
muxerFactory = new FrameworkMuxer.Factory();
|
||||||
outputHeight = Format.NO_VALUE;
|
outputHeight = Format.NO_VALUE;
|
||||||
|
transformationMatrix = new Matrix();
|
||||||
containerMimeType = MimeTypes.VIDEO_MP4;
|
containerMimeType = MimeTypes.VIDEO_MP4;
|
||||||
listener = new Listener() {};
|
listener = new Listener() {};
|
||||||
looper = Util.getCurrentOrMainLooper();
|
looper = Util.getCurrentOrMainLooper();
|
||||||
@ -129,6 +132,7 @@ public final class Transformer {
|
|||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
muxerFactory = new FrameworkMuxer.Factory();
|
muxerFactory = new FrameworkMuxer.Factory();
|
||||||
outputHeight = Format.NO_VALUE;
|
outputHeight = Format.NO_VALUE;
|
||||||
|
transformationMatrix = new Matrix();
|
||||||
containerMimeType = MimeTypes.VIDEO_MP4;
|
containerMimeType = MimeTypes.VIDEO_MP4;
|
||||||
listener = new Listener() {};
|
listener = new Listener() {};
|
||||||
looper = Util.getCurrentOrMainLooper();
|
looper = Util.getCurrentOrMainLooper();
|
||||||
@ -144,6 +148,7 @@ public final class Transformer {
|
|||||||
this.removeVideo = transformer.transformation.removeVideo;
|
this.removeVideo = transformer.transformation.removeVideo;
|
||||||
this.flattenForSlowMotion = transformer.transformation.flattenForSlowMotion;
|
this.flattenForSlowMotion = transformer.transformation.flattenForSlowMotion;
|
||||||
this.outputHeight = transformer.transformation.outputHeight;
|
this.outputHeight = transformer.transformation.outputHeight;
|
||||||
|
this.transformationMatrix = transformer.transformation.transformationMatrix;
|
||||||
this.containerMimeType = transformer.transformation.containerMimeType;
|
this.containerMimeType = transformer.transformation.containerMimeType;
|
||||||
this.audioMimeType = transformer.transformation.audioMimeType;
|
this.audioMimeType = transformer.transformation.audioMimeType;
|
||||||
this.videoMimeType = transformer.transformation.videoMimeType;
|
this.videoMimeType = transformer.transformation.videoMimeType;
|
||||||
@ -260,6 +265,26 @@ public final class Transformer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the transformation matrix. The default value is to apply no change.
|
||||||
|
*
|
||||||
|
* <p>This can be used to perform operations supported by {@link Matrix}, like scaling and
|
||||||
|
* rotating the video.
|
||||||
|
*
|
||||||
|
* <p>For now, resolution will not be affected by this method.
|
||||||
|
*
|
||||||
|
* @param transformationMatrix The transformation to apply to video frames.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
public Builder setTransformationMatrix(Matrix transformationMatrix) {
|
||||||
|
// TODO(Internal b/201293185): After {@link #setResolution} supports arbitrary resolutions,
|
||||||
|
// allow transformations to change the resolution, by scaling to the appropriate min/max
|
||||||
|
// values. This will also be required to create the VertexTransformation class, in order to
|
||||||
|
// have aspect ratio helper methods (which require resolution to change).
|
||||||
|
this.transformationMatrix = transformationMatrix;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated This feature will be removed in a following release and the MIME type of the
|
* @deprecated This feature will be removed in a following release and the MIME type of the
|
||||||
* output will always be MP4.
|
* output will always be MP4.
|
||||||
@ -411,6 +436,7 @@ public final class Transformer {
|
|||||||
removeVideo,
|
removeVideo,
|
||||||
flattenForSlowMotion,
|
flattenForSlowMotion,
|
||||||
outputHeight,
|
outputHeight,
|
||||||
|
transformationMatrix,
|
||||||
containerMimeType,
|
containerMimeType,
|
||||||
audioMimeType,
|
audioMimeType,
|
||||||
videoMimeType);
|
videoMimeType);
|
||||||
|
@ -68,10 +68,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Format inputFormat = checkNotNull(formatHolder.format);
|
Format inputFormat = checkNotNull(formatHolder.format);
|
||||||
if ((transformation.videoMimeType != null
|
if (shouldTranscode(inputFormat)) {
|
||||||
&& !transformation.videoMimeType.equals(inputFormat.sampleMimeType))
|
|
||||||
|| (transformation.outputHeight != Format.NO_VALUE
|
|
||||||
&& transformation.outputHeight != inputFormat.height)) {
|
|
||||||
samplePipeline = new VideoSamplePipeline(context, inputFormat, transformation, getIndex());
|
samplePipeline = new VideoSamplePipeline(context, inputFormat, transformation, getIndex());
|
||||||
} else {
|
} else {
|
||||||
samplePipeline = new PassthroughSamplePipeline(inputFormat);
|
samplePipeline = new PassthroughSamplePipeline(inputFormat);
|
||||||
@ -82,6 +79,21 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldTranscode(Format inputFormat) {
|
||||||
|
if (transformation.videoMimeType != null
|
||||||
|
&& !transformation.videoMimeType.equals(inputFormat.sampleMimeType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (transformation.outputHeight != Format.NO_VALUE
|
||||||
|
&& transformation.outputHeight != inputFormat.height) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!transformation.transformationMatrix.isIdentity()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queues the input buffer to the sample pipeline unless it should be dropped because of slow
|
* Queues the input buffer to the sample pipeline unless it should be dropped because of slow
|
||||||
* motion flattening.
|
* motion flattening.
|
||||||
|
@ -84,6 +84,7 @@ import java.io.IOException;
|
|||||||
context,
|
context,
|
||||||
outputWidth,
|
outputWidth,
|
||||||
outputHeight,
|
outputHeight,
|
||||||
|
transformation.transformationMatrix,
|
||||||
/* outputSurface= */ checkNotNull(encoder.getInputSurface()));
|
/* outputSurface= */ checkNotNull(encoder.getInputSurface()));
|
||||||
try {
|
try {
|
||||||
decoder =
|
decoder =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user