From a1f34ecd3b581aa644580176a23d0466c796ac56 Mon Sep 17 00:00:00 2001 From: lneedham Date: Mon, 13 Jan 2020 14:24:03 +0100 Subject: [PATCH 1/4] Add Alpha-mask filter --- .../java/com/daasuu/epf/EPlayerRenderer.java | 4 +- .../main/java/com/daasuu/epf/EPlayerView.java | 41 ++++++++-- .../daasuu/epf/chooser/EConfigChooser.java | 4 +- .../daasuu/epf/filter/AlphaFrameFilter.java | 80 +++++++++++++++++++ .../java/com/daasuu/epf/filter/GlFilter.java | 4 + 5 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 epf/src/main/java/com/daasuu/epf/filter/AlphaFrameFilter.java diff --git a/epf/src/main/java/com/daasuu/epf/EPlayerRenderer.java b/epf/src/main/java/com/daasuu/epf/EPlayerRenderer.java index 74b37e7..0c03035 100644 --- a/epf/src/main/java/com/daasuu/epf/EPlayerRenderer.java +++ b/epf/src/main/java/com/daasuu/epf/EPlayerRenderer.java @@ -77,7 +77,7 @@ public void run() { @Override public void onSurfaceCreated(final EGLConfig config) { - GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); final int[] args = new int[1]; @@ -136,6 +136,8 @@ public void onSurfaceChanged(final int width, final int height) { @Override public void onDrawFrame(final EFramebufferObject fbo) { + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); synchronized (this) { if (updateSurface) { diff --git a/epf/src/main/java/com/daasuu/epf/EPlayerView.java b/epf/src/main/java/com/daasuu/epf/EPlayerView.java index ef88354..11176af 100644 --- a/epf/src/main/java/com/daasuu/epf/EPlayerView.java +++ b/epf/src/main/java/com/daasuu/epf/EPlayerView.java @@ -1,6 +1,7 @@ package com.daasuu.epf; import android.content.Context; +import android.graphics.PixelFormat; import android.opengl.GLSurfaceView; import android.util.AttributeSet; @@ -10,6 +11,8 @@ import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.video.VideoListener; +import static com.daasuu.epf.chooser.EConfigChooser.EGL_CONTEXT_CLIENT_VERSION; + /** * Created by sudamasayuki on 2017/05/16. */ @@ -20,7 +23,12 @@ public class EPlayerView extends GLSurfaceView implements VideoListener { private final EPlayerRenderer renderer; private SimpleExoPlayer player; - private float videoAspect = 1f; + /* Video Aspect according to the video */ + private float measuredVideoAspect = 1f; + + /* Video Aspect according to the video, adjusted to the needs of the filter */ + private float adjustedVideoAspect = measuredVideoAspect; + private PlayerScaleType playerScaleType = PlayerScaleType.RESIZE_FIT_WIDTH; public EPlayerView(Context context) { @@ -31,11 +39,22 @@ public EPlayerView(Context context, AttributeSet attrs) { super(context, attrs); setEGLContextFactory(new EContextFactory()); - setEGLConfigChooser(new EConfigChooser()); + + setEGLConfigChooser(new EConfigChooser( + 8, + 8, + 8, + 8, + 16, + 0, + EGL_CONTEXT_CLIENT_VERSION + )); + + setZOrderOnTop(true); + getHolder().setFormat(PixelFormat.RGBA_8888); renderer = new EPlayerRenderer(this); setRenderer(renderer); - } public EPlayerView setSimpleExoPlayer(SimpleExoPlayer player) { @@ -51,6 +70,14 @@ public EPlayerView setSimpleExoPlayer(SimpleExoPlayer player) { public void setGlFilter(GlFilter glFilter) { renderer.setGlFilter(glFilter); + + if (glFilter == null) { + adjustedVideoAspect = measuredVideoAspect; + } else { + adjustedVideoAspect = glFilter.getVideoAspect(measuredVideoAspect); + } + + requestLayout(); } public void setPlayerScaleType(PlayerScaleType playerScaleType) { @@ -70,10 +97,10 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { switch (playerScaleType) { case RESIZE_FIT_WIDTH: - viewHeight = (int) (measuredWidth / videoAspect); + viewHeight = (int) (measuredWidth / adjustedVideoAspect); break; case RESIZE_FIT_HEIGHT: - viewWidth = (int) (measuredHeight * videoAspect); + viewWidth = (int) (measuredHeight * adjustedVideoAspect); break; } @@ -95,8 +122,8 @@ public void onPause() { @Override public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { // Log.d(TAG, "width = " + width + " height = " + height + " unappliedRotationDegrees = " + unappliedRotationDegrees + " pixelWidthHeightRatio = " + pixelWidthHeightRatio); - videoAspect = ((float) width / height) * pixelWidthHeightRatio; - // Log.d(TAG, "videoAspect = " + videoAspect); + measuredVideoAspect = ((float) width / height) * pixelWidthHeightRatio; + // Log.d(TAG, "measuredVideoAspect = " + measuredVideoAspect); requestLayout(); } diff --git a/epf/src/main/java/com/daasuu/epf/chooser/EConfigChooser.java b/epf/src/main/java/com/daasuu/epf/chooser/EConfigChooser.java index 4f77625..550e15c 100644 --- a/epf/src/main/java/com/daasuu/epf/chooser/EConfigChooser.java +++ b/epf/src/main/java/com/daasuu/epf/chooser/EConfigChooser.java @@ -30,10 +30,10 @@ public class EConfigChooser implements GLSurfaceView.EGLConfigChooser { private final int depthSize; private final int stencilSize; - private static final int EGL_CONTEXT_CLIENT_VERSION = 2; - private static final boolean USE_RGB_888 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1; + public static final int EGL_CONTEXT_CLIENT_VERSION = 2; + public EConfigChooser() { this( USE_RGB_888 ? 8 : 5, diff --git a/epf/src/main/java/com/daasuu/epf/filter/AlphaFrameFilter.java b/epf/src/main/java/com/daasuu/epf/filter/AlphaFrameFilter.java new file mode 100644 index 0000000..6907301 --- /dev/null +++ b/epf/src/main/java/com/daasuu/epf/filter/AlphaFrameFilter.java @@ -0,0 +1,80 @@ +package com.daasuu.epf.filter; + +/** + * Also known as Luma Matte. + * Used for videos which comprise half of content, and half of alpha-mask / luma-matte. + * The result is a video containing only content, masked by the alpha-mask to add transparency + */ +public class AlphaFrameFilter extends GlFilter { + + private static final String VERTEX_SHADER = + "attribute vec4 aPosition;\n" + + "attribute vec4 aTextureCoord;\n" + + "varying highp vec2 vTextureCoordContent;\n" + + "varying highp vec2 vTextureCoordMask;\n" + + "void main() {\n" + + "gl_Position = aPosition;\n" + + "vTextureCoordContent = %s;\n" + + "vTextureCoordMask = %s;\n" + + "}\n"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "varying highp vec2 vTextureCoordContent;\n" + + "varying highp vec2 vTextureCoordMask;\n" + + "uniform lowp sampler2D sTexture;\n" + + "void main() {\n" + + "vec4 colorContent = texture2D(sTexture, vTextureCoordContent);\n" + + "vec4 colorMask = texture2D(sTexture, vTextureCoordMask);\n" + + "gl_FragColor = vec4(colorContent.rgb, colorMask.r);\n" + + "}\n"; + + private AlphaMaskPosition alphaMaskPosition; + + /** + * @param alphaMaskPosition the position of the alpha-mask in the video. + */ + public AlphaFrameFilter(AlphaMaskPosition alphaMaskPosition) { + super(getVertexShader(alphaMaskPosition), FRAGMENT_SHADER); + this.alphaMaskPosition = alphaMaskPosition; + } + + @Override + public float getVideoAspect(float originalVideoAspect) { + float factor; + if (alphaMaskPosition == AlphaMaskPosition.TOP || alphaMaskPosition == AlphaMaskPosition.BOTTOM) { + factor = 2f; + } else { + factor = 1f / 2f; + } + return originalVideoAspect * factor; + } + + private static String getVertexShader(AlphaMaskPosition alphaMaskPosition) { + String vTextureCoordContent; + String vTextureCoordMask; + + // Note: LEFT and TOP are untested! If funky stuff is occurring, check here + if (alphaMaskPosition == AlphaMaskPosition.LEFT) { + vTextureCoordContent = "vec2(aTextureCoord.x*0.5+0.5, aTextureCoord.y)"; + vTextureCoordMask = "vec2(aTextureCoord.x*0.5, aTextureCoord.y)"; + } else if (alphaMaskPosition == AlphaMaskPosition.TOP) { + vTextureCoordContent = "vec2(aTextureCoord.x, aTextureCoord.y*0.5)"; + vTextureCoordMask = "vec2(aTextureCoord.x, aTextureCoord.y*0.5+0.5)"; + } else if (alphaMaskPosition == AlphaMaskPosition.RIGHT) { + vTextureCoordContent = "vec2(aTextureCoord.x*0.5, aTextureCoord.y)"; + vTextureCoordMask = "vec2(aTextureCoord.x*0.5+0.5, aTextureCoord.y)"; + } else if (alphaMaskPosition == AlphaMaskPosition.BOTTOM) { + vTextureCoordContent = "vec2(aTextureCoord.x, aTextureCoord.y*0.5+0.5)"; + vTextureCoordMask = "vec2(aTextureCoord.x, aTextureCoord.y*0.5)"; + } else { + throw new RuntimeException("No vertex shader found for alphaMaskPosition" + alphaMaskPosition); + } + + return String.format(VERTEX_SHADER, vTextureCoordContent, vTextureCoordMask); + } + + public enum AlphaMaskPosition { + LEFT, TOP, RIGHT, BOTTOM + } +} \ No newline at end of file diff --git a/epf/src/main/java/com/daasuu/epf/filter/GlFilter.java b/epf/src/main/java/com/daasuu/epf/filter/GlFilter.java index 1a399aa..9e692ae 100644 --- a/epf/src/main/java/com/daasuu/epf/filter/GlFilter.java +++ b/epf/src/main/java/com/daasuu/epf/filter/GlFilter.java @@ -130,6 +130,10 @@ public void draw(final int texName, final EFramebufferObject fbo) { GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); } + public float getVideoAspect(float originalVideoAspect) { + return originalVideoAspect; + } + protected void onDraw() { } From de79b28e986a20aa5b04876ca238caf3a777255a Mon Sep 17 00:00:00 2001 From: lneedham Date: Mon, 13 Jan 2020 14:26:45 +0100 Subject: [PATCH 2/4] Reformatting --- epf/src/main/java/com/daasuu/epf/EPlayerView.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/epf/src/main/java/com/daasuu/epf/EPlayerView.java b/epf/src/main/java/com/daasuu/epf/EPlayerView.java index 11176af..4eaf0aa 100644 --- a/epf/src/main/java/com/daasuu/epf/EPlayerView.java +++ b/epf/src/main/java/com/daasuu/epf/EPlayerView.java @@ -40,15 +40,7 @@ public EPlayerView(Context context, AttributeSet attrs) { setEGLContextFactory(new EContextFactory()); - setEGLConfigChooser(new EConfigChooser( - 8, - 8, - 8, - 8, - 16, - 0, - EGL_CONTEXT_CLIENT_VERSION - )); + setEGLConfigChooser(new EConfigChooser(8, 8, 8, 8, 16, 0, EGL_CONTEXT_CLIENT_VERSION)); setZOrderOnTop(true); getHolder().setFormat(PixelFormat.RGBA_8888); From 1209b0af3240dce4c60c33b5635c097b23cb66b9 Mon Sep 17 00:00:00 2001 From: lneedham Date: Tue, 14 Jan 2020 12:09:16 +0100 Subject: [PATCH 3/4] Add `EPlayerTranslucentView` --- .../daasuu/epf/EPlayerTranslucentView.java | 19 +++++++++++++++++++ .../main/java/com/daasuu/epf/EPlayerView.java | 1 - .../daasuu/epf/filter/AlphaFrameFilter.java | 7 ++++++- 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 epf/src/main/java/com/daasuu/epf/EPlayerTranslucentView.java diff --git a/epf/src/main/java/com/daasuu/epf/EPlayerTranslucentView.java b/epf/src/main/java/com/daasuu/epf/EPlayerTranslucentView.java new file mode 100644 index 0000000..69ce8c2 --- /dev/null +++ b/epf/src/main/java/com/daasuu/epf/EPlayerTranslucentView.java @@ -0,0 +1,19 @@ +package com.daasuu.epf; + +import android.content.Context; +import android.util.AttributeSet; + +/** + * Created by LukeNeedham on 2020/01/14. + */ +public class EPlayerTranslucentView extends EPlayerView { + + public EPlayerTranslucentView(Context context) { + this(context, null); + } + + public EPlayerTranslucentView(Context context, AttributeSet attrs) { + super(context, attrs); + setZOrderOnTop(true); + } +} diff --git a/epf/src/main/java/com/daasuu/epf/EPlayerView.java b/epf/src/main/java/com/daasuu/epf/EPlayerView.java index 4eaf0aa..97f0bf3 100644 --- a/epf/src/main/java/com/daasuu/epf/EPlayerView.java +++ b/epf/src/main/java/com/daasuu/epf/EPlayerView.java @@ -42,7 +42,6 @@ public EPlayerView(Context context, AttributeSet attrs) { setEGLConfigChooser(new EConfigChooser(8, 8, 8, 8, 16, 0, EGL_CONTEXT_CLIENT_VERSION)); - setZOrderOnTop(true); getHolder().setFormat(PixelFormat.RGBA_8888); renderer = new EPlayerRenderer(this); diff --git a/epf/src/main/java/com/daasuu/epf/filter/AlphaFrameFilter.java b/epf/src/main/java/com/daasuu/epf/filter/AlphaFrameFilter.java index 6907301..8431845 100644 --- a/epf/src/main/java/com/daasuu/epf/filter/AlphaFrameFilter.java +++ b/epf/src/main/java/com/daasuu/epf/filter/AlphaFrameFilter.java @@ -3,7 +3,12 @@ /** * Also known as Luma Matte. * Used for videos which comprise half of content, and half of alpha-mask / luma-matte. - * The result is a video containing only content, masked by the alpha-mask to add transparency + * The result is a video containing only content, masked by the alpha-mask to add transparency. + *

+ * To use this filter, you need to use a EPlayerTranslucentView rather than an ordinary EPlayerView. + *

+ * Or, use an EPlayerView and ensure you call `EPlayerView.setZOrderOnTop(true)` + * before the surface view's containing window is attached to the window manager */ public class AlphaFrameFilter extends GlFilter { From d1abb3ddc09c16d294ee9a319462acae7744374b Mon Sep 17 00:00:00 2001 From: lneedham Date: Fri, 17 Jan 2020 10:35:42 +0100 Subject: [PATCH 4/4] Fix adjusted video aspect when video size changes --- .../main/java/com/daasuu/epf/EPlayerView.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/epf/src/main/java/com/daasuu/epf/EPlayerView.java b/epf/src/main/java/com/daasuu/epf/EPlayerView.java index 97f0bf3..aa60c5e 100644 --- a/epf/src/main/java/com/daasuu/epf/EPlayerView.java +++ b/epf/src/main/java/com/daasuu/epf/EPlayerView.java @@ -29,6 +29,8 @@ public class EPlayerView extends GLSurfaceView implements VideoListener { /* Video Aspect according to the video, adjusted to the needs of the filter */ private float adjustedVideoAspect = measuredVideoAspect; + private GlFilter glFilter = null; + private PlayerScaleType playerScaleType = PlayerScaleType.RESIZE_FIT_WIDTH; public EPlayerView(Context context) { @@ -60,13 +62,10 @@ public EPlayerView setSimpleExoPlayer(SimpleExoPlayer player) { } public void setGlFilter(GlFilter glFilter) { + this.glFilter = glFilter; renderer.setGlFilter(glFilter); - if (glFilter == null) { - adjustedVideoAspect = measuredVideoAspect; - } else { - adjustedVideoAspect = glFilter.getVideoAspect(measuredVideoAspect); - } + adjustedVideoAspect = calculateAdjustedVideoAspect(); requestLayout(); } @@ -114,7 +113,9 @@ public void onPause() { public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { // Log.d(TAG, "width = " + width + " height = " + height + " unappliedRotationDegrees = " + unappliedRotationDegrees + " pixelWidthHeightRatio = " + pixelWidthHeightRatio); measuredVideoAspect = ((float) width / height) * pixelWidthHeightRatio; + adjustedVideoAspect = calculateAdjustedVideoAspect(); // Log.d(TAG, "measuredVideoAspect = " + measuredVideoAspect); + requestLayout(); } @@ -122,4 +123,12 @@ public void onVideoSizeChanged(int width, int height, int unappliedRotationDegre public void onRenderedFirstFrame() { // do nothing } + + private float calculateAdjustedVideoAspect() { + if (glFilter == null) { + return measuredVideoAspect; + } else { + return glFilter.getVideoAspect(measuredVideoAspect); + } + } }