如何以最有效的方式创建多个 camera2 预览?

Posted

技术标签:

【中文标题】如何以最有效的方式创建多个 camera2 预览?【英文标题】:How to create multiple camera2 previews in most efficient way? 【发布时间】:2020-04-18 20:25:36 【问题描述】:

我正在尝试在我的活动中创建 4 个我的相机预览流。我创建了一个 TextureView,它注册到 feed 的 camera2 API,然后我在 SurfaceView 上设置了一个监听器,以便监听 feed 的变化并相应地更新其他 3 个预览(ImageViews)。您可以在下面的代码中看到:

    private final TextureView.SurfaceTextureListener mSurfaceTextureListener
            = new TextureView.SurfaceTextureListener() 

        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) 
            cameraHandler.openCamera(width, height);
        

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) 
        

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) 
            return true;
        

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture texture) 
            for (ImageView mSecondaryPreview : mSecondaryPreviews) 
                Bitmap frame = Bitmap.createBitmap(mTextureView.getWidth(), mTextureView.getHeight(), Bitmap.Config.ARGB_8888);
                mTextureView.getBitmap(frame);
                mSecondaryPreview.setImageBitmap(frame);
            
        
    ;

如您所见,这必须从 TextureView 中读取每一帧,提取位图,然后设置其他 3 个 ImageView 流的位图。我最初尝试在 UI 线程上执行此操作,这非常慢,然后尝试将其提交给帧速率更好的后台处理程序,但由于负载导致应用程序崩溃的许多问题。

谢谢

编辑

所以为了裁剪预览的底部0.4375,我把ttmp改成了

    float[] ttmp = 
            0.0f, 1.0f,
            1.0f, 1.0f,
            0.0f, 0.4375f,

            0.0f, 0.4375f,
            1.0f, 1.0f,
            1.0f, 0.4375f,

            0.0f, 1.0f,
            1.0f, 1.0f,
            0.0f, 0.4375f,

            0.0f, 0.4375f,
            1.0f, 1.0f,
            1.0f, 0.4375f,

            0.0f, 1.0f,
            1.0f, 1.0f,
            0.0f, 0.4375f,

            0.0f, 0.4375f,
            1.0f, 1.0f,
            1.0f, 0.4375f,

            0.0f, 1.0f,
            1.0f, 1.0f,
            0.0f, 0.4375f,

            0.0f, 0.4375f,
            1.0f, 1.0f,
            1.0f, 0.4375f
    ;

但这并没有按预期裁剪

【问题讨论】:

您的用例是什么?您需要 3 个作为 ImageViews,还是将 4 个副本包含在一个 GLSurfaceView 中? 是的,4 个 GLSurfaceView 副本可以正常工作,我试图了解如何实现这一点,但我迷路了 您能否提供一些指导,让我了解如何获得 4 个 GLSurfaceView 副本 我没有时间给出完整的答案,但如果您可以在 GLSurfaceView (see here for a rough example) 中进行预览,那么再制作 3 个副本只需再添加 6 个即可多边形(vtmpttmp)。就效率而言,OpenGL 绝对是您的最佳选择。 嗨,我已经在GLSurfaceView 上使用带有vtmp = 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f ttmp = 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f 的前置摄像头进行预览。我现在如何添加更多多边形?如果您能提供帮助,还有一个单独的问题,预览从左到右倒置,您知道要更改哪些数字以解决此问题/我找不到任何文档。非常感谢 【参考方案1】:

因此,如果您可以让GLSurfaceView 像在this question 中一样使用相机预览,那么您只需添加 6 个多边形即可再制作 3 个副本。让我解释一下它们是如何布局的。 vtmpttmp 分别以GL_TRIANGLE_STRIP 的形式描述了两个三角形的矢量坐标和纹理坐标:

float[] vtmp = 
    1.0f, 1.0f,    //Top right of screen
    1.0f, -1.0f,    //Bottom right of screen
    -1.0f, 1.0f,    //Top left of screen
    -1.0f, -1.0f    //Bottom left of screen
;

float[] ttmp = 
    1.0f, 1.0f,     //Top right of camera surface
    0.0f, 1.0f,     //Top left of camera surface
    1.0f, 0.0f,     //Bottom right of camera surface
    0.0f, 0.0f      //Bottom left of camera surface
;

第 1 步:让我们将原始类型更改为 GL_TRIANGLES,因为使用 GL_TRIANGLE_STRIP 会很困难:

