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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM