FFmpeg 中的 Android MediaCodec

https://www.jianshu.com/p/5beafbb699e9

 

FFmpeg 中的 Android MediaCodec

96 GeorgeMR 关注

 0.9 2018.12.07 20:55 字数 466 阅读 626评论 0喜欢 5

MediaCodec 类可以用来访问底层媒体编解码器,即编码器/解码器的组件。它是 Android 底层多媒体支持架构的一部分。

mediacodec.png

一个编解码器处理输入数据以生成输出数据。它异步地处理数据,并使用一组输入和输出缓冲器。

调用的时候需要先初始化 MediaCodec 作为视频的编码器,然后只需要不停传入原始的 YUV 数据进入编码器就可以直接输出编码好的 h264 流。解码器则对应相反。

数据类型(Data Types)

编解码器对 3 种数据进行操作:压缩后的数据,原始音频数据和原始视频数据。可以使用 ByteBuffers 处理所有三种数据,但对原始视频数据,可以使用 Surface 提高编解码的性能。Surface 使用本地视频缓冲区而不是映射或复制到 ByteBuffers,因此,效率更高。(通常在使用 Surface 时无法访问原始视频数据,但可以使用 ImageReader 类访问不安全的解码(原始)视频帧)。

压缩缓冲区(Compressed Buffers)

输入缓冲器和输出缓冲器根据格式的类型来存放已压缩的数据。对于视频类型,这是一个单一的压缩后的视频帧。对于音频数据,通常是单个访问单元(一个编码后的音频片段通常包含几毫秒音频)。

FFmpeg 中的 MediaCodec

FFmpeg 中仅支持 Android MediaCodec 解码,本文主要介绍 FFmpeg 中关于 MediaCodec 解码流程。后面会介绍如何在 FFmpeg 中添加 MediaCodec 编码。

解码流程:

  1. mediacodec_deocde_init (初始化 MediaCodec 解码器)

FFmpeg 中 MediaCodec 解码器初始化.png

stativ av_cold int mediacodec_decode_init(AVCodecContext *avctx)
{
    // MediaCodec H.264 解码相关属性
    MediaCodecH264DecContext *s = avctx->priv_data;
    
    // 调用 jni 创建 MediaFormat
     FFAMediaFormat *format = ff_AMediaFormat_new();   
    
    // 根据 codec_id 获取 mimeType 
    const char *codec_mime = "video/avc";
    // 获取 AVCodecContext 的 Extreadata 中的 sps & pps,存入H.264文件的头部 
    ret = h264_set_extradata(avctx, format);
    
    // 设置 MediaFormat 属性
    ff_AMediaFormat_setxxx(format, "xxx", xxx);
    
    ret = ff_mediacodec_dec_init(avctx, s->ctx)
}
  1. mediacodec_decode_frame (解码方法)

    FFmpeg 中 MediaCodec 解码.png

static int mediacodec_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt)
{
    // mediacodec 私有参数
     MediaCodecH264DecContext *s = avctx->priv_data;
    // 解码得到的数据
    AVFrame *frame    = data;
    int ret;   
    
    // 将要解码的 AVPacket 写入 fifo
    av_fifo_generic_write(s->fifo, &input_pkt, sizeof(input_pkt), NULL);
    
    while (!*got_frame) {
        /* prepare the input data */
        if (s->buffered_pkt.size <= 0) {
            // 从 fifo 读到 buffered_pkt
            av_fifo_generic_read(s->fifo, &s->buffered_pkt, sizeof(s->buffered_pkt), NULL);
        }
     // 解码
        ret = mediacodec_process_data(avctx, frame, got_frame, &s->buffered_pkt);
    }
}
  1. ff_mediacodec_dec_decode (解码)

    ff_mediacodec_dec_decode.png

int ff_mediacodec_dec_decode(AVCodecContext *avctx, MediaCodecDecContext *s,
                             AVFrame *frame, int *got_frame,
                             AVPacket *pkt)
{
    while(...) {
        // 查找是否有可用的 输入缓冲区
        index = ff_AMediaCodec_dequeueInputBuffer(codec, input_dequeue_timeout_us);
        
        // 获取输入缓冲区
        data = ff_AMediaCodec_getInputBuffer(codec, index, &size);
        
        if (need_draining) {
            // 发送 结束信号
            status = ff_AMediaCodec_queueInputBuffer(codec, index, 0, 0, pts, flags);
        } else {
            // 传送 要解码的数据
            status = ff_AMediaCodec_queueInputBuffer(codec, index, 0, size, pts, 0);
        }
    }
    
    // 获取输出缓冲区
    index = ff_AMediaCodec_dequeueOutputBuffer(codec, &info, output_dequeue_timeout_us);
    if (index >= 0) {
        if (info.size) {
            if (s->surface) {
                // 直接显示
                ret = mediacodec_wrap_hw_buffer(avctx, s, index, &info, frame);
            } else {
                // 获取解码后的数据
                data = ff_AMediaCodec_getOutputBuffer(codec, index, &size);
             // 转换格式
                ret = mediacodec_wrap_sw_buffer(avctx, s, data, size, index, &info, frame);
            }
        } else {
            // 没有输出数据,释放缓冲区
            status = ff_AMediaCodec_releaseOutputBuffer(codec, index, 0);
        }else if (ff_AMediaCodec_infoOutputFormatChanged(codec, index)) {
            // 输出格式已更改
            
            // 删除当前输出格式
            status = ff_AMediaFormat_delete(s->format);
            // 使用新的输出格式
         s->format = ff_AMediaCodec_getOutputFormat(codec);
        } else if (ff_AMediaCodec_infoOutputBuffersChanged(codec, index)) {
            // 输出缓冲区已更改
            ff_AMediaCodec_cleanOutputBuffers(codec);
        } else if (ff_AMediaCodec_infoTryAgainLater(codec, index)) {
            // 超时
        } else {
            // 解码错误
            av_log(avctx, AV_LOG_ERROR, "Failed to dequeue output buffer (status=%zd)\n", index);
            return AVERROR_EXTERNAL;
        }

    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值