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, b
itstream_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的任何介紹。