Overlapping GLSurfaceView on SurfaceView and vice versa
Asked Answered
S

1

1

ExoPlayer - SurfaceView

Camera2 + MediaCodec - GLSurfaceView

I am using the above view groups for playing video and camera recording.

UI-1: Exo-Surf at the center and Cam-GLS in the top right corner.

UI-2: Cam-GLS at the center and Exo-Surf in the top right corner.

enter image description here

To achieve this I am using setZOrderOnTop to set z-index, as both are inside RelativeLayout.

(exoPlayerView.videoSurfaceView as? SurfaceView)?.setZOrderOnTop(true/false)

It seems working fine on Samsung S9+ with API 29 - Android 10, and also for API 28.

But for API 21-27, it behaves with some random issues.

  • Dash-A top part of SurfaceView/GLSurfaceView is not visible
  • Dash-B bottom part of SurfaceView/GLSurfaceView is not visible
  • Entire SurfaceView / GLSurfaceView becomes completely transparent in the top right corner

Also tried using setZOrderMediaOverlay but no luck.

I am sure two surface view works together as Whatsapp and google duo apps are using them in video calls. But I am wondering if GLSurfaceView is causing an issue "something about locking the GL thread" as commented below in this answer.

Hoping for a working solution for API 21+ or any reference link, suggestions would be highly appreciated.

Schizomycete answered 23/5, 2020 at 2:7 Comment(0)
C
0

Instead of using the built-in GLSurfaceView, you'll have to create multiple SurfaceViews, and manage how OpenGL draws on one (or more) of those.

The Grafika code (that I mentioned in my comment to the answer you link) is here:
https://github.com/google/grafika/blob/master/app/src/main/java/com/android/grafika/MultiSurfaceActivity.java

In that code, onCreate creates the surfaces:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_multi_surface_test);

    // #1 is at the bottom; mark it as secure just for fun.  By default, this will use
    // the RGB565 color format.
    mSurfaceView1 = (SurfaceView) findViewById(R.id.multiSurfaceView1);
    mSurfaceView1.getHolder().addCallback(this);
    mSurfaceView1.setSecure(true);

    // #2 is above it, in the "media overlay"; must be translucent or we will totally
    // obscure #1 and it will be ignored by the compositor.  The addition of the alpha
    // plane should switch us to RGBA8888.
    mSurfaceView2 = (SurfaceView) findViewById(R.id.multiSurfaceView2);
    mSurfaceView2.getHolder().addCallback(this);
    mSurfaceView2.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    mSurfaceView2.setZOrderMediaOverlay(true);

    // #3 is above everything, including the UI.  Also translucent.
    mSurfaceView3 = (SurfaceView) findViewById(R.id.multiSurfaceView3);
    mSurfaceView3.getHolder().addCallback(this);
    mSurfaceView3.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    mSurfaceView3.setZOrderOnTop(true);
}

The initial draw code is in:

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)

which calls different local methods depending on some local flags. For example, it calls an example of GL drawing here:

private void drawRectSurface(Surface surface, int left, int top, int width, int height) {
    EglCore eglCore = new EglCore();
    WindowSurface win = new WindowSurface(eglCore, surface, false);
    win.makeCurrent();
    GLES20.glClearColor(0, 0, 0, 0);
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
    for (int i = 0; i < 4; i++) {
        int x, y, w, h;
        if (width < height) {
            // vertical
            w = width / 4;
            h = height;
            x = left + w * i;
            y = top;
        } else {
            // horizontal
            w = width;
            h = height / 4;
            x = left;
            y = top + h * i;
        }
        GLES20.glScissor(x, y, w, h);
        switch (i) {
            case 0:     // 50% blue at 25% alpha, pre-multiplied
                GLES20.glClearColor(0.0f, 0.0f, 0.125f, 0.25f);
                break;
            case 1:     // 100% blue at 25% alpha, pre-multiplied
                GLES20.glClearColor(0.0f, 0.0f, 0.25f, 0.25f);
                break;
            case 2:     // 200% blue at 25% alpha, pre-multiplied (should get clipped)
                GLES20.glClearColor(0.0f, 0.0f, 0.5f, 0.25f);
                break;
            case 3:     // 100% white at 25% alpha, pre-multiplied
                GLES20.glClearColor(0.25f, 0.25f, 0.25f, 0.25f);
                break;
        }
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    }
    GLES20.glDisable(GLES20.GL_SCISSOR_TEST);

    win.swapBuffers();
    win.release();
    eglCore.release();
}

I haven't used this code, so I can only suggest you search for additional details about the various calls you see in that code.

FIRST, try to get a simple example working that has two overlapping SurfaceViews, WITHOUT any OpenGL calls. E.g. solid background color views that overlap. And I reiterate the key point: Do Not make either of them a GLSurfaceView!

THEN attempt to change one of the views to initialize and use OpenGL. (Using logic similar to the code I describe above; still NOT a GLSurfaceView.)

Crinum answered 26/5, 2020 at 22:21 Comment(1)
Actually, I am using github.com/natario1/CameraView this library and in that, I set GLSurfaceView for video recording. So I don't have much freedom to change that.Schizomycete

© 2022 - 2024 — McMap. All rights reserved.