time_base:
含义:时间基数,可以理解为刻度,或者时间轴上面的小格子,这个小格子具体是多少,要根据time_base在不同的结构中有不同的数值。
哪些结构中有time_base
1、AVFormatContext:中streams:当在解封装的时候,AVFormatContext的streams中的time_base是从rtsp流或者媒体文件流中获取,因为avformat_find_stream_info会填充AVFormatContext中的相相关信息。此时time_base可能是如下:
也就是1/90000秒,也就是此时time_base在时间轴上每个格子大小是1/90000.而此时音视频的数据的状态是AVPacket状态,也就是处于压缩状态。在此时packet的dts、pts就是以这个刻度(1/90000)来作为度量的,比如:
那么这个pts=42902和dts=42902是什么意思呢?
pts:present time stamp:显示时间戳 :42902代表,在音视频显示时间轴上42902*1/90000秒的时候显示这个packet的内容
dts:decode time stamp:解码时间戳:42902代表,在音视频解码时间轴上42902*1/90000秒的时候解码这个packet的内容
所以要正确的处理一帧数据,time_base、dts、pts三个缺一不可。而且这三个值在不同状态(解封装、解码、编码、封装)是不同的。三个值在不同的状态下是绑定出现的。
上面注意到packet结构中还有个duration字段,这个字段的意思是:两帧之间的间隔,也是以time_base为刻度的。是下一帧-当前帧的间隔,上图中duration=7500表示下一帧-当前帧的间隔占了7500个格子也就是7500个1/90000。转化为秒的话就是7500*1/90000
所以,在解封装的时候,必须将time_base保存下来,后面的解码、编码、封装、推流等操作都需要这个time_base
2、在解码时:
在解码阶段time_base:在AVCodecContext中。而这个时间基数是在我们自己在调用avcodec_alloc_context3这个函数创建AVCodeContext后自己设定的。我们比较下在解封装和解码两个阶段通过一个包的dts pts的变化,入下图:是解封装后packet中的pts和dts
而下面这张图是:解码后Frame中的pts:
关于拉流,解码,编码,推流不同阶段的pts、 dts、 durations的转换
问题1:不同阶段time_base是怎么产生的?
* 在拉流阶段:time_base是从文件或者网络流中直接获取的,当打开文件或者网络流后,执行avformat_find_stream_info(),此函数会将相关参数,包括time_base字段内容填充到AVFormatContext中,在AVFormatContext的Strames中。
* 解码阶段:time_base在AVCodecContext结构中,有开发者自己填写,具体填写多少,可以从拉流时AVFormatContext的streams[0]->codec->framerate的倒数作为初始化参数
* 编码:同解码阶段
* 推流:可以从拉流阶段的AVFormatContext的streams[0]->time_base获取
问题2:什么时候需要进行time_base转换
* 解码之前:在调用avcodec_send_packet之前,需要将packet进行以不同事件基数转换,此时packet中国的pts,dts,durations都是以拉流阶段的AVFormatContext.Stream中的time_base为刻度的,需要转换为以解码上下文AVCodecContext.time_base为刻度的pts、dts、durations
* 编码之后,av_interleaved_write_frame函数调用之前,此时需要将以AVCodecContext.time_base为刻度的pts、dts、duration转化为以封装器AVFormatContext.streams中的time_base为刻度的pts,dts、duration
问题3:使用什么函数转换
调用av_packet_rescale_ts(packet,原来的time_base,将要转换的time_base),此函数会转化dts、duration如果B帧为0此时dts=pts
*封装格式的time_base
在封装的时候,调用avformat_alloc_output_context2这个函数的时候,需要指定封装格式,调用后,FFmpeg会自动填充time_base,此时time_base在AVFormatContext.streams中,不需要调用者,自己填写封装格式的time_base
对于拉流,解码,编码,推流过程中的pts\dts\duration的转换
*在执行avcodec_send_packet执行函数之前,需要将av_read_frame读取到的AVPacket进行转化,转换之前,AVPacket中的pts\dts\duration都是以解封装上下文AVFormatContext.streams[]中的time_base为刻度的。因为需要解码,所以要把AVPacket中的pts\dts\duration转为以解码上下文AVCodecContext.time_base为刻度的值。而AVCodecContext.time_base值的设定,需要程序员自己设置,那么设置多少合适呢?可以设置为解封转上下文AVFormatContext.stream[].avg_frame_rate.num/AVFormatContext.stream[].avg_frame_rate.den也就相当于输入流的fps.
*执行完上述步骤后,AVPaket中的pts\dts\duration就是以解码上下AVFormatContext.time_base为刻度的了。然后正常执行avcodec_receive_frame,得到解码后的AVFrame,可以进行二次创作,比如添加字体,缩放图像等操作,继而进行编码,编码后得到等待推流的AVPacket,这个过程中,pts\dts\duration不会发生改变。
*推流,在执行av_interleaved_write_frame之前,需要将编码后的AVPacket进行time_base转化,因为,封装器的time_base是根据封装格式确定,如上图。所以要将编码后的AVPacket转化为以封装器time_base为刻度的值。转换后在调用av_interleaved_write_frame,发送。
*特别需要注意,如果在转化过程中,time_base使用错误,将会导致播放时常,比如播放延迟越来越大等等。
*相关函数:av_packet_rescale_ts函数会同时转化pts\sts\duration
如果想分开转换,可以用av_rescale_q_rnd。
关于ffmpeg拉流延迟
关于ffmpeg编码延迟
关于FFmpeg编码质量
关于specified frame type (3) at 358 is not compatible with keyframe interval警告的解决方法
解决出现Could not find ref with POCXX问题解决方法
关于RTSP推流:
RTMP推流封装格式是FLV。RTMP官方FFmpeg不支持H265推流。
如果要用RTSP推流的话,封装格式设置为:rtsp,但是推流地址应该是rtp://ip:port格式,url不是rtsp://XXX,应该是rtp://XXX.如果使用rtsp://XXX ffmpeg将会提示 protocol not found.而且不会推流成功!
关于推流GOP:
GOP:Group Of Picture:就是图片组,一般一个图片组的第一针是I帧(关键帧,帧内解码),后面跟随的是P帧(Prediction预测帧:该帧需要在前一帧(I或者P)解码出来的图像的基础上叠加差异图像,从而得出当前P帧的完成图像),GOP决定了每个多少帧有一个I帧。GOP将会影响编码速度。GOP越小,关键帧越多,编码速度越慢,因为关键帧越多,需要处理的编码数据量越大,编码速度越慢。反之,GOP越大,I帧越少,P帧越多,P帧是当前帧和前一帧的差异,所以编码时只要处理差异数据,编码数量就会小的多,编码速度就会越快。占用的cpu资源(软件码时),就越少。
关于Could not find ref with POC XX
当输入跟不上编解码的时候就会打印这些,假如说你能控制输入,当你把输入速率调低,比如10p/s那么就会出现上述现象,当调为30p/s就不会出现,所以关键是输入,至于怎么解决这个问题,根据不同的情况解决就好