Add support for customizing overlay anchor point.

PiperOrigin-RevId: 496956035
This commit is contained in:
tofunmi 2022-12-21 18:40:30 +00:00 committed by Marc Baechinger
parent 890fd0a9fb
commit 4d1283f43e
4 changed files with 98 additions and 12 deletions

View File

@ -62,6 +62,8 @@ public class OverlayTextureProcessorPixelTest {
"media/bitmap/sample_mp4_first_frame/electrical_colors/original.png";
public static final String OVERLAY_BITMAP_DEFAULT =
"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 =
"media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_scaled.png";
public static final String OVERLAY_BITMAP_TRANSLUCENT =
@ -173,6 +175,33 @@ public class OverlayTextureProcessorPixelTest {
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
public void drawFrame_translucentBitmapOverlay_blendsBitmapIntoFrame() throws Exception {
String testId = "drawFrame_translucentBitmapOverlay";

View File

@ -16,6 +16,7 @@ package com.google.android.exoplayer2.effect;
*/
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import android.util.Pair;
import androidx.annotation.FloatRange;
import com.google.android.exoplayer2.util.GlUtil;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
@ -25,11 +26,13 @@ public final class OverlaySettings {
public final boolean useHdr;
public final float alpha;
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.alpha = alpha;
this.matrix = matrix;
this.anchor = anchor;
}
/** A builder for {@link OverlaySettings} instances. */
@ -37,10 +40,12 @@ public final class OverlaySettings {
private boolean useHdr;
private float alpha = 1;
private float[] matrix;
private Pair<Float, Float> anchor;
/** Creates a new {@link Builder}. */
public Builder() {
matrix = GlUtil.create4x4IdentityMatrix();
anchor = Pair.create(0f, 0f);
}
/**
@ -81,9 +86,33 @@ public final class OverlaySettings {
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. */
public OverlaySettings build() {
return new OverlaySettings(useHdr, alpha, matrix);
return new OverlaySettings(useHdr, alpha, matrix, anchor);
}
}
}

View File

@ -20,6 +20,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Pair;
import com.google.android.exoplayer2.util.FrameProcessingException;
import com.google.android.exoplayer2.util.GlProgram;
import com.google.android.exoplayer2.util.GlUtil;
@ -36,6 +37,8 @@ import com.google.common.collect.ImmutableList;
private final ImmutableList<TextureOverlay> overlays;
private final float[] aspectRatioMatrix;
private final float[] overlayMatrix;
private final float[] anchorMatrix;
private final float[] transformationMatrix;
private int videoWidth;
private int videoHeight;
@ -61,7 +64,8 @@ import com.google.common.collect.ImmutableList;
this.overlays = overlays;
aspectRatioMatrix = GlUtil.create4x4IdentityMatrix();
overlayMatrix = GlUtil.create4x4IdentityMatrix();
anchorMatrix = GlUtil.create4x4IdentityMatrix();
transformationMatrix = GlUtil.create4x4IdentityMatrix();
try {
glProgram =
new GlProgram(createVertexShader(overlays.size()), createFragmentShader(overlays.size()));
@ -89,10 +93,12 @@ import com.google.common.collect.ImmutableList;
if (!overlays.isEmpty()) {
for (int texUnitIndex = 1; texUnitIndex <= overlays.size(); texUnitIndex++) {
TextureOverlay overlay = overlays.get(texUnitIndex - 1);
glProgram.setSamplerTexIdUniform(
Util.formatInvariant("uOverlayTexSampler%d", texUnitIndex),
overlay.getTextureId(presentationTimeUs),
texUnitIndex);
GlUtil.setToIdentity(aspectRatioMatrix);
Matrix.scaleM(
aspectRatioMatrix,
@ -100,15 +106,40 @@ import com.google.common.collect.ImmutableList;
videoWidth / (float) overlay.getTextureSize(presentationTimeUs).getWidth(),
videoHeight / (float) overlay.getTextureSize(presentationTimeUs).getHeight(),
/* z= */ 1);
glProgram.setFloatsUniform(
Util.formatInvariant("uAspectRatioMatrix%d", texUnitIndex), aspectRatioMatrix);
Matrix.invertM(
overlayMatrix,
MATRIX_OFFSET,
overlay.getOverlaySettings(presentationTimeUs).matrix,
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(
Util.formatInvariant("uOverlayMatrix%d", texUnitIndex), overlayMatrix);
Util.formatInvariant("uTransformationMatrix%d", texUnitIndex), transformationMatrix);
glProgram.setFloatUniform(
Util.formatInvariant("uOverlayAlpha%d", texUnitIndex),
overlay.getOverlaySettings(presentationTimeUs).alpha);
@ -143,9 +174,8 @@ import com.google.common.collect.ImmutableList;
for (int texUnitIndex = 1; texUnitIndex <= numOverlays; texUnitIndex++) {
shader
.append(Util.formatInvariant("uniform mat4 uAspectRatioMatrix%d;\n", texUnitIndex))
.append(Util.formatInvariant("uniform mat4 uOverlayMatrix%d;\n", texUnitIndex))
.append(Util.formatInvariant("varying vec2 vOverlayTexSamplingCoord%d;\n", texUnitIndex));
.append(Util.formatInvariant("uniform mat4 uTransformationMatrix%s;\n", texUnitIndex))
.append(Util.formatInvariant("varying vec2 vOverlayTexSamplingCoord%s;\n", texUnitIndex));
}
shader
@ -160,9 +190,7 @@ import com.google.common.collect.ImmutableList;
shader
.append(Util.formatInvariant(" vec4 aOverlayPosition%d = \n", texUnitIndex))
.append(
Util.formatInvariant(
" uAspectRatioMatrix%d * uOverlayMatrix%d * aFramePosition;\n",
texUnitIndex, texUnitIndex))
Util.formatInvariant(" uTransformationMatrix%s * aFramePosition;\n", texUnitIndex))
.append(
Util.formatInvariant(
" vOverlayTexSamplingCoord%d = getTexSamplingCoord(aOverlayPosition%d.xy);\n",

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 KiB