工作过程中,遇到了视频播放的问题,由于之前没有接触过音视频的编解码,所以,花了较长时间来分析解决问题。因此,针对音视频的编解码进行梳理总结,为后续进一步研究做好储备。

本文首先简单介绍了ffmpeg, 然后再说明项目中遇到的两个编解码问题,由此梳理编解码的基本知识点,最后再介绍ffmpeg命令工具的使用,方便后续快速定位问题。

ffmpeg简介

FFmpeg是一个自由软件,可以运行音频和视频多种格式的录影、转换、流功能,并提供音视频的编解码库。

无法解析最后一帧

使用ffmpeg解码mp4格式的视频时,调用接口avcodec_receive_frame, 在解析到最后一帧的时候,返回信息AVERROR(EAGAIN)的错误, 并且都是在最后一帧出现该错误。

ffmpeg开启硬解码(ffmpeg编解码问题的经验总结)(1)

搜索网络上对AVERROR(EAGAIN)错误的说明,大致说法是,codec需要新的输入来输出更多的数据,因此尝试重新avcodec_send_packet包,然后再调用avcodec_receive_frame,但是最后一帧仍然没有获取成功。

因为每次都是最后一帧没有解析成功,那么推测是不是缓存数据没有刷出来。所以尝试调用avcodec_send_packet(p_ctx, NULL), 最后一个参数为NULL, 表示刷新缓存数据。但是实际验证结果,还是不成功。

另外,从官网上查看av_read_frame的功能,它能确保每次都能收到一帧视频帧,所以上面两种尝试方式似乎都不成立。

ffmpeg开启硬解码(ffmpeg编解码问题的经验总结)(2)

最后一位同事发现是在编码生成MP4格式的时候,帧率被覆盖,没有生效导致。而解决办法是:写帧头avformat_write_header之前,设置以下代码,强制设置帧率为25。

ffmpeg开启硬解码(ffmpeg编解码问题的经验总结)(3)

按照上面方式的处理,问题虽然已经解决,但是根本原因还是没有弄明白,所以,对于编解码的原理还是需要更深入的研究。

播放先慢后快

最近使用FFmpeg解码播放视频的时候,视频播放的过程中,出现了先慢后快的现象。初步分析,当编码的时候设置了包的dts之后,出现明显了先慢后快的现象。由于编码入手分析, 找不到根源,所以从解码方向进行分析。

首先说明下,视频播放的逻辑是,打开视频之前,先初始化相关的环境、内容上下文等,并读取一帧显示的时长,然后设置定时器,每隔一帧的时长,调用接口av_read_frame读取一帧,并进行显示。注意这个过程都是在主线程执行的,另外还有一点是,没有处理音频帧,只处理视频帧。

经过分析调试,发现av_read_frame如果读取到音频帧,那么直接返回,并等待下一个定时器的执行,读取过程中,还发现av_read_frame会连续读取到多个音频帧,然后才读取到视频帧,而视频最后没有音频,那么就能只读取到视频帧并进行处理,所以,这就导致播放的时候先慢后快的现象。

其实解帧代码本身是有问题的,只是编码没有设置dts的时候,音频是均匀分布的,但是加入dts之后,导致音频帧都堆积到前面读取,另外,还发现这个会导致mp4的den数据加赔,即原来是16,那么就变成32。

最后的解决办法是,因为程序本身不支持音频,所以av_read_frame如果读取到音频帧,则直接跳过,不进入定时器。这只是临时解决方案,根本的解决办法,应该将解码放入线程中执行。

ffmpeg开启硬解码(ffmpeg编解码问题的经验总结)(4)

基本知识点

1、 编码的一般流程

写文件头,avformat_write_header

写包数据,av_write_frame/av_interleaved_write_frame

写文件尾,av_write_trailer

2、PTS和DTS的概念

PTS:Presentation Time Stamp, 播放时间戳,用于度量解码后的视频帧什么时候显示。

DTS:Decode Time Stamp, 解码时间戳,标示读入内存中的流什么时候送入解码器中进行解码。

3、帧率

一帧多少秒: r_frame_rate.den/r_frame_rate.num

每秒多少帧: r_frame_rate.num/r_frame_rate.den

4、帧率与time_base关系

AVCodecContext 中的time_base,缩略词为 tbc

AVStream中的time_base,缩略词为tbn

AVStream中的r_frame_rate, 即帧率,表示每秒播放多少帧, 缩略词为tbr

通常来说, tbc等于tbn, tbn = 1/tbr, 而解码的时候,一般是解析AVStream中的tbr。

命令工具

1、ffmpeg的解包生成jpg图片,其中25表示帧率,mp4_pic为文件夹

ffmpeg -i “xxx.mp4” -r 25 mp4_pic/frameM.jpg

2、ffprobe查看帧格式信息

ffprobe -show_format -show_streams xxx.mp4

3、播放视频

ffplay xxx.mp4

4、转换帧率

ffmpeg xxx_old -r 25 xxx_new

5、ffprobe -show_streams xxx.MP4

ffmpeg开启硬解码(ffmpeg编解码问题的经验总结)(5)

概括总结

基于ffmpeg,进行编解码的时候,如果只是简单实现,那么只要参照官方例子进行实现即可。但是实际应用过程中,如果对编解码的原理理解不够,那么遇到问题的时候,就会无从下手。

本文只是对ffmpeg使用过程中进行一个初步的总结,其中还有很多疑问待进一步分析研究。

如果遇到编解码问题,一般的分析手段是,首先利用ffmpeg提供的工具来查看视频中相关信息,确认编码是否正确。如果编码正确,那么就需要从解码入手分析。而如果怀疑编码问题,那么可以将编码后的视频上传到PC上,然后使用主流的播放器来看看播放的效果。如果PC播放有问题,那么大概率是编码问题。

,