- 数据怎么送进编码器?
- 怎么从编码器取数据?
- 如何做流控?
在开始之前,我们先了解一下 MediaCodec 的基本知识。
MediaCodec 基础
Developer 官网 上的描述已经很清楚了,下面简要总结一下。
首先是工作流程:
生产者不断把输入数据送进 codec,消费者则不断消费 codec 的输出数据。
接下来是调用流程:
- 选择编码器:根据 mimeType 和 colorFormat,以及是否为编码器,选择出一个 MediaCodecInfo;
- 创建编码器:MediaCodec.createByCodecName(codecInfo.getName());
- 对于 API 21 以上的系统,我们可以选择异步消费输出:mVideoCodec.setCallback;
- 配置编码器:设置各种编码器参数(MediaFormat),再调用 mVideoCodec.configure(文档也没有明确说 setCallback 应该在 configure 之前,但既然示例是这样写的,我们还是保持这样好了,毕竟相机采集也是踩过坑了的);
- 对于 API 19 以上的系统,我们可以选择 Surface 输入:mVideoCodec.createInputSurface;
- 启动编码器:mVideoCodec.start;
- 输入数据到编码器:输入数据到来时,Surface 输入模式下(提前用 Surface 创建 EGLSurface),调用 GLES API 绘制,最后 eglSwapBuffers 即可;普通模式下我们需要 dequeueInputBuffer、填入数据、queueInputBuffer;
- 消费编码器输出数据:异步模式下,我们在 onOutputBufferAvailable 中使用 buffer 内的数据,然后 releaseOutputBuffer 即可;同步模式下我们需要 dequeueOutputBuffer、使用 buffer 内的数据、releaseOutputBuffer;
- 停止并销毁编码器:先告知编码器我们要结束编码,Surface 输入时调用 mVideoCodec.signalEndOfInputStream,普通输入则可以为在 queueInputBuffer 时指定 MediaCodec.BUFFER_FLAG_END_OF_STREAM 这个 flag;告知编码器后我们就可以等到编码器输出的 buffer 带着 MediaCodec.BUFFER_FLAG_END_OF_STREAM 这个 flag 了,等到之后我们调用 mVideoEncoder.release 销毁编码器;
简单了解了 MediaCodec 基础之后,我们就可以开始看看 WebRTC 是怎么做硬编码的了。
数据怎么送进编码器?
WebRTC 的硬编码封装在 MediaCodecVideoEncoder 类中,只看 Java 代码我们会发现这个类的方法基本都是 package private 的,而且并没有被其他类调用过,一开始我也很疑惑,但当我开始看 native 代码的时候就发现了蹊跷。
原来对 MediaCodecVideoEncoder 接口的调用都发生在 native 层,就在 webrtc/sdk/android/src/jni/androidmediaencoder_jni.cc 这个文件中。Java 调用 native 代码想必大家都知道,但 native 代码