视频录制——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实例不可再用。

你可能感兴趣的:(视频录制——3.输出到编码器)