本文主要适用于输出为固定帧率的情况。
包括帧率转换但输出帧率是固定帧率的情况。
![](https://img.haomeiwen.com/i1720840/736b2eda22802c84.png)
![](https://img.haomeiwen.com/i1720840/0a0d594481b0067c.png)
ost->sync_opts
![](https://img.haomeiwen.com/i1720840/b8f56e449cd80908.png)
delta0 & delta
// delta0 is the "drift" between the input frame (next_picture)
// and where it would fall in the output.
delta0 = sync_ipts - ost->sync_opts;
delta = delta0 + duration;
/* by default, we output a single frame */
nb0_frames = 0; // tracks the number of times the PREVIOUS frame should be duplicated,
// mostly for variable framerate (VFR)
nb_frames = 1;
![](https://img.haomeiwen.com/i1720840/1ab8f5aafb071c18.png)
// duration的计算
// 通过原素材的帧率计算。
frame_rate = av_buffersink_get_frame_rate(filter);
if (frame_rate.num > 0 && frame_rate.den > 0)
{
duration = 1/(av_q2d(frame_rate) * av_q2d(enc->time_base));
}
// 通过目标帧率进行计算。
if(ist && ist->st->start_time != AV_NOPTS_VALUE && ist->st->first_dts != AV_NOPTS_VALUE && ost->frame_rate.num)
{
duration = FFMIN(duration, 1/(av_q2d(ost->frame_rate) * av_q2d(enc->time_base)));
}
// 通过当前帧的next_picture->pkt_duration进行计算。
if (!ost->filters_script &&
!ost->filters &&
next_picture &&
ist &&
lrintf(next_picture->pkt_duration * av_q2d(ist->st->time_base) / av_q2d(enc->time_base)) > 0) {
duration = lrintf(next_picture->pkt_duration * av_q2d(ist->st->time_base) / av_q2d(enc->time_base));
}
nb0_frames & nb_frames
![](https://img.haomeiwen.com/i1720840/d28c0c7d9c5b7e09.png)
调整
![](https://img.haomeiwen.com/i1720840/ded187702de258b6.png)
// 进行调整
if (delta0 < 0 &&
delta > 0 &&
format_video_sync != VSYNC_PASSTHROUGH &&
format_video_sync != VSYNC_DROP) {
if (delta0 < -0.6) {
av_log(NULL, AV_LOG_VERBOSE, "Past duration %f too large\n", -delta0);
} else
av_log(NULL, AV_LOG_ERROR, "Clipping frame in rate conversion by %f\n", -delta0);
sync_ipts = ost->sync_opts;
duration += delta0;
delta0 = 0;
}
计算nb_frames和nb0_frames
// 计算nb_frames和nb0_frames
switch (format_video_sync) {
case VSYNC_VSCFR:
if (ost->frame_number == 0 && delta0 >= 0.5) {
av_log(NULL, AV_LOG_DEBUG, "Not duplicating %d initial frames\n", (int)lrintf(delta0));
delta = duration;
delta0 = 0;
ost->sync_opts = lrint(sync_ipts);
}
case VSYNC_CFR:
// FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c
if (frame_drop_threshold && delta < frame_drop_threshold && ost->frame_number) {
nb_frames = 0;
} else if (delta < -1.1)
{
nb_frames = 0;
}
else if (delta > 1.1) {
nb_frames = lrintf(delta);
if (delta0 > 1.1)
{
nb0_frames = lrintf(delta0 - 0.6);
}
}
break;
case VSYNC_VFR:
if (delta <= -0.6)
nb_frames = 0;
else if (delta > 0.6)
ost->sync_opts = lrint(sync_ipts);
break;
case VSYNC_DROP:
case VSYNC_PASSTHROUGH:
ost->sync_opts = lrint(sync_ipts);
break;
default:
av_assert0(0);
}
丢帧策略
![](https://img.haomeiwen.com/i1720840/81883a4045022235.png)
-frame_drop_threshold parameter
Frame drop threshold, which specifies how much behind video frames can be before they are dropped.
In frame rate units, so 1.0 is one frame.
The default is -1.1.
One possible usecase is to avoid framedrops in case of noisy timestamps or to increase frame drop precision in case of exact timestamps.
![](https://img.haomeiwen.com/i1720840/66a689bfd7770f8e.png)
![](https://img.haomeiwen.com/i1720840/df45ba9f66de8f08.png)
补帧策略
![](https://img.haomeiwen.com/i1720840/00f851a090e81c22.png)
保存当前帧的数据
![](https://img.haomeiwen.com/i1720840/2f219c461666c6a6.png)
保存当前帧(Frame0)的数据,因为下一帧(Frame1)处理的时候,会判断是否编码Frame0还是彻底丢弃。
编码
![](https://img.haomeiwen.com/i1720840/30f21f6491aa66ec.png)
References:
https://blog.csdn.net/sidumqz/article/details/53102623
https://www.jianshu.com/p/a1ad543a0d0e
https://www.cnblogs.com/jingzhishen/p/3767342.html
http://www.rosoo.net/a/201107/14663.html
http://www.ishenping.com/ArtInfo/353415.html
http://ffmpeg.org/pipermail/ffmpeg-cvslog/2014-November/083387.html
https://blog.csdn.net/newchenxf/article/details/51777891
https://www.jianshu.com/p/27279255f67e
https://codertw.com/%E4%BA%BA%E5%B7%A5%E6%99%BA%E6%85%A7/130033/
https://www.jianshu.com/p/27279255f67e
网友评论