mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Make OverlaySettings dynamic
PiperOrigin-RevId: 687269731
This commit is contained in:
parent
709246ac6a
commit
2a49ffcb23
19 changed files with 363 additions and 242 deletions
|
|
@ -154,6 +154,11 @@ This release includes the following changes since the
|
||||||
* Fix `IllegalStateException` from
|
* Fix `IllegalStateException` from
|
||||||
`DefaultDrmSession.requiresSecureDecoder` after opening a DRM session
|
`DefaultDrmSession.requiresSecureDecoder` after opening a DRM session
|
||||||
failed. This issue was introduced in `1.5.0-alpha01`.
|
failed. This issue was introduced in `1.5.0-alpha01`.
|
||||||
|
* Effect:
|
||||||
|
* Moved the functionality of `OverlaySettings` into
|
||||||
|
`StaticOverlaySettings`. `OverlaySettings` can be subclassed to allow
|
||||||
|
dynamic overlay settings.
|
||||||
|
* Muxers:
|
||||||
* IMA extension:
|
* IMA extension:
|
||||||
* Fix bug where server-side inserted DAI streams without a preroll can
|
* Fix bug where server-side inserted DAI streams without a preroll can
|
||||||
result in an `ArrayIndexOutOfBoundsException` when playing past the last
|
result in an `ArrayIndexOutOfBoundsException` when playing past the last
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import android.graphics.PorterDuff;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import androidx.media3.effect.CanvasOverlay;
|
import androidx.media3.effect.CanvasOverlay;
|
||||||
import androidx.media3.effect.OverlaySettings;
|
import androidx.media3.effect.OverlaySettings;
|
||||||
|
import androidx.media3.effect.StaticOverlaySettings;
|
||||||
|
|
||||||
/* package */ final class ClockOverlay extends CanvasOverlay {
|
/* package */ final class ClockOverlay extends CanvasOverlay {
|
||||||
private static final int CLOCK_COLOR = Color.WHITE;
|
private static final int CLOCK_COLOR = Color.WHITE;
|
||||||
|
|
@ -96,7 +97,7 @@ import androidx.media3.effect.OverlaySettings;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OverlaySettings getOverlaySettings(long presentationTimeUs) {
|
public OverlaySettings getOverlaySettings(long presentationTimeUs) {
|
||||||
return new OverlaySettings.Builder()
|
return new StaticOverlaySettings.Builder()
|
||||||
.setBackgroundFrameAnchor(
|
.setBackgroundFrameAnchor(
|
||||||
BOTTOM_RIGHT_ANCHOR_X - ANCHOR_INSET_X, BOTTOM_RIGHT_ANCHOR_Y - ANCHOR_INSET_Y)
|
BOTTOM_RIGHT_ANCHOR_X - ANCHOR_INSET_X, BOTTOM_RIGHT_ANCHOR_Y - ANCHOR_INSET_Y)
|
||||||
.setOverlayFrameAnchor(BOTTOM_RIGHT_ANCHOR_X, BOTTOM_RIGHT_ANCHOR_Y)
|
.setOverlayFrameAnchor(BOTTOM_RIGHT_ANCHOR_X, BOTTOM_RIGHT_ANCHOR_Y)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import android.text.SpannableString;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.effect.OverlaySettings;
|
import androidx.media3.effect.OverlaySettings;
|
||||||
|
import androidx.media3.effect.StaticOverlaySettings;
|
||||||
import androidx.media3.effect.TextOverlay;
|
import androidx.media3.effect.TextOverlay;
|
||||||
import androidx.media3.effect.TextureOverlay;
|
import androidx.media3.effect.TextureOverlay;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
@ -31,11 +32,11 @@ import java.util.Locale;
|
||||||
*/
|
*/
|
||||||
/* package */ final class TimerOverlay extends TextOverlay {
|
/* package */ final class TimerOverlay extends TextOverlay {
|
||||||
|
|
||||||
private final OverlaySettings overlaySettings;
|
private final StaticOverlaySettings overlaySettings;
|
||||||
|
|
||||||
public TimerOverlay() {
|
public TimerOverlay() {
|
||||||
overlaySettings =
|
overlaySettings =
|
||||||
new OverlaySettings.Builder()
|
new StaticOverlaySettings.Builder()
|
||||||
// Place the timer in the bottom left corner of the screen with some padding from the
|
// Place the timer in the bottom left corner of the screen with some padding from the
|
||||||
// edges.
|
// edges.
|
||||||
.setOverlayFrameAnchor(/* x= */ -1f, /* y= */ -1f)
|
.setOverlayFrameAnchor(/* x= */ -1f, /* y= */ -1f)
|
||||||
|
|
|
||||||
|
|
@ -71,13 +71,13 @@ import androidx.media3.effect.GlShaderProgram;
|
||||||
import androidx.media3.effect.HslAdjustment;
|
import androidx.media3.effect.HslAdjustment;
|
||||||
import androidx.media3.effect.LanczosResample;
|
import androidx.media3.effect.LanczosResample;
|
||||||
import androidx.media3.effect.OverlayEffect;
|
import androidx.media3.effect.OverlayEffect;
|
||||||
import androidx.media3.effect.OverlaySettings;
|
|
||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.effect.Presentation;
|
||||||
import androidx.media3.effect.RgbAdjustment;
|
import androidx.media3.effect.RgbAdjustment;
|
||||||
import androidx.media3.effect.RgbFilter;
|
import androidx.media3.effect.RgbFilter;
|
||||||
import androidx.media3.effect.RgbMatrix;
|
import androidx.media3.effect.RgbMatrix;
|
||||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||||
import androidx.media3.effect.SingleColorLut;
|
import androidx.media3.effect.SingleColorLut;
|
||||||
|
import androidx.media3.effect.StaticOverlaySettings;
|
||||||
import androidx.media3.effect.TextOverlay;
|
import androidx.media3.effect.TextOverlay;
|
||||||
import androidx.media3.effect.TextureOverlay;
|
import androidx.media3.effect.TextureOverlay;
|
||||||
import androidx.media3.exoplayer.DefaultLoadControl;
|
import androidx.media3.exoplayer.DefaultLoadControl;
|
||||||
|
|
@ -599,8 +599,8 @@ public final class TransformerActivity extends AppCompatActivity {
|
||||||
private OverlayEffect createOverlayEffectFromBundle(Bundle bundle, boolean[] selectedEffects) {
|
private OverlayEffect createOverlayEffectFromBundle(Bundle bundle, boolean[] selectedEffects) {
|
||||||
ImmutableList.Builder<TextureOverlay> overlaysBuilder = new ImmutableList.Builder<>();
|
ImmutableList.Builder<TextureOverlay> overlaysBuilder = new ImmutableList.Builder<>();
|
||||||
if (selectedEffects[ConfigurationActivity.OVERLAY_LOGO_AND_TIMER_INDEX]) {
|
if (selectedEffects[ConfigurationActivity.OVERLAY_LOGO_AND_TIMER_INDEX]) {
|
||||||
OverlaySettings logoSettings =
|
StaticOverlaySettings logoSettings =
|
||||||
new OverlaySettings.Builder()
|
new StaticOverlaySettings.Builder()
|
||||||
// Place the logo in the bottom left corner of the screen with some padding from the
|
// Place the logo in the bottom left corner of the screen with some padding from the
|
||||||
// edges.
|
// edges.
|
||||||
.setOverlayFrameAnchor(/* x= */ -1f, /* y= */ -1f)
|
.setOverlayFrameAnchor(/* x= */ -1f, /* y= */ -1f)
|
||||||
|
|
@ -619,8 +619,8 @@ public final class TransformerActivity extends AppCompatActivity {
|
||||||
overlaysBuilder.add(logoOverlay, timerOverlay);
|
overlaysBuilder.add(logoOverlay, timerOverlay);
|
||||||
}
|
}
|
||||||
if (selectedEffects[ConfigurationActivity.BITMAP_OVERLAY_INDEX]) {
|
if (selectedEffects[ConfigurationActivity.BITMAP_OVERLAY_INDEX]) {
|
||||||
OverlaySettings overlaySettings =
|
StaticOverlaySettings overlaySettings =
|
||||||
new OverlaySettings.Builder()
|
new StaticOverlaySettings.Builder()
|
||||||
.setAlphaScale(
|
.setAlphaScale(
|
||||||
bundle.getFloat(
|
bundle.getFloat(
|
||||||
ConfigurationActivity.BITMAP_OVERLAY_ALPHA, /* defaultValue= */ 1))
|
ConfigurationActivity.BITMAP_OVERLAY_ALPHA, /* defaultValue= */ 1))
|
||||||
|
|
@ -633,8 +633,8 @@ public final class TransformerActivity extends AppCompatActivity {
|
||||||
overlaysBuilder.add(bitmapOverlay);
|
overlaysBuilder.add(bitmapOverlay);
|
||||||
}
|
}
|
||||||
if (selectedEffects[ConfigurationActivity.TEXT_OVERLAY_INDEX]) {
|
if (selectedEffects[ConfigurationActivity.TEXT_OVERLAY_INDEX]) {
|
||||||
OverlaySettings overlaySettings =
|
StaticOverlaySettings overlaySettings =
|
||||||
new OverlaySettings.Builder()
|
new StaticOverlaySettings.Builder()
|
||||||
.setAlphaScale(
|
.setAlphaScale(
|
||||||
bundle.getFloat(ConfigurationActivity.TEXT_OVERLAY_ALPHA, /* defaultValue= */ 1))
|
bundle.getFloat(ConfigurationActivity.TEXT_OVERLAY_ALPHA, /* defaultValue= */ 1))
|
||||||
.build();
|
.build();
|
||||||
|
|
|
||||||
|
|
@ -169,8 +169,8 @@ public class OverlayShaderProgramPixelTest {
|
||||||
public void drawFrame_anchoredAndTranslatedBitmapOverlay_blendsBitmapIntoTopLeftOfFrame()
|
public void drawFrame_anchoredAndTranslatedBitmapOverlay_blendsBitmapIntoTopLeftOfFrame()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
||||||
OverlaySettings overlaySettings =
|
StaticOverlaySettings overlaySettings =
|
||||||
new OverlaySettings.Builder()
|
new StaticOverlaySettings.Builder()
|
||||||
.setOverlayFrameAnchor(/* x= */ -1f, /* y= */ 1f)
|
.setOverlayFrameAnchor(/* x= */ -1f, /* y= */ 1f)
|
||||||
.setBackgroundFrameAnchor(/* x= */ -1f, /* y= */ 1f)
|
.setBackgroundFrameAnchor(/* x= */ -1f, /* y= */ 1f)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -198,8 +198,10 @@ public class OverlayShaderProgramPixelTest {
|
||||||
drawFrame_overlayFrameAnchoredOnlyBitmapOverlay_anchorsOverlayFromTopLeftCornerOfFrame()
|
drawFrame_overlayFrameAnchoredOnlyBitmapOverlay_anchorsOverlayFromTopLeftCornerOfFrame()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
||||||
OverlaySettings overlaySettings =
|
StaticOverlaySettings overlaySettings =
|
||||||
new OverlaySettings.Builder().setOverlayFrameAnchor(/* x= */ -1f, /* y= */ 1f).build();
|
new StaticOverlaySettings.Builder()
|
||||||
|
.setOverlayFrameAnchor(/* x= */ -1f, /* y= */ 1f)
|
||||||
|
.build();
|
||||||
BitmapOverlay staticBitmapOverlay =
|
BitmapOverlay staticBitmapOverlay =
|
||||||
BitmapOverlay.createStaticBitmapOverlay(overlayBitmap, overlaySettings);
|
BitmapOverlay.createStaticBitmapOverlay(overlayBitmap, overlaySettings);
|
||||||
overlayShaderProgram =
|
overlayShaderProgram =
|
||||||
|
|
@ -222,7 +224,8 @@ public class OverlayShaderProgramPixelTest {
|
||||||
@Test
|
@Test
|
||||||
public void drawFrame_rotatedBitmapOverlay_blendsBitmapRotated90degrees() throws Exception {
|
public void drawFrame_rotatedBitmapOverlay_blendsBitmapRotated90degrees() throws Exception {
|
||||||
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
||||||
OverlaySettings overlaySettings = new OverlaySettings.Builder().setRotationDegrees(90f).build();
|
StaticOverlaySettings overlaySettings =
|
||||||
|
new StaticOverlaySettings.Builder().setRotationDegrees(90f).build();
|
||||||
BitmapOverlay staticBitmapOverlay =
|
BitmapOverlay staticBitmapOverlay =
|
||||||
BitmapOverlay.createStaticBitmapOverlay(overlayBitmap, overlaySettings);
|
BitmapOverlay.createStaticBitmapOverlay(overlayBitmap, overlaySettings);
|
||||||
overlayShaderProgram =
|
overlayShaderProgram =
|
||||||
|
|
@ -245,7 +248,8 @@ public class OverlayShaderProgramPixelTest {
|
||||||
@Test
|
@Test
|
||||||
public void drawFrame_translucentBitmapOverlay_blendsBitmapIntoFrame() throws Exception {
|
public void drawFrame_translucentBitmapOverlay_blendsBitmapIntoFrame() throws Exception {
|
||||||
Bitmap bitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
Bitmap bitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
||||||
OverlaySettings overlaySettings = new OverlaySettings.Builder().setAlphaScale(0.5f).build();
|
StaticOverlaySettings overlaySettings =
|
||||||
|
new StaticOverlaySettings.Builder().setAlphaScale(0.5f).build();
|
||||||
BitmapOverlay translucentBitmapOverlay =
|
BitmapOverlay translucentBitmapOverlay =
|
||||||
BitmapOverlay.createStaticBitmapOverlay(bitmap, overlaySettings);
|
BitmapOverlay.createStaticBitmapOverlay(bitmap, overlaySettings);
|
||||||
overlayShaderProgram =
|
overlayShaderProgram =
|
||||||
|
|
@ -268,7 +272,8 @@ public class OverlayShaderProgramPixelTest {
|
||||||
@Test
|
@Test
|
||||||
public void drawFrame_transparentTextOverlay_blendsBitmapIntoFrame() throws Exception {
|
public void drawFrame_transparentTextOverlay_blendsBitmapIntoFrame() throws Exception {
|
||||||
SpannableString overlayText = new SpannableString(/* source= */ "Text styling");
|
SpannableString overlayText = new SpannableString(/* source= */ "Text styling");
|
||||||
OverlaySettings overlaySettings = new OverlaySettings.Builder().setAlphaScale(0f).build();
|
StaticOverlaySettings overlaySettings =
|
||||||
|
new StaticOverlaySettings.Builder().setAlphaScale(0f).build();
|
||||||
overlayText.setSpan(
|
overlayText.setSpan(
|
||||||
new ForegroundColorSpan(Color.GRAY),
|
new ForegroundColorSpan(Color.GRAY),
|
||||||
/* start= */ 0,
|
/* start= */ 0,
|
||||||
|
|
@ -354,8 +359,8 @@ public class OverlayShaderProgramPixelTest {
|
||||||
/* start= */ 0,
|
/* start= */ 0,
|
||||||
/* end= */ 4,
|
/* end= */ 4,
|
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
OverlaySettings overlaySettings =
|
StaticOverlaySettings overlaySettings =
|
||||||
new OverlaySettings.Builder().setBackgroundFrameAnchor(0.5f, 0.5f).build();
|
new StaticOverlaySettings.Builder().setBackgroundFrameAnchor(0.5f, 0.5f).build();
|
||||||
TextOverlay staticTextOverlay =
|
TextOverlay staticTextOverlay =
|
||||||
TextOverlay.createStaticTextOverlay(overlayText, overlaySettings);
|
TextOverlay.createStaticTextOverlay(overlayText, overlaySettings);
|
||||||
overlayShaderProgram =
|
overlayShaderProgram =
|
||||||
|
|
@ -383,11 +388,12 @@ public class OverlayShaderProgramPixelTest {
|
||||||
/* start= */ 0,
|
/* start= */ 0,
|
||||||
/* end= */ 4,
|
/* end= */ 4,
|
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
OverlaySettings overlaySettings1 =
|
StaticOverlaySettings overlaySettings1 =
|
||||||
new OverlaySettings.Builder().setBackgroundFrameAnchor(0.5f, 0.5f).build();
|
new StaticOverlaySettings.Builder().setBackgroundFrameAnchor(0.5f, 0.5f).build();
|
||||||
TextOverlay textOverlay = TextOverlay.createStaticTextOverlay(overlayText, overlaySettings1);
|
TextOverlay textOverlay = TextOverlay.createStaticTextOverlay(overlayText, overlaySettings1);
|
||||||
Bitmap bitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
Bitmap bitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
||||||
OverlaySettings overlaySettings2 = new OverlaySettings.Builder().setAlphaScale(0.5f).build();
|
StaticOverlaySettings overlaySettings2 =
|
||||||
|
new StaticOverlaySettings.Builder().setAlphaScale(0.5f).build();
|
||||||
BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(bitmap, overlaySettings2);
|
BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(bitmap, overlaySettings2);
|
||||||
overlayShaderProgram =
|
overlayShaderProgram =
|
||||||
new OverlayEffect(ImmutableList.of(textOverlay, bitmapOverlay))
|
new OverlayEffect(ImmutableList.of(textOverlay, bitmapOverlay))
|
||||||
|
|
@ -414,12 +420,12 @@ public class OverlayShaderProgramPixelTest {
|
||||||
/* start= */ 0,
|
/* start= */ 0,
|
||||||
/* end= */ overlayText.length(),
|
/* end= */ overlayText.length(),
|
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
OverlaySettings overlaySettings1 =
|
StaticOverlaySettings overlaySettings1 =
|
||||||
new OverlaySettings.Builder().setScale(/* x= */ 0.5f, /* y= */ 0.5f).build();
|
new StaticOverlaySettings.Builder().setScale(/* x= */ 0.5f, /* y= */ 0.5f).build();
|
||||||
TextOverlay textOverlay = TextOverlay.createStaticTextOverlay(overlayText, overlaySettings1);
|
TextOverlay textOverlay = TextOverlay.createStaticTextOverlay(overlayText, overlaySettings1);
|
||||||
Bitmap bitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
Bitmap bitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
|
||||||
OverlaySettings overlaySettings2 =
|
StaticOverlaySettings overlaySettings2 =
|
||||||
new OverlaySettings.Builder().setScale(/* x= */ 3, /* y= */ 3).build();
|
new StaticOverlaySettings.Builder().setScale(/* x= */ 3, /* y= */ 3).build();
|
||||||
BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(bitmap, overlaySettings2);
|
BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(bitmap, overlaySettings2);
|
||||||
|
|
||||||
overlayShaderProgram =
|
overlayShaderProgram =
|
||||||
|
|
@ -461,14 +467,14 @@ public class OverlayShaderProgramPixelTest {
|
||||||
|
|
||||||
private static final class LetterBoxStretchedBitmapOverlay extends BitmapOverlay {
|
private static final class LetterBoxStretchedBitmapOverlay extends BitmapOverlay {
|
||||||
|
|
||||||
private final OverlaySettings.Builder overlaySettingsBuilder;
|
private final StaticOverlaySettings.Builder overlaySettingsBuilder;
|
||||||
private final Bitmap overlayBitmap;
|
private final Bitmap overlayBitmap;
|
||||||
|
|
||||||
private @MonotonicNonNull OverlaySettings overlaySettings;
|
private @MonotonicNonNull StaticOverlaySettings overlaySettings;
|
||||||
|
|
||||||
public LetterBoxStretchedBitmapOverlay(Bitmap overlayBitmap) {
|
public LetterBoxStretchedBitmapOverlay(Bitmap overlayBitmap) {
|
||||||
this.overlayBitmap = overlayBitmap;
|
this.overlayBitmap = overlayBitmap;
|
||||||
overlaySettingsBuilder = new OverlaySettings.Builder();
|
overlaySettingsBuilder = new StaticOverlaySettings.Builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ public abstract class BitmapOverlay extends TextureOverlay {
|
||||||
* the frames.
|
* the frames.
|
||||||
*/
|
*/
|
||||||
public static BitmapOverlay createStaticBitmapOverlay(
|
public static BitmapOverlay createStaticBitmapOverlay(
|
||||||
Bitmap overlayBitmap, OverlaySettings overlaySettings) {
|
Bitmap overlayBitmap, StaticOverlaySettings overlaySettings) {
|
||||||
return new BitmapOverlay() {
|
return new BitmapOverlay() {
|
||||||
@Override
|
@Override
|
||||||
public Bitmap getBitmap(long presentationTimeUs) {
|
public Bitmap getBitmap(long presentationTimeUs) {
|
||||||
|
|
@ -95,15 +95,15 @@ public abstract class BitmapOverlay extends TextureOverlay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link BitmapOverlay} that shows the input at {@code overlayBitmapUri} with the same
|
* Creates a {@link BitmapOverlay} that shows the input at {@code overlayBitmapUri} with the same
|
||||||
* {@link OverlaySettings} throughout the whole video.
|
* {@link StaticOverlaySettings} throughout the whole video.
|
||||||
*
|
*
|
||||||
* @param context The {@link Context}.
|
* @param context The {@link Context}.
|
||||||
* @param overlayBitmapUri The {@link Uri} pointing to the resource to be converted into a bitmap.
|
* @param overlayBitmapUri The {@link Uri} pointing to the resource to be converted into a bitmap.
|
||||||
* @param overlaySettings The {@link OverlaySettings} configuring how the overlay is displayed on
|
* @param overlaySettings The {@link StaticOverlaySettings} configuring how the overlay is
|
||||||
* the frames.
|
* displayed on the frames.
|
||||||
*/
|
*/
|
||||||
public static BitmapOverlay createStaticBitmapOverlay(
|
public static BitmapOverlay createStaticBitmapOverlay(
|
||||||
Context context, Uri overlayBitmapUri, OverlaySettings overlaySettings) {
|
Context context, Uri overlayBitmapUri, StaticOverlaySettings overlaySettings) {
|
||||||
return new BitmapOverlay() {
|
return new BitmapOverlay() {
|
||||||
private @MonotonicNonNull Bitmap lastBitmap;
|
private @MonotonicNonNull Bitmap lastBitmap;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -524,7 +524,7 @@ public final class DefaultVideoCompositor implements VideoCompositor {
|
||||||
/* overlaySize= */ new Size(inputTexture.width, inputTexture.height),
|
/* overlaySize= */ new Size(inputTexture.width, inputTexture.height),
|
||||||
inputFrameInfo.overlaySettings);
|
inputFrameInfo.overlaySettings);
|
||||||
glProgram.setFloatsUniform("uTransformationMatrix", transformationMatrix);
|
glProgram.setFloatsUniform("uTransformationMatrix", transformationMatrix);
|
||||||
glProgram.setFloatUniform("uAlphaScale", inputFrameInfo.overlaySettings.alphaScale);
|
glProgram.setFloatUniform("uAlphaScale", inputFrameInfo.overlaySettings.getAlphaScale());
|
||||||
glProgram.bindAttributesAndUniforms();
|
glProgram.bindAttributesAndUniforms();
|
||||||
|
|
||||||
// The four-vertex triangle strip forms a quad.
|
// The four-vertex triangle strip forms a quad.
|
||||||
|
|
|
||||||
|
|
@ -71,14 +71,14 @@ public abstract class DrawableOverlay extends BitmapOverlay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link DrawableOverlay} that shows the {@link Drawable} with the same {@link
|
* Creates a {@link DrawableOverlay} that shows the {@link Drawable} with the same {@link
|
||||||
* OverlaySettings} throughout the whole video.
|
* StaticOverlaySettings} throughout the whole video.
|
||||||
*
|
*
|
||||||
* @param drawable The {@link Drawable} to be displayed.
|
* @param drawable The {@link Drawable} to be displayed.
|
||||||
* @param overlaySettings The {@link OverlaySettings} configuring how the overlay is displayed on
|
* @param overlaySettings The {@link StaticOverlaySettings} configuring how the overlay is
|
||||||
* the frames.
|
* displayed on the frames.
|
||||||
*/
|
*/
|
||||||
public static DrawableOverlay createStaticDrawableOverlay(
|
public static DrawableOverlay createStaticDrawableOverlay(
|
||||||
Drawable drawable, OverlaySettings overlaySettings) {
|
Drawable drawable, StaticOverlaySettings overlaySettings) {
|
||||||
return new DrawableOverlay() {
|
return new DrawableOverlay() {
|
||||||
@Override
|
@Override
|
||||||
public Drawable getDrawable(long presentationTimeUs) {
|
public Drawable getDrawable(long presentationTimeUs) {
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
// Anchor point of overlay within output frame.
|
// Anchor point of overlay within output frame.
|
||||||
Pair<Float, Float> backgroundFrameAnchor = overlaySettings.backgroundFrameAnchor;
|
Pair<Float, Float> backgroundFrameAnchor = overlaySettings.getBackgroundFrameAnchor();
|
||||||
Matrix.translateM(
|
Matrix.translateM(
|
||||||
backgroundFrameAnchorMatrix,
|
backgroundFrameAnchorMatrix,
|
||||||
MATRIX_OFFSET,
|
MATRIX_OFFSET,
|
||||||
|
|
@ -79,13 +79,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
/* z= */ 1f);
|
/* z= */ 1f);
|
||||||
|
|
||||||
// Scale the image.
|
// Scale the image.
|
||||||
Pair<Float, Float> scale = overlaySettings.scale;
|
Pair<Float, Float> scale = overlaySettings.getScale();
|
||||||
Matrix.scaleM(scaleMatrix, MATRIX_OFFSET, scale.first, scale.second, /* z= */ 1f);
|
Matrix.scaleM(scaleMatrix, MATRIX_OFFSET, scale.first, scale.second, /* z= */ 1f);
|
||||||
Matrix.invertM(scaleMatrixInv, MATRIX_OFFSET, scaleMatrix, MATRIX_OFFSET);
|
Matrix.invertM(scaleMatrixInv, MATRIX_OFFSET, scaleMatrix, MATRIX_OFFSET);
|
||||||
|
|
||||||
// Translate the overlay within its frame. To position the overlay frame's anchor at the correct
|
// Translate the overlay within its frame. To position the overlay frame's anchor at the correct
|
||||||
// position, it must be translated the opposite direction by the same magnitude.
|
// position, it must be translated the opposite direction by the same magnitude.
|
||||||
Pair<Float, Float> overlayFrameAnchor = overlaySettings.overlayFrameAnchor;
|
Pair<Float, Float> overlayFrameAnchor = overlaySettings.getOverlayFrameAnchor();
|
||||||
Matrix.translateM(
|
Matrix.translateM(
|
||||||
overlayFrameAnchorMatrix,
|
overlayFrameAnchorMatrix,
|
||||||
MATRIX_OFFSET,
|
MATRIX_OFFSET,
|
||||||
|
|
@ -97,7 +97,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
Matrix.rotateM(
|
Matrix.rotateM(
|
||||||
rotateMatrix,
|
rotateMatrix,
|
||||||
MATRIX_OFFSET,
|
MATRIX_OFFSET,
|
||||||
overlaySettings.rotationDegrees,
|
overlaySettings.getRotationDegrees(),
|
||||||
/* x= */ 0f,
|
/* x= */ 0f,
|
||||||
/* y= */ 0f,
|
/* y= */ 0f,
|
||||||
/* z= */ 1f);
|
/* z= */ 1f);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package androidx.media3.effect;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2022 The Android Open Source Project
|
* Copyright 2024 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -15,199 +13,121 @@ package androidx.media3.effect;
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
|
||||||
|
package androidx.media3.effect;
|
||||||
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import androidx.annotation.FloatRange;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains information to control how an input texture (for example, a {@link VideoCompositor} or
|
* Provides information of how an input texture (for example, a {@link TextureOverlay} or in {@link
|
||||||
* {@link TextureOverlay}) is displayed on a background.
|
* VideoCompositor}) is presented.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class OverlaySettings {
|
public interface OverlaySettings {
|
||||||
|
|
||||||
/** A builder for {@link OverlaySettings} instances. */
|
/** The default alpha scale value of the overlay. */
|
||||||
public static final class Builder {
|
float DEFAULT_ALPHA_SCALE = 1f;
|
||||||
private float alphaScale;
|
|
||||||
private Pair<Float, Float> backgroundFrameAnchor;
|
|
||||||
private Pair<Float, Float> overlayFrameAnchor;
|
|
||||||
private Pair<Float, Float> scale;
|
|
||||||
private float rotationDegrees;
|
|
||||||
private float hdrLuminanceMultiplier;
|
|
||||||
|
|
||||||
/** Creates a new {@link Builder}. */
|
/** The default coordinates for the anchor point of the overlay within the background frame. */
|
||||||
public Builder() {
|
Pair<Float, Float> DEFAULT_BACKGROUND_FRAME_ANCHOR = Pair.create(0f, 0f);
|
||||||
alphaScale = 1f;
|
|
||||||
backgroundFrameAnchor = Pair.create(0f, 0f);
|
|
||||||
overlayFrameAnchor = Pair.create(0f, 0f);
|
|
||||||
scale = Pair.create(1f, 1f);
|
|
||||||
rotationDegrees = 0f;
|
|
||||||
hdrLuminanceMultiplier = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Builder(OverlaySettings overlaySettings) {
|
/** The default coordinates for the anchor point of the overlay frame. */
|
||||||
this.alphaScale = overlaySettings.alphaScale;
|
Pair<Float, Float> DEFAULT_OVERLAY_FRAME_ANCHOR = Pair.create(0f, 0f);
|
||||||
this.backgroundFrameAnchor = overlaySettings.backgroundFrameAnchor;
|
|
||||||
this.overlayFrameAnchor = overlaySettings.overlayFrameAnchor;
|
|
||||||
this.scale = overlaySettings.scale;
|
|
||||||
this.rotationDegrees = overlaySettings.rotationDegrees;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/** The default scaling of the overlay. */
|
||||||
* Sets the alpha scale value of the overlay, altering its translucency.
|
Pair<Float, Float> DEFAULT_SCALE = Pair.create(1f, 1f);
|
||||||
*
|
|
||||||
* <p>An {@code alphaScale} value of {@code 1} means no change is applied. A value below {@code
|
|
||||||
* 1} increases translucency, and a value above {@code 1} reduces translucency.
|
|
||||||
*
|
|
||||||
* <p>Set to always return {@code 1} by default.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setAlphaScale(@FloatRange(from = 0) float alphaScale) {
|
|
||||||
checkArgument(0 <= alphaScale, "alphaScale needs to be greater than or equal to zero.");
|
|
||||||
this.alphaScale = alphaScale;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/** The default rotation of the overlay, counter-clockwise. */
|
||||||
* Sets the coordinates for the anchor point of the overlay within the background frame.
|
float DEFAULT_ROTATION_DEGREES = 0f;
|
||||||
*
|
|
||||||
* <p>The coordinates are specified in Normalised Device Coordinates (NDCs) relative to the
|
|
||||||
* background frame. The default value is {@code (0,0)}, the center of the background frame.
|
|
||||||
*
|
|
||||||
* <p>The overlay's {@linkplain #setOverlayFrameAnchor(float, float) anchor point} will be
|
|
||||||
* positioned at the anchor point set in this method. For example, setting a value of {@code
|
|
||||||
* (+1,+1)} will move the {@linkplain #setOverlayFrameAnchor overlay's anchor} to the top right
|
|
||||||
* corner. That is, if the overlay's anchor is at {@code (+1,+1)} (the top right corner), the
|
|
||||||
* overlay's top right corner will be aligned with that of the background frame; whereas if the
|
|
||||||
* overlay's anchor is at {@code (0,0)} (the center), the overlay's center will be positioned at
|
|
||||||
* the top right corner of the background frame.
|
|
||||||
*
|
|
||||||
* @param x The NDC x-coordinate in the range [-1, 1].
|
|
||||||
* @param y The NDC y-coordinate in the range [-1, 1].
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setBackgroundFrameAnchor(
|
|
||||||
@FloatRange(from = -1, to = 1) float x, @FloatRange(from = -1, to = 1) float y) {
|
|
||||||
checkArgument(-1 <= x && x <= 1);
|
|
||||||
checkArgument(-1 <= y && y <= 1);
|
|
||||||
this.backgroundFrameAnchor = Pair.create(x, y);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/** The default luminance multiplier of an SDR overlay when overlaid on a HDR frame. */
|
||||||
* Sets the coordinates for the anchor point within the overlay.
|
float DEFAULT_HDR_LUMINANCE_MULTIPLIER = 1f;
|
||||||
*
|
|
||||||
* <p>The anchor point is the point inside the overlay that is placed on the {@linkplain
|
|
||||||
* #setBackgroundFrameAnchor background frame anchor}
|
|
||||||
*
|
|
||||||
* <p>The coordinates are specified in Normalised Device Coordinates (NDCs) relative to the
|
|
||||||
* overlay. The default value is {@code (0,0)}, the center of the overlay.
|
|
||||||
*
|
|
||||||
* <p>See {@link #setBackgroundFrameAnchor} for examples of how to position an overlay.
|
|
||||||
*
|
|
||||||
* @param x The NDC x-coordinate in the range [-1, 1].
|
|
||||||
* @param y The NDC y-coordinate in the range [-1, 1].
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setOverlayFrameAnchor(
|
|
||||||
@FloatRange(from = -1, to = 1) float x, @FloatRange(from = -1, to = 1) float y) {
|
|
||||||
checkArgument(-1 <= x && x <= 1);
|
|
||||||
checkArgument(-1 <= y && y <= 1);
|
|
||||||
this.overlayFrameAnchor = Pair.create(x, y);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the scaling of the overlay.
|
* Returns the alpha scale value of the overlay, altering its translucency.
|
||||||
*
|
*
|
||||||
* @param x The desired scaling in the x axis of the overlay.
|
* <p>An {@code alphaScale} value of {@code 1} means no change is applied. A value below {@code 1}
|
||||||
* @param y The desired scaling in the y axis of the overlay.
|
* increases translucency, and a value above {@code 1} reduces translucency.
|
||||||
*/
|
*
|
||||||
@CanIgnoreReturnValue
|
* <p>The default value is {@link #DEFAULT_ALPHA_SCALE}.
|
||||||
public Builder setScale(float x, float y) {
|
*/
|
||||||
this.scale = Pair.create(x, y);
|
default float getAlphaScale() {
|
||||||
return this;
|
return DEFAULT_ALPHA_SCALE;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the rotation of the overlay, counter-clockwise.
|
|
||||||
*
|
|
||||||
* <p>The overlay is rotated at the center of its frame.
|
|
||||||
*
|
|
||||||
* @param rotationDegree The desired degrees of rotation, counter-clockwise.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setRotationDegrees(float rotationDegree) {
|
|
||||||
this.rotationDegrees = rotationDegree;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the luminance multiplier of an SDR overlay when overlaid on a HDR frame.
|
|
||||||
*
|
|
||||||
* <p>Scales the luminance of the overlay to adjust the output brightness of the overlay on the
|
|
||||||
* frame. The default value is 1, which scales the overlay colors into the standard HDR
|
|
||||||
* luminance within the processing pipeline. Use 0.5 to scale the luminance of the overlay to
|
|
||||||
* SDR range, so that no extra luminance is added.
|
|
||||||
*
|
|
||||||
* <p>Currently only supported on text overlays
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setHdrLuminanceMultiplier(float hdrLuminanceMultiplier) {
|
|
||||||
this.hdrLuminanceMultiplier = hdrLuminanceMultiplier;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates an instance of {@link OverlaySettings}, using defaults if values are unset. */
|
|
||||||
public OverlaySettings build() {
|
|
||||||
return new OverlaySettings(
|
|
||||||
alphaScale,
|
|
||||||
backgroundFrameAnchor,
|
|
||||||
overlayFrameAnchor,
|
|
||||||
scale,
|
|
||||||
rotationDegrees,
|
|
||||||
hdrLuminanceMultiplier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The alpha scale value of the overlay, altering its translucency. */
|
/**
|
||||||
public final float alphaScale;
|
* Returns the coordinates for the anchor point of the overlay within the background frame.
|
||||||
|
*
|
||||||
/** The coordinates for the anchor point of the overlay within the background frame. */
|
* <p>The coordinates are specified in Normalised Device Coordinates (NDCs) relative to the
|
||||||
public final Pair<Float, Float> backgroundFrameAnchor;
|
* background frame. The ranges for x and y are from {@code -1} to {@code 1}. The default value is
|
||||||
|
* {@code (0,0)}, the center of the background frame.
|
||||||
/** The coordinates for the anchor point of the overlay frame. */
|
*
|
||||||
public final Pair<Float, Float> overlayFrameAnchor;
|
* <p>The overlay's {@linkplain #getOverlayFrameAnchor anchor point} will be positioned at the
|
||||||
|
* anchor point returned from this method. For example, a value of {@code (1,1)} will move the
|
||||||
/** The scaling of the overlay. */
|
* {@linkplain #getOverlayFrameAnchor overlay's anchor} to the top right corner. That is, if the
|
||||||
public final Pair<Float, Float> scale;
|
* overlay's anchor is at {@code (1,1)} (the top right corner), the overlay's top right corner
|
||||||
|
* will be aligned with that of the background frame; whereas if the overlay's anchor is at {@code
|
||||||
/** The rotation of the overlay, counter-clockwise. */
|
* (0,0)} (the center), the overlay's center will be positioned at the top right corner of the
|
||||||
public final float rotationDegrees;
|
* background frame.
|
||||||
|
*
|
||||||
/** The luminance multiplier of an SDR overlay when overlaid on a HDR frame. */
|
* <p>The default value is {@link #DEFAULT_BACKGROUND_FRAME_ANCHOR}.
|
||||||
public final float hdrLuminanceMultiplier;
|
*/
|
||||||
|
default Pair<Float, Float> getBackgroundFrameAnchor() {
|
||||||
private OverlaySettings(
|
return DEFAULT_BACKGROUND_FRAME_ANCHOR;
|
||||||
float alphaScale,
|
|
||||||
Pair<Float, Float> backgroundFrameAnchor,
|
|
||||||
Pair<Float, Float> overlayFrameAnchor,
|
|
||||||
Pair<Float, Float> scale,
|
|
||||||
float rotationDegrees,
|
|
||||||
float hdrLuminanceMultiplier) {
|
|
||||||
this.alphaScale = alphaScale;
|
|
||||||
this.backgroundFrameAnchor = backgroundFrameAnchor;
|
|
||||||
this.overlayFrameAnchor = overlayFrameAnchor;
|
|
||||||
this.scale = scale;
|
|
||||||
this.rotationDegrees = rotationDegrees;
|
|
||||||
this.hdrLuminanceMultiplier = hdrLuminanceMultiplier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a new {@link Builder} initialized with the values of this instance. */
|
/**
|
||||||
/* package */ Builder buildUpon() {
|
* Returns the coordinates for the anchor point within the overlay.
|
||||||
return new Builder(this);
|
*
|
||||||
|
* <p>The anchor point is the point inside the overlay that is placed on the {@linkplain
|
||||||
|
* #getBackgroundFrameAnchor background frame anchor}
|
||||||
|
*
|
||||||
|
* <p>The coordinates are specified in Normalised Device Coordinates (NDCs) relative to the
|
||||||
|
* overlay. The ranges for x and y are from {@code -1} to {@code 1}. The default value is {@code
|
||||||
|
* (0,0)}, the center of the overlay.
|
||||||
|
*
|
||||||
|
* <p>See {@link #getBackgroundFrameAnchor} for examples of how to position an overlay.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link #DEFAULT_OVERLAY_FRAME_ANCHOR}.
|
||||||
|
*/
|
||||||
|
default Pair<Float, Float> getOverlayFrameAnchor() {
|
||||||
|
return DEFAULT_OVERLAY_FRAME_ANCHOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scaling of the overlay.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link #DEFAULT_SCALE}.
|
||||||
|
*/
|
||||||
|
default Pair<Float, Float> getScale() {
|
||||||
|
return DEFAULT_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the rotation of the overlay, counter-clockwise.
|
||||||
|
*
|
||||||
|
* <p>The overlay is rotated at the center of its frame.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link #DEFAULT_ROTATION_DEGREES}.
|
||||||
|
*/
|
||||||
|
default float getRotationDegrees() {
|
||||||
|
return DEFAULT_ROTATION_DEGREES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the luminance multiplier of an SDR overlay when overlaid on a HDR frame.
|
||||||
|
*
|
||||||
|
* <p>Scales the luminance of the overlay to adjust the output brightness of the overlay on the
|
||||||
|
* frame. The default value is 1, which scales the overlay colors into the standard HDR luminance
|
||||||
|
* within the processing pipeline. Use 0.5 to scale the luminance of the overlay to SDR range, so
|
||||||
|
* that no extra luminance is added.
|
||||||
|
*
|
||||||
|
* <p>Currently only supported on text overlays
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link #DEFAULT_HDR_LUMINANCE_MULTIPLIER}.
|
||||||
|
*/
|
||||||
|
default float getHdrLuminanceMultiplier() {
|
||||||
|
return DEFAULT_HDR_LUMINANCE_MULTIPLIER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ import java.io.IOException;
|
||||||
} else if (hdrTypes[texUnitIndex - 1] == HDR_TYPE_TEXT) {
|
} else if (hdrTypes[texUnitIndex - 1] == HDR_TYPE_TEXT) {
|
||||||
float[] luminanceMatrix = GlUtil.create4x4IdentityMatrix();
|
float[] luminanceMatrix = GlUtil.create4x4IdentityMatrix();
|
||||||
float multiplier =
|
float multiplier =
|
||||||
overlay.getOverlaySettings(presentationTimeUs).hdrLuminanceMultiplier;
|
overlay.getOverlaySettings(presentationTimeUs).getHdrLuminanceMultiplier();
|
||||||
Matrix.scaleM(luminanceMatrix, /* mOffset= */ 0, multiplier, multiplier, multiplier);
|
Matrix.scaleM(luminanceMatrix, /* mOffset= */ 0, multiplier, multiplier, multiplier);
|
||||||
glProgram.setFloatsUniform(
|
glProgram.setFloatsUniform(
|
||||||
formatInvariant("uLuminanceMatrix%d", texUnitIndex), luminanceMatrix);
|
formatInvariant("uLuminanceMatrix%d", texUnitIndex), luminanceMatrix);
|
||||||
|
|
@ -169,7 +169,7 @@ import java.io.IOException;
|
||||||
formatInvariant("uTransformationMatrix%d", texUnitIndex),
|
formatInvariant("uTransformationMatrix%d", texUnitIndex),
|
||||||
samplerOverlayMatrixProvider.getTransformationMatrix(overlaySize, overlaySettings));
|
samplerOverlayMatrixProvider.getTransformationMatrix(overlaySize, overlaySettings));
|
||||||
glProgram.setFloatUniform(
|
glProgram.setFloatUniform(
|
||||||
formatInvariant("uOverlayAlphaScale%d", texUnitIndex), overlaySettings.alphaScale);
|
formatInvariant("uOverlayAlphaScale%d", texUnitIndex), overlaySettings.getAlphaScale());
|
||||||
}
|
}
|
||||||
|
|
||||||
glProgram.setSamplerTexIdUniform("uVideoTexSampler0", inputTexId, /* texUnitIndex= */ 0);
|
glProgram.setSamplerTexIdUniform("uVideoTexSampler0", inputTexId, /* texUnitIndex= */ 0);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
package androidx.media3.effect;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.
|
||||||
|
*/
|
||||||
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
|
|
||||||
|
import android.util.Pair;
|
||||||
|
import androidx.annotation.FloatRange;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
|
|
||||||
|
/** A statically valued {@link OverlaySettings}. */
|
||||||
|
@UnstableApi
|
||||||
|
public final class StaticOverlaySettings implements OverlaySettings {
|
||||||
|
|
||||||
|
/** A builder for {@link StaticOverlaySettings} instances. */
|
||||||
|
public static final class Builder {
|
||||||
|
private float alphaScale;
|
||||||
|
private Pair<Float, Float> backgroundFrameAnchor;
|
||||||
|
private Pair<Float, Float> overlayFrameAnchor;
|
||||||
|
private Pair<Float, Float> scale;
|
||||||
|
private float rotationDegrees;
|
||||||
|
private float hdrLuminanceMultiplier;
|
||||||
|
|
||||||
|
/** Creates a new {@link Builder}. */
|
||||||
|
public Builder() {
|
||||||
|
alphaScale = DEFAULT_ALPHA_SCALE;
|
||||||
|
backgroundFrameAnchor = DEFAULT_BACKGROUND_FRAME_ANCHOR;
|
||||||
|
overlayFrameAnchor = DEFAULT_OVERLAY_FRAME_ANCHOR;
|
||||||
|
scale = DEFAULT_SCALE;
|
||||||
|
rotationDegrees = DEFAULT_ROTATION_DEGREES;
|
||||||
|
hdrLuminanceMultiplier = DEFAULT_HDR_LUMINANCE_MULTIPLIER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the alpha scale value of the overlay, altering its translucency.
|
||||||
|
*
|
||||||
|
* @see OverlaySettings#getAlphaScale()
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setAlphaScale(@FloatRange(from = 0) float alphaScale) {
|
||||||
|
checkArgument(0 <= alphaScale, "alphaScale needs to be greater than or equal to zero.");
|
||||||
|
this.alphaScale = alphaScale;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the coordinates for the anchor point of the overlay within the background frame.
|
||||||
|
*
|
||||||
|
* @see OverlaySettings#getBackgroundFrameAnchor()
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setBackgroundFrameAnchor(
|
||||||
|
@FloatRange(from = -1, to = 1) float x, @FloatRange(from = -1, to = 1) float y) {
|
||||||
|
checkArgument(-1 <= x && x <= 1);
|
||||||
|
checkArgument(-1 <= y && y <= 1);
|
||||||
|
this.backgroundFrameAnchor = Pair.create(x, y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the coordinates for the anchor point within the overlay.
|
||||||
|
*
|
||||||
|
* @see OverlaySettings#getOverlayFrameAnchor()
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setOverlayFrameAnchor(
|
||||||
|
@FloatRange(from = -1, to = 1) float x, @FloatRange(from = -1, to = 1) float y) {
|
||||||
|
checkArgument(-1 <= x && x <= 1);
|
||||||
|
checkArgument(-1 <= y && y <= 1);
|
||||||
|
this.overlayFrameAnchor = Pair.create(x, y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scaling of the overlay.
|
||||||
|
*
|
||||||
|
* @see OverlaySettings#getScale()
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setScale(float x, float y) {
|
||||||
|
this.scale = Pair.create(x, y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the rotation of the overlay, counter-clockwise.
|
||||||
|
*
|
||||||
|
* @see OverlaySettings#getRotationDegrees()
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setRotationDegrees(float rotationDegree) {
|
||||||
|
this.rotationDegrees = rotationDegree;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the luminance multiplier of an SDR overlay when overlaid on a HDR frame.
|
||||||
|
*
|
||||||
|
* @see OverlaySettings#getHdrLuminanceMultiplier()
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setHdrLuminanceMultiplier(float hdrLuminanceMultiplier) {
|
||||||
|
this.hdrLuminanceMultiplier = hdrLuminanceMultiplier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates an instance of {@link StaticOverlaySettings}, using defaults if values are unset. */
|
||||||
|
public StaticOverlaySettings build() {
|
||||||
|
return new StaticOverlaySettings(
|
||||||
|
alphaScale,
|
||||||
|
backgroundFrameAnchor,
|
||||||
|
overlayFrameAnchor,
|
||||||
|
scale,
|
||||||
|
rotationDegrees,
|
||||||
|
hdrLuminanceMultiplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final float alphaScale;
|
||||||
|
private final Pair<Float, Float> backgroundFrameAnchor;
|
||||||
|
private final Pair<Float, Float> overlayFrameAnchor;
|
||||||
|
private final Pair<Float, Float> scale;
|
||||||
|
private final float rotationDegrees;
|
||||||
|
private final float hdrLuminanceMultiplier;
|
||||||
|
|
||||||
|
private StaticOverlaySettings(
|
||||||
|
float alphaScale,
|
||||||
|
Pair<Float, Float> backgroundFrameAnchor,
|
||||||
|
Pair<Float, Float> overlayFrameAnchor,
|
||||||
|
Pair<Float, Float> scale,
|
||||||
|
float rotationDegrees,
|
||||||
|
float hdrLuminanceMultiplier) {
|
||||||
|
this.alphaScale = alphaScale;
|
||||||
|
this.backgroundFrameAnchor = backgroundFrameAnchor;
|
||||||
|
this.overlayFrameAnchor = overlayFrameAnchor;
|
||||||
|
this.scale = scale;
|
||||||
|
this.rotationDegrees = rotationDegrees;
|
||||||
|
this.hdrLuminanceMultiplier = hdrLuminanceMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getAlphaScale() {
|
||||||
|
return alphaScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<Float, Float> getBackgroundFrameAnchor() {
|
||||||
|
return backgroundFrameAnchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<Float, Float> getOverlayFrameAnchor() {
|
||||||
|
return overlayFrameAnchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<Float, Float> getScale() {
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getRotationDegrees() {
|
||||||
|
return rotationDegrees;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getHdrLuminanceMultiplier() {
|
||||||
|
return hdrLuminanceMultiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -43,7 +43,7 @@ public abstract class TextOverlay extends BitmapOverlay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link TextOverlay} that shows the {@code overlayText} with the same default settings
|
* Creates a {@link TextOverlay} that shows the {@code overlayText} with the same default settings
|
||||||
* in {@link OverlaySettings} throughout the whole video.
|
* in {@link StaticOverlaySettings} throughout the whole video.
|
||||||
*/
|
*/
|
||||||
public static TextOverlay createStaticTextOverlay(SpannableString overlayText) {
|
public static TextOverlay createStaticTextOverlay(SpannableString overlayText) {
|
||||||
return new TextOverlay() {
|
return new TextOverlay() {
|
||||||
|
|
@ -56,14 +56,14 @@ public abstract class TextOverlay extends BitmapOverlay {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link TextOverlay} that shows the {@code overlayText} with the same {@link
|
* Creates a {@link TextOverlay} that shows the {@code overlayText} with the same {@link
|
||||||
* OverlaySettings} throughout the whole video.
|
* StaticOverlaySettings} throughout the whole video.
|
||||||
*
|
*
|
||||||
* @param overlayText The text to overlay on the video.
|
* @param overlayText The text to overlay on the video.
|
||||||
* @param overlaySettings The {@link OverlaySettings} configuring how the overlay is displayed on
|
* @param overlaySettings The {@link StaticOverlaySettings} configuring how the overlay is
|
||||||
* the frames.
|
* displayed on the frames.
|
||||||
*/
|
*/
|
||||||
public static TextOverlay createStaticTextOverlay(
|
public static TextOverlay createStaticTextOverlay(
|
||||||
SpannableString overlayText, OverlaySettings overlaySettings) {
|
SpannableString overlayText, StaticOverlaySettings overlaySettings) {
|
||||||
return new TextOverlay() {
|
return new TextOverlay() {
|
||||||
@Override
|
@Override
|
||||||
public SpannableString getText(long presentationTimeUs) {
|
public SpannableString getText(long presentationTimeUs) {
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ public abstract class TextureOverlay {
|
||||||
* @param presentationTimeUs The presentation timestamp of the current frame, in microseconds.
|
* @param presentationTimeUs The presentation timestamp of the current frame, in microseconds.
|
||||||
*/
|
*/
|
||||||
public OverlaySettings getOverlaySettings(long presentationTimeUs) {
|
public OverlaySettings getOverlaySettings(long presentationTimeUs) {
|
||||||
return new OverlaySettings.Builder().build();
|
return new OverlaySettings() {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ public interface VideoCompositorSettings {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
||||||
return new OverlaySettings.Builder().build();
|
return new OverlaySettings() {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ import androidx.media3.effect.OverlaySettings;
|
||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.effect.Presentation;
|
||||||
import androidx.media3.effect.RgbFilter;
|
import androidx.media3.effect.RgbFilter;
|
||||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||||
|
import androidx.media3.effect.StaticOverlaySettings;
|
||||||
import androidx.media3.effect.TextOverlay;
|
import androidx.media3.effect.TextOverlay;
|
||||||
import androidx.media3.effect.VideoCompositor;
|
import androidx.media3.effect.VideoCompositor;
|
||||||
import androidx.media3.effect.VideoCompositorSettings;
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
|
|
@ -536,7 +537,7 @@ public final class DefaultVideoCompositorPixelTest {
|
||||||
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
||||||
if (inputId == 0) {
|
if (inputId == 0) {
|
||||||
// This tests all OverlaySettings builder variables.
|
// This tests all OverlaySettings builder variables.
|
||||||
return new OverlaySettings.Builder()
|
return new StaticOverlaySettings.Builder()
|
||||||
.setScale(.25f, .5f)
|
.setScale(.25f, .5f)
|
||||||
.setOverlayFrameAnchor(1, -1)
|
.setOverlayFrameAnchor(1, -1)
|
||||||
.setBackgroundFrameAnchor(.9f, -.7f)
|
.setBackgroundFrameAnchor(.9f, -.7f)
|
||||||
|
|
@ -544,7 +545,7 @@ public final class DefaultVideoCompositorPixelTest {
|
||||||
.setAlphaScale(.5f)
|
.setAlphaScale(.5f)
|
||||||
.build();
|
.build();
|
||||||
} else {
|
} else {
|
||||||
return new OverlaySettings.Builder().build();
|
return new StaticOverlaySettings.Builder().build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -575,7 +576,7 @@ public final class DefaultVideoCompositorPixelTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
||||||
return new OverlaySettings.Builder().build();
|
return new StaticOverlaySettings.Builder().build();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
compositorTestRunner =
|
compositorTestRunner =
|
||||||
|
|
@ -617,7 +618,7 @@ public final class DefaultVideoCompositorPixelTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
||||||
return new OverlaySettings.Builder()
|
return new StaticOverlaySettings.Builder()
|
||||||
.setOverlayFrameAnchor(-1, -1)
|
.setOverlayFrameAnchor(-1, -1)
|
||||||
.setBackgroundFrameAnchor(-1, -1 + 2f * inputId / NUMBER_OF_INPUT_STREAMS)
|
.setBackgroundFrameAnchor(-1, -1 + 2f * inputId / NUMBER_OF_INPUT_STREAMS)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -950,7 +951,7 @@ public final class DefaultVideoCompositorPixelTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OverlaySettings getOverlaySettings(long presentationTimeUs) {
|
public OverlaySettings getOverlaySettings(long presentationTimeUs) {
|
||||||
return new OverlaySettings.Builder()
|
return new StaticOverlaySettings.Builder()
|
||||||
.setBackgroundFrameAnchor(/* x= */ 0f, /* y= */ 0.5f)
|
.setBackgroundFrameAnchor(/* x= */ 0f, /* y= */ 0.5f)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||||
import androidx.media3.effect.OverlaySettings;
|
import androidx.media3.effect.OverlaySettings;
|
||||||
import androidx.media3.effect.Presentation;
|
import androidx.media3.effect.Presentation;
|
||||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||||
|
import androidx.media3.effect.StaticOverlaySettings;
|
||||||
import androidx.media3.effect.VideoCompositorSettings;
|
import androidx.media3.effect.VideoCompositorSettings;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
@ -190,13 +191,13 @@ public final class TransformerMultiSequenceCompositionTest {
|
||||||
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
public OverlaySettings getOverlaySettings(int inputId, long presentationTimeUs) {
|
||||||
if (inputId == 0) {
|
if (inputId == 0) {
|
||||||
// This tests all OverlaySettings builder variables.
|
// This tests all OverlaySettings builder variables.
|
||||||
return new OverlaySettings.Builder()
|
return new StaticOverlaySettings.Builder()
|
||||||
.setScale(.25f, .25f)
|
.setScale(.25f, .25f)
|
||||||
.setOverlayFrameAnchor(1, -1)
|
.setOverlayFrameAnchor(1, -1)
|
||||||
.setBackgroundFrameAnchor(.9f, -.7f)
|
.setBackgroundFrameAnchor(.9f, -.7f)
|
||||||
.build();
|
.build();
|
||||||
} else {
|
} else {
|
||||||
return new OverlaySettings.Builder().build();
|
return new StaticOverlaySettings.Builder().build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||||
import androidx.media3.effect.GaussianBlur;
|
import androidx.media3.effect.GaussianBlur;
|
||||||
import androidx.media3.effect.GlTextureProducer;
|
import androidx.media3.effect.GlTextureProducer;
|
||||||
import androidx.media3.effect.OverlayEffect;
|
import androidx.media3.effect.OverlayEffect;
|
||||||
import androidx.media3.effect.OverlaySettings;
|
import androidx.media3.effect.StaticOverlaySettings;
|
||||||
import androidx.media3.effect.TextOverlay;
|
import androidx.media3.effect.TextOverlay;
|
||||||
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
import androidx.media3.test.utils.BitmapPixelTestUtil;
|
||||||
import androidx.media3.test.utils.TextureBitmapReader;
|
import androidx.media3.test.utils.TextureBitmapReader;
|
||||||
|
|
@ -304,7 +304,8 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
|
||||||
/* end= */ 7,
|
/* end= */ 7,
|
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
TextOverlay textOverlay =
|
TextOverlay textOverlay =
|
||||||
TextOverlay.createStaticTextOverlay(overlayText, new OverlaySettings.Builder().build());
|
TextOverlay.createStaticTextOverlay(
|
||||||
|
overlayText, new StaticOverlaySettings.Builder().build());
|
||||||
videoFrameProcessorTestRunner =
|
videoFrameProcessorTestRunner =
|
||||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||||
.setEffects(new OverlayEffect(ImmutableList.of(bitmapOverlay, textOverlay)))
|
.setEffects(new OverlayEffect(ImmutableList.of(bitmapOverlay, textOverlay)))
|
||||||
|
|
@ -432,7 +433,7 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
|
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
TextOverlay textOverlay =
|
TextOverlay textOverlay =
|
||||||
TextOverlay.createStaticTextOverlay(
|
TextOverlay.createStaticTextOverlay(
|
||||||
overlayText, new OverlaySettings.Builder().setHdrLuminanceMultiplier(3f).build());
|
overlayText, new StaticOverlaySettings.Builder().setHdrLuminanceMultiplier(3f).build());
|
||||||
videoFrameProcessorTestRunner =
|
videoFrameProcessorTestRunner =
|
||||||
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
getDefaultFrameProcessorTestRunnerBuilder(testId)
|
||||||
.setEffects(new OverlayEffect(ImmutableList.of(textOverlay)))
|
.setEffects(new OverlayEffect(ImmutableList.of(textOverlay)))
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.style.TypefaceSpan;
|
import android.text.style.TypefaceSpan;
|
||||||
import androidx.media3.effect.OverlayEffect;
|
import androidx.media3.effect.OverlayEffect;
|
||||||
import androidx.media3.effect.OverlaySettings;
|
import androidx.media3.effect.OverlaySettings;
|
||||||
|
import androidx.media3.effect.StaticOverlaySettings;
|
||||||
import androidx.media3.effect.TextOverlay;
|
import androidx.media3.effect.TextOverlay;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
|
@ -74,7 +75,7 @@ import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OverlaySettings getOverlaySettings(long presentationTimeUs) {
|
public OverlaySettings getOverlaySettings(long presentationTimeUs) {
|
||||||
return new OverlaySettings.Builder().setBackgroundFrameAnchor(x, y).build();
|
return new StaticOverlaySettings.Builder().setBackgroundFrameAnchor(x, y).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue