面试官: 谈谈FFmpeg的解码流程? 说说你能够认识的函数作用
心理分析:基本上只要是做视频 音频 这是一个避不开的话题。如果回答的好 基本是一个加分项。没有多少开发者能够把它弄懂。面试官问这个问题的时候是非常期待的。如果你能准确的说出来 已经成功了一大半接下来,会问你解码的过程,那些函数的作用 重点再用上
求职者: 大致了解相关的过程即可
1.1 音频解码过程
音频解码过程如下图所示:
1.2. FFmpeg流程
1.3 关键函数说明:
-
avcodec_find_decoder
:根据指定的AVCodecID查找注册的解码器。 -
av_parser_init
:初始化AVCodecParserContext。 -
avcodec_alloc_context3
:为AVCodecContext分配内存。 -
avcodec_open2
:打开解码器。 -
av_parser_parse2
:解析获得一个Packet。 -
avcodec_send_packet
:将AVPacket压缩数据给解码器。 -
avcodec_receive_frame
:获取到解码后的AVFrame数据。 -
av_get_bytes_per_sample
: 获取每个sample中的字节数
1.4 关键数据结构说明:
-
AVCodecParser
:用于解析输入的数据流并把它分成一帧一帧的压缩编码数据。比较形象的说法就是把长长的一段连续的数据“切割”成一段段的数据。
1.5 avcodec编解码API介绍
avcodec_send_packet、avcodec_receive_frame的API是FFmpeg3版本加入的。为了正确的使用它们,有必要阅读FFmpeg的文档说明[2]。
FFmpeg提供了两组函数,分别用于编码和解码:
- 解码:
avcodec_send_packet()
、avcodec_receive_frame()
。 - 解码:
avcodec_send_frame()
、avcodec_receive_packet()
。
API的设计与编解码的流程非常贴切。
建议的使用流程如下:
- 像以前一样设置并打开
AVCodecContext
。 - 输入有效的数据:
- 解码:调用
avcodec_send_packet()
给解码器传入包含原始的压缩数据的AVPacket
对象。 - 编码:调用
avcodec_send_frame()
给编码器传入包含解压数据的AVFrame
对象。 - 两种情况下推荐
AVPacket
和AVFrame
都使用refcounted
(引用计数)的模式,否则libavcodec
可能需要对输入的数据进行拷贝。
1.6在一个循环体内去接收codec的输出,.
即周期性地调用avcodec_receive_()
来接收codec输出的数据:
-
解码:调用
avcodec_receive_frame()
,如果成功会返回一个包含未压缩数据的AVFrame
。 -
编码:调用
avcodec_receive_packet()
,如果成功会返回一个包含压缩数据的AVPacket
。 -
反复地调用
avcodec_receive_packet()
直到返回AVERROR(EAGAIN)
或其他错误。返回AVERROR(EAGAIN)
错误表示codec需要新的输入来输出更多的数据。对于每个输入的packet或frame,codec一般会输出一个frame或packet,但是也有可能输出0个或者多于1个。
- 流处理结束的时候需要flush(洗刷) codec。因为codec可能在内部缓冲多个frame或packet,出于性能或其他必要的情况(如考虑B帧的情况)。 处理流程如下:
- 调用
avcodec_send_()
传入的AVFrame或AVPacket指针设置为NULL。 这将开启draining mode(排水模式)。 - 反复地调用
avcodec_receive_()
直到返回AVERROR_EOF
的错误,这个方法这个时候不会返回AVERROR(EAGAIN)
的错误,除非你忘记了开启draining mode。 - codec可以重新开启,但是需要先调用
avcodec_flush_buffers()
来重置codec。
说明:
- 编码或者解码刚开始的时候,codec可能接收了多个输入的frame或packet后还没有输出数据,直到内部的buffer被填充满。上面的使用流程可以处理这种情况。
- 理论上调用
avcodec_send_()
的时候可能会发生AVERROR(EAGAIN)
的错误,这只应该在有输出数据没有被接收的情况,你可以依赖这个机制来实现区别于上面建议流程的处理方式,比如反复地调用avcodec_send_()
,出现AVERROR(EAGAIN)
错误的时候再去调用avcodec_receive_()
。 - 并不是所有的codec都遵循一个严格、可预测的数据处理流程,唯一可以保证的是 “调用
avcodec_send_\()/avcodec_receive_\()
返回AVERROR(EAGAIN)
的时候去avcodec_receive_\()/avcodec_send_\()
会成功,否则不应该返回AVERROR(EAGAIN)
的错误。”一般来说,任何codec都不允许无限制地缓冲输入或者输出。 - 在同一个
AVCodecContext
上混合使用新旧API是不允许的,这将导致未定义的行为。
更多面试内容,面试专题,flutter视频 全套,音视频从0到高手开发。
关注GitHub:https://github.com/xiangjiana/Android-MS
免费获取面试PDF合集
网友评论