1. 程式人生 > >[SimplePlayer] 3. 視訊幀同步

[SimplePlayer] 3. 視訊幀同步

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。

 

 

同步流程

視訊幀有固定的輸出時間的話,意味著在解碼完成一幀之後,需要進行等待,等到合適的時間才能進行幀的顯示。

image

上圖為一個視訊幀的解碼、顯示迴圈。在解碼完成一幀後,需要獲得當前時間以及當前幀的顯示時間,兩者的時間差就是需要睡眠的時長。

image

不過由於我們常用的作業系統為非實時系統,因此如果希望執行短時間的休眠,呼叫sleep通常不會得到非常滿意的結果。解決方法就是通過多次休眠更短的時間段,並在被喚醒過後檢查是否已經超時,超時即可進行視訊幀的輸出。

image

 

            //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();
            }