//This also includes a fix for the left/right inversion:
float[] vtmp = 
    //Triangle 1:
    -1.0f, -1.0f,
    -1.0f, 1.0f,
    1.0f, 1.0f,

    //Triangle 2:
    1.0f, -1.0f,
    -1.0f, 1.0f,
    1.0f, 1.0f
;

float[] ttmp = 
    //Triangle 1:
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 0.0f,

    //Triangle 2:
    0.0f, 0.0f,
    1.0f, 1.0f,
    1.0f, 0.0f
;

pVertex = ByteBuffer.allocateDirect(12*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
(...)
pTexCoord = ByteBuffer.allocateDirect(12*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
(...)
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);    //Careful: Multiple changes on this line
(...)

如果步骤 1 顺利,那么让我们在步骤 2 中添加额外的三角形:

float[] vtmp = 
    //Triangle 1:
    -1.0f, 0.0f,
    -1.0f, 1.0f,
    0.0f, 0.0f,

    //Triangle 2:
    0.0f, 0.0f,
    -1.0f, 1.0f,
    0.0f, 1.0f,

    //Triangle 3:
    0.0f, 0.0f,
    0.0f, 1.0f,
    1.0f, 0.0f,

    //Triangle 4:
    1.0f, 0.0f,
    0.0f, 1.0f,
    1.0f, 1.0f,

    //Triangle 5:
    0.0f, -1.0f,
    0.0f, 0.0f,
    1.0f, -1.0f,

    //Triangle 6:
    1.0f, -1.0f,
    0.0f, 0.0f,
    1.0f, 0.0f,

    //Triangle 7:
    -1.0f, -1.0f,
    -1.0f, 0.0f,
    0.0f, -1.0f,

    //Triangle 8:
    0.0f, -1.0f,
    -1.0f, 0.0f,
    0.0f, 0.0f
;

float[] ttmp = 
    //This is the same as in Step 1, but duplicated 4 times over:
    //Triangle 1:
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 0.0f,

    //Triangle 2:
    0.0f, 0.0f,
    1.0f, 1.0f,
    1.0f, 0.0f,

    //Triangle 3:
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 0.0f,

    //Triangle 4:
    0.0f, 0.0f,
    1.0f, 1.0f,
    1.0f, 0.0f,

    //Triangle 5:
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 0.0f,

    //Triangle 6:
    0.0f, 0.0f,
    1.0f, 1.0f,
    1.0f, 0.0f,

    //Triangle 7:
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 0.0f,

    //Triangle 8:
    0.0f, 0.0f,
    1.0f, 1.0f,
    1.0f, 0.0f
;

pVertex = ByteBuffer.allocateDirect(48*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
(...)
pTexCoord = ByteBuffer.allocateDirect(48*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
(...)
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 24);
(...)

【讨论】:

非常感谢,您的回答对我理解它的工作原理有很大帮助,我会试一试并回复您 所以我一直在努力解决,基本上演示使用GL_TRIANGLE_STRIP而不是GL_TRIANGLE_LIST。我能够使用 vtmp = -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0fttmp = 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f 解决 xy 反转,但我无法在步骤 1 中将其转换为使用 GL_TRIANGLES。再次感谢您的所有帮助 GL_TRIANGLE_LIST 是一个错字,我的意思是GL_TRIANGLE_STRIP。那么出了什么问题呢? 如果我将代码更改为第 1 步,我会从左下角、左上角、右上角的三角形中获得预览,该三角形被分成 3 个三角形,显示预览的不同部分。我试图将它与openglbook.com/images/4262_02_06.png 进行比较,它似乎是正确的,所以我真的不确定它为什么这样做:/。在添加额外多边形之前,我是否需要在第一步中更改字节缓冲区分配,我已经这样做了,但可能是第 2 步? 很抱歉打扰您,我还有最后一个问题。感谢您的帮助,我现在已经完全按照我喜欢的方式设置了预览,作为正方形,但是相机是 1920x1080,在以前的方法中,我可以简单地为视图添加一个边距,但你知道我之前如何裁剪纹理它是这样写的,所以它是一个未压扁的方形输出?非常感谢

以上是关于如何以最有效的方式创建多个 camera2 预览?的主要内容,如果未能解决你的问题,请参考以下文章

Java一次替换字符串中的多个不同子字符串(或以最有效的方式)

如何以最有效的方式将运动员的名字分配给新列“名字”? [复制]

如何在 Camera2 API Android 5.0 中获取单个预览帧?

Camera2全屏预览和图像捕捉

使用Android NDK Camera2获取预览帧的正确方法是啥

如何使用Android中的Camera2 API在不预览的情况下拍摄多张照片?