美文网首页
视频录制——3.输出到编码器

视频录制——3.输出到编码器

作者: 猫侠 | 来源:发表于2017-09-28 13:32 被阅读0次

    输出到编码器,和输出到屏幕一样,就是输出到编码器提供的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.输出到编码器

          本文链接:https://www.haomeiwen.com/subject/sfoqcxtx.html