Camera2 和 MediaCodec录制mp4

本文的主要内容是通过 Android 原生的硬编解码框架 MediaCodec 和复用器 MediaMuxer 实现 mp4 视频文件的录制,视频数据源由 Camera2 来提供,这里重点是编码、复用的这个过程而不是 mp4 的录制,如果仅仅是视频录制,可以选择更方便的 MediaRecorder,按照惯例还是以案列的形式学习 MediaCodec,其更多用法将在后续的文章中介绍。

Camera2的使用

Camera2 是从 Android 5.0 开始推出的新的相机 API,最新的是 CameraX,CameraX 基于 Camera2,相较 Camera2 提供了更好用的 API,后文中涉及到的相关的 API 可以直接参考下面这张示意图,这也是 Camera2 的使用示意图,如下:

图片

MediaCodec的输入方式

为了能够使用 MediaCodec 进行编码操作,就需要将相机的数据输入到编码器 MediaCodec 中,可以通过两种方式将数据写入 MediaCodec,具体如下:

  • Surface:使用 Surface 作为编码器 MediaCodec 的输入,即将 MediaCodec 创建的 Surface 作为其输入,这个 Surface 由 MediaCodec 的 createInputSurface 方法创建,当相机将会将有效数据渲染到该 Surface 中,MediaCodec 就可以直接输出编码后的数据了。
  • InputBuffer:使用输入缓冲区作为编码器 MediaCodec 的输入,这里需要填充的数据就是原始帧数据,对应 Camera2 来说可直接通过 ImageReader 来进行帧数据的获取,获取到的 Image 包含了宽度、高度、格式、时间戳及 YUV 数据分量等信息,可控程度更高。

MediaCodec编码Camera2数据

简单说一下 MediaCodec 的数据处理方式,Android 5.0 之前只支持 ByteBuffer[] 的同步方式,之后推荐使用 ByteBuffer 的同步、异步方式,这里使用 ByteBuffer 的同步方式,涉及的流程主要是视频数据编码和复用,因为前面提到 MediaCodec 的输入是通过 Surface 完成的,所以这里只需要获取的已经编码好的数据和使用复用器 MediaMuxer 来实现 Mp4 文件的生成,关键代码如下:

 1 // 返回已成功编码的输出缓冲区的索引
 2 var outputBufferId: Int = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0)
 3 if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
 4    // 添加视频轨道
 5    mTrackIndex = mMediaMuxer.addTrack(mMediaCodec.outputFormat)
 6    mMediaMuxer.start()
 7    mStartMuxer = true
 8 } else {
 9    while (outputBufferId >= 0) {
10        if (!mStartMuxer) {
11            Log.i(TAG, "MediaMuxer not start")
12            continue
13        }
14        // 获取有效数据
15        val outputBuffer = mMediaCodec.getOutputBuffer(outputBufferId) ?: continue
16        outputBuffer.position(bufferInfo.offset)
17        outputBuffer.limit(bufferInfo.offset + bufferInfo.size)
18        if (pts == 0L) {
19            pts = bufferInfo.presentationTimeUs
20        }
21        bufferInfo.presentationTimeUs = bufferInfo.presentationTimeUs - pts
22        // 将数据写入复用器以生成文件
23        mMediaMuxer.writeSampleData(mTrackIndex, outputBuffer, bufferInfo)
24        Log.d(
25            TAG,
26            "pts = ${bufferInfo.presentationTimeUs / 1000000.0f} s ,${pts / 1000} ms"
27        )
28        mMediaCodec.releaseOutputBuffer(outputBufferId, false)
29        outputBufferId = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0)
30    }
31} 

录制流程

这里使用 Surface 作为编码器 MediaCodec 的输入,在 MediaCodec 进入配置状态才可以创建 Surface,也就是 createInputSurface 只能在 configure 与 start之间调用,参考如下:

1 // 配置状态
2 mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
3 // 创建Surface作为MediaCodec的输入,createInputSurface只能在configure与start之间调用创建Surface
4 mSurface = mMediaCodec.createInputSurface()
5 // start ...

将其添加到 SessionConfiguration 的输出 Surface 列表中,参考如下:

 1 // 创建CaptureSession
 2 @RequiresApi(Build.VERSION_CODES.P)
 3 private suspend fun createCaptureSession(): CameraCaptureSession = suspendCoroutine { cont ->
 4    val outputs = mutableListOf<OutputConfiguration>()
 5    // 预览Surface                                                                              
 6    outputs.add(OutputConfiguration(mSurface))
 7    // 添加MediaCodec用作输入的Surface                                                                               
 8    outputs.add(OutputConfiguration(EncodeManager.getSurface()))
 9    val sessionConfiguration = SessionConfiguration(
10        SessionConfiguration.SESSION_REGULAR,
11        outputs, mExecutor, ...)
12    mCameraDevice.createCaptureSession(sessionConfiguration)
13 }

然后发起 CaptureRequest 开启预览和接收 Surface 输出,同时开启编码,参考如下:

 1 // 添加预览的Surface和生成Image的Surface
 2 mCaptureRequestBuild = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
 3 val sur = EncodeManager.getSurface()
 4 mCaptureRequestBuild.addTarget(sur)
 5 mCaptureRequestBuild.addTarget(mSurface)
 6
 7 // 设置各种参数
 8 mCaptureRequestBuild.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, 1) // 视频稳定功能是否激活
 9 // 发送CaptureRequest
10 mCameraCaptureSession.setRepeatingRequest(
11    mCaptureRequestBuild.build(),
12    null,
13    mCameraHandler
14)
15 // 开始编码
16 EncodeManager.startEncode()

作者:jzman | 公众号——躬行之,可在公众号后台回复关键字【record】获取源码。

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论