【问题标题】:Android Camera2 pipeline: How do I encode h.264 units using MediaCodec from an input Surface?Android Camera2 管道:如何使用来自输入 Surface 的 MediaCodec 对 h.264 单元进行编码?
【发布时间】:2020-09-07 08:29:18
【问题描述】:

我有一个使用 Camera2 API 的 Android 应用程序。最终目标是让 h264 单元写入流。到目前为止我有

  1. 成功创建了一个捕获会话,并且可以通过以下方式写入预览、本地录制和流式处理表面:
    session.device.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).run {
                    addTarget(previewSurface)
                    addTarget(recorder.surface)
                    addTarget(streamer.surface)
                    set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(args.fps, args.fps))
                    build()
                }
  1. 设置一个 MediaCodec 以对来自上述 streamer.suface 参数的数据进行编码,其中表面是从如下创建的 MediaCodec 对 mediaCodec.createInputSurface 的调用派生而来的
    internal fun streamingCodec(args: CameraFragmentArgs): MediaCodec {
        val mediaFormat = MediaFormat.createVideoFormat("video/avc", args.width, args.height).apply {
            setInteger(MediaFormat.KEY_BIT_RATE, 2000 * 1024)
            setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2)
            setInteger(MediaFormat.KEY_FRAME_RATE, args.fps)
            setInteger(
                MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
            )
        }

        val encoderName = MediaCodecList(MediaCodecList.REGULAR_CODECS).findEncoderForFormat(mediaFormat)

        return MediaCodec.createByCodecName(encoderName).apply {
            configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        }
    }
  1. 当上面的surfaceByteBuffer中有信息时使用异步回调
private class StreamingCallBack) : MediaCodec.Callback() {

    override fun onInputBufferAvailable(codec: MediaCodec, index: Int) = Unit

    override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: BufferInfo) {

       val byteBuffer = codec.getOutputBuffer(index)
       // Is the data in the buffer properly encoded as h.264 here? Did I need to use MediaExtractor?
    }

    override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) = Unit

    override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) {
        Log.i("TEST", "onError in codec")
    }

}

此时我的困惑是,ByteBuffer 中的值是否正确编码?在将MediaCodec 传递给编码之前,我是否需要使用MediaExtractor 来清理来自输入Surface 的数据?管道足够干净,但我不太确定什么是必要的。这个document 一直是最大的指南,它提到MediaCodec 对原始数据进行操作,这让我觉得我需要MediaExtractor,但这并不需要Surface 作为输入来制定正确的顺序管道中的项目更加令人困惑。

【问题讨论】:

    标签: android android-camera android-camera2 video-encoding android-mediacodec


    【解决方案1】:

    您不需要 MediaExtractor - 它用于处理完整的容器文件并拆分其各种流和其他组件。

    MediaCodec 直接从相机接收原始图像缓冲区,并将输出编码缓冲区。如果要保存标准视频文件,则需要将这些编码的 ByteBuffers 提供给 MediaMuxer 实例。如果您只是将编码缓冲区发送到其他地方进行解码(例如视频聊天应用程序),您可以将 ByteBuffers 提供给目的地的 MediaCodec。

    我无法确定您对 MediaCodec 的所有参数是否正确,但我看不出有任何明显错误。

    【讨论】:

    • 原来我创建MediaCodec 的方式是错误的:MediaCodec.createEncoderByType("video/avc") 修复了它,而不是上面的MediaCodec.createByCodecName(encoderName)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-03
    • 2012-11-07
    • 1970-01-01
    • 2011-04-02
    • 2013-04-07
    • 2015-12-04
    相关资源
    最近更新 更多