视频录制——3.输出到编码器
输出到编码器,和输出到屏幕一样,就是输出到编码器提供的Surface。
MediaCodec、MediaFormat既是编码,也是解码;既可用于音频,也可用于视频;可以建立多个实例
创建MediaCodec
class MediaCodec{
// type:输出数据的mime type。为了避免出现不支持的mime type,用下面的方法
public static MediaCodec createEncoderByType(@NonNull String type)
// name:编/解码器的名字,可以由下面的方法得到
// public static MediaCodec createByCodecName(@NonNull String name)
// format:媒体格式
public final String findEncoderForFormat(MediaFormat format)
}
创建MediaFormat
键 | 含义 |
---|---|
width | 视频宽 |
height | 视频高 |
color-format | 如何表示一个像素的颜色值 |
frame-rate | 帧率 |
capture-rate | 曝光率。如果和帧率不同,或造成慢动作或快进的效果 |
bitrate | 比特率 |
i-frame-interval | 两个关键帧之间的时间间隔 |
intra-refresh-period | |
repeat-previous-frame-after | 只在surface-input模式有用。一帧之后多少毫秒如果没有新的数据进来,就重复这一帧 |
ts-schema |
MediaFormat本质上是一系列键值对。下面只记录视频编码相关的。
键 | 含义 |
---|---|
width | 视频宽 |
height | 视频高 |
color-format | 如何表示一个像素的颜色值 |
frame-rate | 帧率 |
capture-rate | 曝光率。如果和帧率不同,或造成慢动作或快进的效果 |
bitrate | 比特率 |
i-frame-interval | 两个关键帧之间的时间间隔 |
intra-refresh-period | |
repeat-previous-frame-after | 只在surface-input模式有用。一帧之后多少毫秒如果没有新的数据进来,就重复这一帧 |
ts-schema |
// 创建视频信息
public static final MediaFormat createVideoFormat(String mime, int width, int height)
// 设置key为name的value,value类型为int
public final void setInteger(String name, int value)
配置MediaCodec
// surface:编码器传null
// crypto:不加密传null
// flag:传CONFIGURE_FLAG_ENCODE表示编码器
public void configure(
@Nullable MediaFormat format,
@Nullable Surface surface,
@Nullable MediaCrypto crypto,
@ConfigureFlag int flags)
获取编码器输入Surface
public native final Surface createInputSurface();
给编码器喂数据
mCachedInputBuffers、mDequeuedInputBuffers指向同一内存块。
mCachedInputBuffers包含所有内存块;
mDequeuedInputBuffers包含APP持有的内存块。
在同步模式下,API >= 21
int index = encoder.dequeueInputBuffer();
ByteBuffer buffer = encoder.getInputBuffer(index);
//
// ......填充buffer
//
encoder.queueInputBuffer(buffer);
在同步模式下,API < 21
int index = encoder.dequeueInputBuffer();
ByteBuffer buffer = encoder.getInputBuffers()[index];
//
// ......填充buffer
//
encoder.queueInputBuffer(buffer);
在异步模式下,必须API >= 21
public void onInputBufferAvailable(MediaCodec codec, int index){
ByteBuffer buffer = encoder.getInputBuffer(index);
//
// ......填充buffer
//
encoder.queueInputBuffer(buffer);
}
同步异步的区别在于:同步是由APP主动调用dequeueInputBuffer(),轮询InputBuffer的状态,轮询造成阻塞;异步是由native层的编解码线程向APP发送message,回调InputBuffer的状态,APP不做轮询,不会阻塞。
从编码器取数据
把上一段的Input都换成Output。
标志位
dequeueInputBuffer()/dequeueOutputBuffer()返回的标志位:
INFO_TRY_AGAIN_LATER:没有可用的Buffer。在同步模式下查到这个,就跳出轮询。异步模式下不用这个。
INFO_OUTPUT_FORMAT_CHANGED:输出格式改变了。查到这个,就要重新获取输出格式。
INFO_OUTPUT_BUFFERS_CHANGED:OutputBuffer大小改变了。查到这个,就要重新获取OutputBuffer。异步模式下不用这个。
BufferInfo中的标志位:
BUFFER_FLAG_SYNC_FRAME(deprecated):这一帧是关键帧
BUFFER_FLAG_KEY_FRAME:同上
BUFFER_FLAG_CODEC_CONFIG:这一帧数据不是多媒体数据,而是codec specific data
BUFFER_FLAG_END_OF_STREAM:后面没有数据了。接收到这个,编码器会关闭入口,不再吃数据,除非调用flush()重启
关闭编码器
定义三个状态:初始状态、工作状态、停止状态
new MediaCodec -> config():进入初始状态,还没有申请Buffer
start():申请Buffer,进入工作状态
stop():释放Buffer,进入停止状态
flush():清除InputBuffer和OutputBuffer。同步模式下进入工作状态;异步模式下需要调用start()进入工作状态。
release():释放Buffer,MediaCodec实例不可再用。