【问题标题】:How can I share a Surface between MediaCodec encoder and CameraX如何在 MediaCodec 编码器和 CameraX 之间共享 Surface
【发布时间】:2020-09-08 02:12:49
【问题描述】:

我想从CameraX(预览用例)获取图像,并使用MediaCodec 将它们编码为h.264 视频。我怎样才能做到这一点? 我正在尝试的是,通过使用Preview.setSurfaceProvider()Preview.Builder() 中使用从MediaCodec.createInputSurface() 返回的Surface。我从Preview.SurfaceProvider 继承了一个类,然后在该设置中配置我的编码器并覆盖onSurfaceRequested() 以从createInputSurface() 返回Surface。这预计会奏效吗?我真的可以像这样共享一个 Surface 并期望 CameraX 写入这个 Surface 并为我的编码器填充输入吗?

是否有更有效的方法来编码实时 CameraX 提要?

注意:我正在使用 KOTLIN

【问题讨论】:

    标签: android android-mediacodec kotlin-android-extensions android-camerax


    【解决方案1】:

    我终于用 CameraX OpenGL 测试中的OpenGLRenderer 解决了这个问题。这适用于 CameraX 的 beta 7 版本。

    像往常一样设置 camerax,但使用 2 个预览:

    val preview: Preview = Preview.Builder().apply {
        setTargetResolution(targetSize)
        setTargetRotation(rotation)
    }.build()
    
    val encoderPreview: Preview = Preview.Builder().apply {
        setTargetResolution(targetSize)
        setTargetRotation(rotation)
    }.build()
    
    cameraProvider.unbindAll()
    
    camera = cameraProvider.bindToLifecycle(
            lifecycleOwner,
            cameraSelector,
            preview,
            encoderPreview
    )
    
    preview.setSurfaceProvider(viewFinder.createSurfaceProvider())
    

    然后初始化编码器:

    val format = MediaFormat.createVideoFormat(
            "video/avc", resolution.width, resolution.height
    )
    
    format.setInteger(
            MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
    )
    
    format.setInteger(MediaFormat.KEY_BIT_RATE, 500 * 1024)
    format.setInteger(MediaFormat.KEY_FRAME_RATE, 25)
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 3)
    
    encoder = MediaCodec.createEncoderByType("video/avc")
    
    encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
    

    并连接两者:

    private val glRenderer = OpenGLRenderer()
    
    surface = encoder.createInputSurface()
    
    glRenderer.attachInputPreview(encoderPreview)
    
    glRenderer.setFrameUpdateListener(executor, Consumer<Long> {
        // when frame is written to output surface
        publishFrame()
    })
    
    encoder.start()
    
    glRenderer.attachOutputSurface(surface, resolution, 0)
    

    发布框架功能:

    private fun publishFrame() {
        val index: Int = try {
            encoder.dequeueOutputBuffer(info, 10 * 1000)
        } catch (e: Exception) {
            -1
        }
    
        if (!isRunning.get()) {
            return
        }
    
        if (index >= 0) {
            val outputBuffer = encoder.getOutputBuffer(index)
            if (outputBuffer == null) {
                return
            }
    
            if (info.size > 0) {
                outputBuffer.position(info.offset)
                outputBuffer.limit(info.offset + info.size)
                info.presentationTimeUs = System.nanoTime() / 1000
    
                // do something with frame
            }
    
            encoder.releaseOutputBuffer(index, false)
    
            if (info.flags.hasFlag(MediaCodec.BUFFER_FLAG_END_OF_STREAM)) {
                return
            }
        }
    }
    

    请注意,编码器中的FRAME_RATE 参数不受尊重,您将根据发布到输出表面的帧数获得帧速率(多少次称为publishFrame)。在OpenGLRenderer中控制帧率变化private void renderLatest()函数(丢帧,不要调用renderTexture)。

    编辑:作为camerax google group can be found here对话的一部分出现的较新的解决方案

    【讨论】:

    • 谢谢你。连接两者时使用的encoderPreview在哪里?我看到你在glRenderer.attachInputPreview(preview) 中使用了preview。如果你有一个样本,我真的很难让它与我的编码器一起工作,屏幕只是黑色的。
    • 嗨!我犯了一个错误。 glRenderer.attachInputPreview 中应该是encoderPreview。上面已经修复了。
    • 我不再使用上述解决方案。它起作用了,但是我在预览转换时遇到了问题,因为流(编码)与 UI 上的不同,它可以动态更改。现在只需一个预览就实现了所有内容,并且在 OpenGLRender 中有额外的上下文。在 cpp 中也有必要重新编写一些代码。太复杂了,不能在这里发帖:)
    • 啊,谢谢。我希望有一种更简单的方法来告诉相机预览表面使用媒体编解码器输入表面。我将更多地关注使用 OpenGLRenderer。我正在做的部分编码是在 cpp 中,但我希望将它与相机分开 - 我猜要确定。还是谢谢!
    • @nymeria,检查我在上次编辑时添加的链接,以查看新的解决方案。
    猜你喜欢
    • 2015-12-03
    • 2019-09-04
    • 2011-03-14
    • 1970-01-01
    • 2019-07-03
    • 2014-09-24
    • 1970-01-01
    • 2014-07-07
    • 1970-01-01
    相关资源
    最近更新 更多