Add support for customizing overlay anchor point.
PiperOrigin-RevId: 496956035
This commit is contained in:
parent
890fd0a9fb
commit
4d1283f43e
@ -62,6 +62,8 @@ public class OverlayTextureProcessorPixelTest {
|
|||||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/original.png";
|
"media/bitmap/sample_mp4_first_frame/electrical_colors/original.png";
|
||||||
public static final String OVERLAY_BITMAP_DEFAULT =
|
public static final String OVERLAY_BITMAP_DEFAULT =
|
||||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_default.png";
|
"media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_default.png";
|
||||||
|
public static final String OVERLAY_BITMAP_ANCHORED =
|
||||||
|
"media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_anchored.png";
|
||||||
public static final String OVERLAY_BITMAP_SCALED =
|
public static final String OVERLAY_BITMAP_SCALED =
|
||||||
"media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_scaled.png";
|
"media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_scaled.png";
|
||||||
public static final String OVERLAY_BITMAP_TRANSLUCENT =
|
public static final String OVERLAY_BITMAP_TRANSLUCENT =
|
||||||
@ -173,6 +175,33 @@ public class OverlayTextureProcessorPixelTest {
|
|||||||
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void drawFrame_anchoredBitmapOverlay_blendsBitmapIntoTopLeftOfFrame() throws Exception {
|
||||||
|
String testId = "drawFrame_anchoredBitmapOverlay";
|
||||||
|
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
||||||
|
float[] translateMatrix = GlUtil.create4x4IdentityMatrix();
|
||||||
|
Matrix.translateM(translateMatrix, /* mOffset= */ 0, /* x= */ -1f, /* y= */ 1f, /* z= */ 1);
|
||||||
|
OverlaySettings overlaySettings =
|
||||||
|
new OverlaySettings.Builder().setMatrix(translateMatrix).setAnchor(-1f, 1f).build();
|
||||||
|
BitmapOverlay staticBitmapOverlay =
|
||||||
|
BitmapOverlay.createStaticBitmapOverlay(overlayBitmap, overlaySettings);
|
||||||
|
overlayTextureProcessor =
|
||||||
|
new OverlayEffect(ImmutableList.of(staticBitmapOverlay))
|
||||||
|
.toGlTextureProcessor(context, /* useHdr= */ false);
|
||||||
|
Size outputSize = overlayTextureProcessor.configure(inputWidth, inputHeight);
|
||||||
|
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());
|
||||||
|
Bitmap expectedBitmap = readBitmap(OVERLAY_BITMAP_ANCHORED);
|
||||||
|
|
||||||
|
overlayTextureProcessor.drawFrame(inputTexId, /* presentationTimeUs= */ 0);
|
||||||
|
Bitmap actualBitmap =
|
||||||
|
createArgb8888BitmapFromCurrentGlFramebuffer(outputSize.getWidth(), outputSize.getHeight());
|
||||||
|
|
||||||
|
maybeSaveTestBitmapToCacheDirectory(testId, /* bitmapLabel= */ "actual", actualBitmap);
|
||||||
|
float averagePixelAbsoluteDifference =
|
||||||
|
getBitmapAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, actualBitmap, testId);
|
||||||
|
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void drawFrame_translucentBitmapOverlay_blendsBitmapIntoFrame() throws Exception {
|
public void drawFrame_translucentBitmapOverlay_blendsBitmapIntoFrame() throws Exception {
|
||||||
String testId = "drawFrame_translucentBitmapOverlay";
|
String testId = "drawFrame_translucentBitmapOverlay";
|
||||||
|
@ -16,6 +16,7 @@ package com.google.android.exoplayer2.effect;
|
|||||||
*/
|
*/
|
||||||
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
|
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
|
||||||
|
|
||||||
|
import android.util.Pair;
|
||||||
import androidx.annotation.FloatRange;
|
import androidx.annotation.FloatRange;
|
||||||
import com.google.android.exoplayer2.util.GlUtil;
|
import com.google.android.exoplayer2.util.GlUtil;
|
||||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
@ -25,11 +26,13 @@ public final class OverlaySettings {
|
|||||||
public final boolean useHdr;
|
public final boolean useHdr;
|
||||||
public final float alpha;
|
public final float alpha;
|
||||||
public final float[] matrix;
|
public final float[] matrix;
|
||||||
|
public final Pair<Float, Float> anchor;
|
||||||
|
|
||||||
private OverlaySettings(boolean useHdr, float alpha, float[] matrix) {
|
private OverlaySettings(boolean useHdr, float alpha, float[] matrix, Pair<Float, Float> anchor) {
|
||||||
this.useHdr = useHdr;
|
this.useHdr = useHdr;
|
||||||
this.alpha = alpha;
|
this.alpha = alpha;
|
||||||
this.matrix = matrix;
|
this.matrix = matrix;
|
||||||
|
this.anchor = anchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A builder for {@link OverlaySettings} instances. */
|
/** A builder for {@link OverlaySettings} instances. */
|
||||||
@ -37,10 +40,12 @@ public final class OverlaySettings {
|
|||||||
private boolean useHdr;
|
private boolean useHdr;
|
||||||
private float alpha = 1;
|
private float alpha = 1;
|
||||||
private float[] matrix;
|
private float[] matrix;
|
||||||
|
private Pair<Float, Float> anchor;
|
||||||
|
|
||||||
/** Creates a new {@link Builder}. */
|
/** Creates a new {@link Builder}. */
|
||||||
public Builder() {
|
public Builder() {
|
||||||
matrix = GlUtil.create4x4IdentityMatrix();
|
matrix = GlUtil.create4x4IdentityMatrix();
|
||||||
|
anchor = Pair.create(0f, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,9 +86,33 @@ public final class OverlaySettings {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the coordinates for the anchor point of the overlay.
|
||||||
|
*
|
||||||
|
* <p>The anchor point is the point inside the overlay that the overlay is positioned from.
|
||||||
|
*
|
||||||
|
* <p>The coordinates are specified in Normalised Device Coordinates (NDCs). Set to always
|
||||||
|
* return {@code (0,0)} (the center) by default.
|
||||||
|
*
|
||||||
|
* @param x the NDC x-coordinate.
|
||||||
|
* @param y the NDC y-coordinate.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setAnchor(
|
||||||
|
@FloatRange(from = -1, to = 1) float x, @FloatRange(from = -1, to = 1) float y) {
|
||||||
|
checkArgument(
|
||||||
|
-1 <= x && x <= 1,
|
||||||
|
"x needs to be specified in terms of NDCs which lie in the interval [-1, 1].");
|
||||||
|
checkArgument(
|
||||||
|
-1 <= y && y <= 1,
|
||||||
|
"y needs to be specified in terms of NDCs which lie in the interval [-1, 1].");
|
||||||
|
this.anchor = Pair.create(x, y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** Creates an instance of {@link OverlaySettings}, using defaults if values are unset. */
|
/** Creates an instance of {@link OverlaySettings}, using defaults if values are unset. */
|
||||||
public OverlaySettings build() {
|
public OverlaySettings build() {
|
||||||
return new OverlaySettings(useHdr, alpha, matrix);
|
return new OverlaySettings(useHdr, alpha, matrix, anchor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkArgument;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.opengl.GLES20;
|
import android.opengl.GLES20;
|
||||||
import android.opengl.Matrix;
|
import android.opengl.Matrix;
|
||||||
|
import android.util.Pair;
|
||||||
import com.google.android.exoplayer2.util.FrameProcessingException;
|
import com.google.android.exoplayer2.util.FrameProcessingException;
|
||||||
import com.google.android.exoplayer2.util.GlProgram;
|
import com.google.android.exoplayer2.util.GlProgram;
|
||||||
import com.google.android.exoplayer2.util.GlUtil;
|
import com.google.android.exoplayer2.util.GlUtil;
|
||||||
@ -36,6 +37,8 @@ import com.google.common.collect.ImmutableList;
|
|||||||
private final ImmutableList<TextureOverlay> overlays;
|
private final ImmutableList<TextureOverlay> overlays;
|
||||||
private final float[] aspectRatioMatrix;
|
private final float[] aspectRatioMatrix;
|
||||||
private final float[] overlayMatrix;
|
private final float[] overlayMatrix;
|
||||||
|
private final float[] anchorMatrix;
|
||||||
|
private final float[] transformationMatrix;
|
||||||
|
|
||||||
private int videoWidth;
|
private int videoWidth;
|
||||||
private int videoHeight;
|
private int videoHeight;
|
||||||
@ -61,7 +64,8 @@ import com.google.common.collect.ImmutableList;
|
|||||||
this.overlays = overlays;
|
this.overlays = overlays;
|
||||||
aspectRatioMatrix = GlUtil.create4x4IdentityMatrix();
|
aspectRatioMatrix = GlUtil.create4x4IdentityMatrix();
|
||||||
overlayMatrix = GlUtil.create4x4IdentityMatrix();
|
overlayMatrix = GlUtil.create4x4IdentityMatrix();
|
||||||
|
anchorMatrix = GlUtil.create4x4IdentityMatrix();
|
||||||
|
transformationMatrix = GlUtil.create4x4IdentityMatrix();
|
||||||
try {
|
try {
|
||||||
glProgram =
|
glProgram =
|
||||||
new GlProgram(createVertexShader(overlays.size()), createFragmentShader(overlays.size()));
|
new GlProgram(createVertexShader(overlays.size()), createFragmentShader(overlays.size()));
|
||||||
@ -89,10 +93,12 @@ import com.google.common.collect.ImmutableList;
|
|||||||
if (!overlays.isEmpty()) {
|
if (!overlays.isEmpty()) {
|
||||||
for (int texUnitIndex = 1; texUnitIndex <= overlays.size(); texUnitIndex++) {
|
for (int texUnitIndex = 1; texUnitIndex <= overlays.size(); texUnitIndex++) {
|
||||||
TextureOverlay overlay = overlays.get(texUnitIndex - 1);
|
TextureOverlay overlay = overlays.get(texUnitIndex - 1);
|
||||||
|
|
||||||
glProgram.setSamplerTexIdUniform(
|
glProgram.setSamplerTexIdUniform(
|
||||||
Util.formatInvariant("uOverlayTexSampler%d", texUnitIndex),
|
Util.formatInvariant("uOverlayTexSampler%d", texUnitIndex),
|
||||||
overlay.getTextureId(presentationTimeUs),
|
overlay.getTextureId(presentationTimeUs),
|
||||||
texUnitIndex);
|
texUnitIndex);
|
||||||
|
|
||||||
GlUtil.setToIdentity(aspectRatioMatrix);
|
GlUtil.setToIdentity(aspectRatioMatrix);
|
||||||
Matrix.scaleM(
|
Matrix.scaleM(
|
||||||
aspectRatioMatrix,
|
aspectRatioMatrix,
|
||||||
@ -100,15 +106,40 @@ import com.google.common.collect.ImmutableList;
|
|||||||
videoWidth / (float) overlay.getTextureSize(presentationTimeUs).getWidth(),
|
videoWidth / (float) overlay.getTextureSize(presentationTimeUs).getWidth(),
|
||||||
videoHeight / (float) overlay.getTextureSize(presentationTimeUs).getHeight(),
|
videoHeight / (float) overlay.getTextureSize(presentationTimeUs).getHeight(),
|
||||||
/* z= */ 1);
|
/* z= */ 1);
|
||||||
glProgram.setFloatsUniform(
|
|
||||||
Util.formatInvariant("uAspectRatioMatrix%d", texUnitIndex), aspectRatioMatrix);
|
|
||||||
Matrix.invertM(
|
Matrix.invertM(
|
||||||
overlayMatrix,
|
overlayMatrix,
|
||||||
MATRIX_OFFSET,
|
MATRIX_OFFSET,
|
||||||
overlay.getOverlaySettings(presentationTimeUs).matrix,
|
overlay.getOverlaySettings(presentationTimeUs).matrix,
|
||||||
MATRIX_OFFSET);
|
MATRIX_OFFSET);
|
||||||
|
Pair<Float, Float> overlayAnchor = overlay.getOverlaySettings(presentationTimeUs).anchor;
|
||||||
|
GlUtil.setToIdentity(anchorMatrix);
|
||||||
|
Matrix.translateM(
|
||||||
|
anchorMatrix,
|
||||||
|
/* mOffset= */ 0,
|
||||||
|
overlayAnchor.first
|
||||||
|
* overlay.getTextureSize(presentationTimeUs).getWidth()
|
||||||
|
/ videoWidth,
|
||||||
|
overlayAnchor.second
|
||||||
|
* overlay.getTextureSize(presentationTimeUs).getHeight()
|
||||||
|
/ videoHeight,
|
||||||
|
/* z= */ 1);
|
||||||
|
Matrix.multiplyMM(
|
||||||
|
transformationMatrix,
|
||||||
|
MATRIX_OFFSET,
|
||||||
|
overlayMatrix,
|
||||||
|
MATRIX_OFFSET,
|
||||||
|
anchorMatrix,
|
||||||
|
MATRIX_OFFSET);
|
||||||
|
Matrix.multiplyMM(
|
||||||
|
transformationMatrix,
|
||||||
|
MATRIX_OFFSET,
|
||||||
|
aspectRatioMatrix,
|
||||||
|
MATRIX_OFFSET,
|
||||||
|
transformationMatrix,
|
||||||
|
MATRIX_OFFSET);
|
||||||
glProgram.setFloatsUniform(
|
glProgram.setFloatsUniform(
|
||||||
Util.formatInvariant("uOverlayMatrix%d", texUnitIndex), overlayMatrix);
|
Util.formatInvariant("uTransformationMatrix%d", texUnitIndex), transformationMatrix);
|
||||||
|
|
||||||
glProgram.setFloatUniform(
|
glProgram.setFloatUniform(
|
||||||
Util.formatInvariant("uOverlayAlpha%d", texUnitIndex),
|
Util.formatInvariant("uOverlayAlpha%d", texUnitIndex),
|
||||||
overlay.getOverlaySettings(presentationTimeUs).alpha);
|
overlay.getOverlaySettings(presentationTimeUs).alpha);
|
||||||
@ -143,9 +174,8 @@ import com.google.common.collect.ImmutableList;
|
|||||||
|
|
||||||
for (int texUnitIndex = 1; texUnitIndex <= numOverlays; texUnitIndex++) {
|
for (int texUnitIndex = 1; texUnitIndex <= numOverlays; texUnitIndex++) {
|
||||||
shader
|
shader
|
||||||
.append(Util.formatInvariant("uniform mat4 uAspectRatioMatrix%d;\n", texUnitIndex))
|
.append(Util.formatInvariant("uniform mat4 uTransformationMatrix%s;\n", texUnitIndex))
|
||||||
.append(Util.formatInvariant("uniform mat4 uOverlayMatrix%d;\n", texUnitIndex))
|
.append(Util.formatInvariant("varying vec2 vOverlayTexSamplingCoord%s;\n", texUnitIndex));
|
||||||
.append(Util.formatInvariant("varying vec2 vOverlayTexSamplingCoord%d;\n", texUnitIndex));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shader
|
shader
|
||||||
@ -160,9 +190,7 @@ import com.google.common.collect.ImmutableList;
|
|||||||
shader
|
shader
|
||||||
.append(Util.formatInvariant(" vec4 aOverlayPosition%d = \n", texUnitIndex))
|
.append(Util.formatInvariant(" vec4 aOverlayPosition%d = \n", texUnitIndex))
|
||||||
.append(
|
.append(
|
||||||
Util.formatInvariant(
|
Util.formatInvariant(" uTransformationMatrix%s * aFramePosition;\n", texUnitIndex))
|
||||||
" uAspectRatioMatrix%d * uOverlayMatrix%d * aFramePosition;\n",
|
|
||||||
texUnitIndex, texUnitIndex))
|
|
||||||
.append(
|
.append(
|
||||||
Util.formatInvariant(
|
Util.formatInvariant(
|
||||||
" vOverlayTexSamplingCoord%d = getTexSamplingCoord(aOverlayPosition%d.xy);\n",
|
" vOverlayTexSamplingCoord%d = getTexSamplingCoord(aOverlayPosition%d.xy);\n",
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 545 KiB |
Loading…
x
Reference in New Issue
Block a user