author: hjjdebug
date: 2025年 07月 05日 星期六 18:20:45 CST
descrip: 視頻播放中時鐘的概念及音視頻同步概念
文章目錄
- 1.前言: 視頻播放:
- 1. 固定延時時間
- 2. 根據frame的duration來延時.
- 3. 根據frame的PTS 來播放
- 3.1. 時鐘是什么?
- 3.2. 時鐘的用途.
- 2.音視頻同步:
1.前言: 視頻播放:
一幀一幀的把畫面渲染出來就構成了視頻播放.
從上一幀到這一幀的時間間隔是多少呢?
1. 固定延時時間
我們可以通過幀率計算每一幀的延時時間
每一幀和每一幀之間延時固定長度來播放.
架構大概是這樣的.
double duration=(double)1/framerate;
for(;;)
{getframe(ctx,frame);delay(duration);display(frame);
}
這樣每個畫面看起來是連續的,但是播放時間卻是不準確的.
2. 根據frame的duration來延時.
每個frame 都有duration屬性值,視頻一般是3600個時間單位,40ms時間,也有例外.
架構大概是這樣的.
for(;;)
{getframe(ctx,frame);delay(frame.duration);display(frame);
}
缺點. 只考慮了每一幀和每一幀之間理論上的時間間隔,
但是獲取到該幀也是要花時間的,
播放時間還是不準確.沒有按frame 的時間戳來播放.
3. 根據frame的PTS 來播放
讓每一次播放時間與PTS 時間戳對齊. PTS 讓我們啥時候播放,我們就啥時候播放.
當拿到一個frame時,根據frame.pts,判斷需要等待多少時間后再播放
架構大概是這樣的.
for(;;)
{getframe(ctx,frame);double duration=compute_target_delay(video_clock,frame.pts); //這個duration 是動態的.根據pts計算的.delay(duration);display(frame);
}
那根據pts 計算duration的過程是怎樣的呢?
關鍵就是這個時鐘video_clock. 它必需要知道某個時間點對應的PTS值是多少,
這樣給一個新的pts, 就能夠計算出期望的時間點.
拿期望的時間點減掉當前時間戳,就能得到延時時間duration
所以計算過程是這樣的.
typedef struct _Clock
{long pts_ref; //參考pts時間double sys_time_ref; //對應該pts的系統時間
}Clockdouble compute_target_delay(Clock *clk, long pts)
{long delta_pts = pts - clk->pts_ref;double time = delta_pts * AVRational(1,90000); //時間戳pts一般是90Kdouble expect_time=sys_time_ref+time;double current_time=av_gettime_relative()/1000000.0;double delay=expect_time-current_time; //期望播出時間減去當前時間就是應該delay的時間.return delay;
}
是的,也許你發現了問題,參考時間和參考pts又如何獲得呢?
這個簡單,你可以在獲取第一幀數據時,設定pts為pts_ref, 那時的系統時間為sys_time_ref.
而且在播放過程中,當你發現不正常時,隨時設置參考幀pts及對應的參考時間.
這叫時鐘同步,可能對應某個set_clock函數, 下面給個示例.
程序代碼:
for(;;)
{getframe(ctx,frame);double duration=compute_target_delay(vid_clk,frame.pts); //這個duration 是動態的.根據pts計算的.if(duration > 0){delay(duration)}else if(duration < -0.1) //超前有點離譜,我們矯正一下時鐘{set_clock(vid_clk,frame.pts); //設置時鐘同步點}display(frame);
}
//時鐘同步
void set_clock(Clock *vid_clk,long pts)
{vid_clk. pts_ref = pts;vid_clk.sys_time_ref = av_gettime_relative()/1000000.0;
}
3.1. 時鐘是什么?
時鐘就是某個frame的PTS, 對應著播放的系統時鐘時間是多少.
這是時鐘的主要概念.
有個這個基礎,我們可以以后根據frame的pts, 計算出它期望的播出時間.
在此基礎上再衍生出其它成員只是為了完善其它的功能而已.
1.1 時鐘對時:
把PTS與系統時間關聯上叫時鐘對時, 通過調用 set_clock(Clock *clk,LONG pts) 來完成
3.2. 時鐘的用途.
給定一個frame, 可以拿到它的pts, 通過與時鐘比較,可以知道它應該在什么時間播放.
從而也知道,它此時應該等待多少時間才可以播放.
2.音視頻同步:
音頻和視頻解碼/渲染是獨立線程,它們各有自己的時間戳PTS, 因而可以對應不同的時鐘.
即音頻對應音頻時鐘. audclk
視頻對應視頻時鐘. vidclk
如果我們拿音頻時鐘去調教視頻播放,這就實現了音視頻同步,具體是怎樣操作的呢?
視頻時鐘與主時鐘本身可能就有偏差,這個需要考慮在內.
diff = get_clock(&is->vidclk) - get_master_clock(is); //這里的master_clock 可能是音頻時鐘,視頻時鐘或外部時鐘
我們現在在談視頻同步到音頻,當然這里要把主時鐘理解為音頻時鐘.
根據視頻計算的delay值,用音頻進一步矯正,需要一種算法,可能是簡單的delay+diff,
也可能在極端條件下進行過激調整2delay+diff, delay+2diff 等等, 可以觀察一下哪種效果更好.
這就是編程靈活的地方了. 算法以簡潔,使用,有效為基礎.
有了這些概念,我們再來讀ffplay6, 就好很多了.