[SimplePlayer] 3. 視訊幀同步
阿新 • • 發佈:2018-11-09
Frame Rate
幀率代表的是每一秒所播放的視訊影象數目。通常,視訊都會有固定的幀率,具體點地說是每一幀的時間間隔都是一樣的,這種情況簡稱為CFR(Constant Frame Rate);另外一種情況就是每一幀的時間間隔不一定相同,即可變幀率,簡稱為VFR(Variable Frame Rate),現在也有些錄影裝置支援錄製VFR視訊了,在錄製具有大量靜止場景的視訊時,採用VFR能降低錄製出來的視訊的容量大小。
PTS
通過上文對幀率的描述,我們知道在進行視訊播放時,每一幀都應該有自己的播放時刻。不過視訊檔案中不會直接存放幀的播放時刻,為了保證精度以及節省儲存空間,檔案中會儲存每一幀的PTS(Presentation Time Stamp)以及一個名為time base的引數,PTS是整數,而time base是小數,通過PTS與time base相乘,就能得到視訊的播放時刻,單位為秒。大部分的視訊格式都支援存放幀的PTS,而在播放的時候,通過對PTS進行計算就能使得視訊的每一幀都在合適的時間顯示。通常,第一幀的PTS為0。
同步流程
視訊幀有固定的輸出時間的話,意味著在解碼完成一幀之後,需要進行等待,等到合適的時間才能進行幀的顯示。
上圖為一個視訊幀的解碼、顯示迴圈。在解碼完成一幀後,需要獲得當前時間以及當前幀的顯示時間,兩者的時間差就是需要睡眠的時長。
不過由於我們常用的作業系統為非實時系統,因此如果希望執行短時間的休眠,呼叫sleep通常不會得到非常滿意的結果。解決方法就是通過多次休眠更短的時間段,並在被喚醒過後檢查是否已經超時,超時即可進行視訊幀的輸出。
//Decode video frame avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); if(frameFinished){ memcpy(Display.YPlane, pFrame->data[0], bufsize); memcpy(Display.UPlane, pFrame->data[1], bufsize/4); memcpy(Display.VPlane, pFrame->data[2], bufsize/4); vs.frame_last_pts = vs.frame_cur_pts; vs.frame_cur_pts = pFrame->pts * time_base * 1000000; pts_delay = vs.frame_cur_pts - vs.frame_last_pts; vs.cur_display_time = vs.last_display_time + pts_delay; //vs.last_frame_displayed = 0; if(!vs.is_first_frame){ time = av_gettime_relative(); delay = vs.cur_display_time - time; while(delay > 0){ if(delay > 10000) vs.sleep_time = 10000; else vs.sleep_time = delay; av_usleep(vs.sleep_time); time = av_gettime_relative(); delay = vs.cur_display_time - time; } vs.last_display_time = time; DisplayFrame(&Display); //vs.last_frame_displayed = 1; }else{ vs.last_display_time = av_gettime_relative(); DisplayFrame(&Display); vs.is_first_frame = 0; //vs.last_frame_displated = 1; } SDL_PumpEvents(); }