ffmpeg转码时对变帧率和固定帧率的处理

此文章分析的是旧的ffmpeg。

一般fps在代码里这样表示
Fps = den/num
如果den = 15,num=1,则fps = 15。
如果帧率固定,pts*fps 就表示当前是第几帧。
当输入视频流的帧率不固定,如rmvb ,而输出视频流的帧率固定,ffmpeg作如下处理(参考ffmpeg代码版本0.6.1):
1、  记录和输出视频流ost相对应的输入视频流ist,变量为ost->sync_ist。这是在av_transcode函数进行输出流初始化时进行的。代码分别为:
    if (ist->discard && ist->st->discard != AVDISCARD_ALL && !skip &&
      ist->st->codec->codec_type == ost->st->codec->codec_type) {
        if(best_nb_frames < ist->st->codec_info_nb_frames){
          best_nb_frames= ist->st->codec_info_nb_frames;
          ost->source_index = j;
          found = 1;
        }
      }
      if (!found) {
       if(! opt_programid) {
          /* try again and reuse existing stream */
          for(j=0;j<nb_istreams;j++) {
          ist = ist_table[j];
          if (   ist->st->codec->codec_type == ost->st->codec->codec_type
       && ist->st->discard != AVDISCARD_ALL) {
          ost->source_index = j;
          found = 1;
          }
          }
          }
          ist = ist_table[ost->source_index];
          ist->discard = 0;
          ost->sync_ist = (nb_stream_maps > 0) ?
    ist_table[file_table[stream_maps[n].sync_file_index].ist_index +
              stream_maps[n].sync_stream_index] : ist;
2、  记录输出视频流ost的时间戳。输出为固定帧率,故可以简化为记录帧数,变量为ost->sync_opts。
3、  将ost对应的ist的pts转换成固定帧率的帧数形式。代码为
    sync_ipts = get_sync_ipts(ost) / av_q2d(enc->time_base);
get_sync_ipts计算ost对应的ist的pts,
av_q2d返回enc->time_base.num/ enc->time_base.den,即1/fps.
4、  将sync_ipts和ost->sync_opts进行求差。
    double vdelta = sync_ipts - ost->sync_opts;
5、  根据vdelta来判断不同的情况。
l  情况一:Vdelta<-1.1,表示当前输入帧的播放时间在当前输出帧的前一帧之前,故舍弃该帧,nb_frames = 0。
l  情况二: (video_sync_method == 2 || (video_sync_method<0 && (s->oformat->flags & AVFMT_VARIABLE_FPS))){
if(vdelta<=-0.6){
nb_frames=0;
}else if(vdelta>0.6)
ost->sync_opts= lrintf(sync_ipts);}
这里video_sync_method==2 和video_sync_method < 0 表示什么意义,不是很清楚。貌似ffmpeg里video_sync_method一直设为-1。AVFMT_VARIABLE_FPS应该是变帧率的意思。这种情况下,vdelta<=0.6,表示位于当前帧之前,也舍弃该帧,nb_frames = 0;vdelta>0.6表示位于当前帧之后,直接把该帧的时间戳作为输出的时间出来输出该帧;0.6<vdelta<=0.6时,不做任何处理,nb_frames 根据默认值为1。
l  情况三:vdelta > 1.1
此时nb_frames = lrintf(vdelta),需要做插帧操作。
Ffmpeg的插帧操作,貌似是把当前输出帧重复输出nb_frames次。
    AVFrame* old_frame = enc->coded_frame;
    enc->coded_frame = dec->coded_frame; //FIXME/XXX remove this hack
    pkt.data= (uint8_t *)final_picture;
    pkt.size=  sizeof(AVPicture);
    pkt.pts= av_rescale_q(ost->sync_opts, enc->time_base, ost->st->time_base);
    pkt.flags |= AV_PKT_FLAG_KEY;
    write_frame(s, &pkt, ost->st->codec, bitstream_filters[ost->file_index][pkt.stream_index]);
    enc->coded_frame = old_frame;
输出的数据在pkt.data里,final_picture即为经过处理的输入Pic。
6、输出视频流的帧率,是从输入视频流的包头数据中获得的。Rmvb的vedio MDPR块里,保存有fps和fps2信息。Ffmpeg取fps作为帧率,fps2丢弃了。Fps2有什么用,还不清楚。介绍rmvb格式的文章里也没有看到过关于fps的任何介绍。

一些调试

以为313版本为例:

  delta0 = sync_ipts - ost->sync_opts; // delta0 is the "drift" between the input frame (next_picture) and where it would fall in the output.
        delta  = delta0 + duration;
        printf("delta0:%f,sync_ipts:%f-%ld,duration:%f\n",delta0,sync_ipts,ost->sync_opts,duration);

  nb_frames = FFMIN(nb_frames, ost->max_frames - ost->frame_number);
    nb0_frames = FFMIN(nb0_frames, nb_frames);
    printf("nb_frames:%d,frame_number:%d\n",nb_frames, ost->frame_number);

以-i  /home/otvcloud/rec/t.mp4  -loglevel  trace -vcodec h264  -r 10 /home/otvcloud/rec/1.mp4  -y 为例

delta0:-0.000008,sync_ipts:-0.000008-0,duration:0.400000
nb_frames:1,frame_number:0
dup frame,framenum:1
delta0:-0.599998,sync_ipts:0.400002-1,duration:0.400000
nb_frames:1,frame_number:1
dup frame,framenum:2
delta0:-1.199989,sync_ipts:0.800011-2,duration:0.400000
nb_frames:1,frame_number:2
dup frame,framenum:3
delta0:-1.799995,sync_ipts:1.200005-3,duration:0.400000
nb_frames:0,frame_number:3
delta0:-1.399986,sync_ipts:1.600014-3,duration:0.400000
nb_frames:1,frame_number:3
dup frame,framenum:4
delta0:-1.999992,sync_ipts:2.000008-4,duration:0.400000
nb_frames:0,frame_number:4
delta0:-1.599998,sync_ipts:2.400002-4,duration:0.400000
nb_frames:0,frame_number:4
delta0:-1.199989,sync_ipts:2.800011-4,duration:0.400000
nb_frames:1,frame_number:4
dup frame,framenum:5
delta0:-1.799995,sync_ipts:3.200005-5,duration:0.400000
nb_frames:0,frame_number:5
delta0:-1.399986,sync_ipts:3.600014-5,duration:0.400000
nb_frames:1,frame_number:5
dup frame,framenum:6
delta0:-1.999992,sync_ipts:4.000008-6,duration:0.400000
nb_frames:0,frame_number:6

可以看出输出帧是0.4的增量在加,当输入时间跟输出时间相差很多的时候,跳过输入帧;当接近时,使用输入帧率。

  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;

  /* duplicates frame if needed */  
  for (i = 0; i < nb_frames; i++) {

。。。。。。。 //此处即使使用帧,每使用一帧,ost->frame_number++。

}

以-i  /home/otvcloud/rec/t.mp4  -loglevel  trace -vcodec h264  -r 20  /home/otvcloud/rec/1.mp4  -y 为例

delta0:-0.199989,sync_ipts:0.800011-1,duration:1.000000
nb_frames:1,frame_number:1
dup frame,framenum:2
delta0:-0.399986,sync_ipts:1.600014-2,duration:1.000000
nb_frames:1,frame_number:2
dup frame,framenum:3
delta0:-0.599998,sync_ipts:2.400002-3,duration:1.000000
nb_frames:1,frame_number:3
dup frame,framenum:4
delta0:-0.799995,sync_ipts:3.200005-4,duration:1.000000
nb_frames:1,frame_number:4
dup frame,framenum:5
delta0:-0.999992,sync_ipts:4.000008-5,duration:1.000000
nb_frames:1,frame_number:5
dup frame,framenum:6
delta0:-1.199989,sync_ipts:4.800011-6,duration:1.000000
nb_frames:1,frame_number:6
dup frame,framenum:7
delta0:-1.399986,sync_ipts:5.600014-7,duration:1.000000
nb_frames:1,frame_number:7
dup frame,framenum:8
delta0:-1.599998,sync_ipts:6.400002-8,duration:1.000000
nb_frames:1,frame_number:8
dup frame,framenum:9
delta0:-1.799995,sync_ipts:7.200005-9,duration:1.000000
nb_frames:1,frame_number:9
dup frame,framenum:10

猜你喜欢

转载自blog.csdn.net/evsqiezi/article/details/81870117
今日推荐