android - 如何在 MediaCodec 编码器和 CameraX 之间共享 Surface

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

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

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

注意:我正在使用 KOTLIN

最佳答案

我终于用 OpenGLRenderer 解决了这个问题来自 CameraX OpenGL 测试。这是针对 Beta 7 版的 CameraX。

像往常一样设置 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 谷歌群组对话的一部分出现的较新解决方案 can be found here

关于android - 如何在 MediaCodec 编码器和 CameraX 之间共享 Surface,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61934659/

相关文章:

android - 为什么我的程序打不开webView页面?

android - H264 RTP 视频流被 MediaCodec 损坏

android - Nexus 5 上的 GLSurfaceView 帧率问题

android - Kotlin RC NoClassDefFoundError

android - 如何在 cmake 中添加对自定义 android 编译器的支持?

android - 在 Activity 之间切换时出现黑屏

java - Android - 如何确定加载到 ImageView 的位图何时显示?

android - 使用 MediaCodec 和 MediaExtractor 播放视频和音频

Kotlin - 如何使用 OkHttp Kotlin 设置连接超时

android - 在协程上下文中返回 Kotlin 结果会引发异常