生活随笔
收集整理的這篇文章主要介紹了
ffplay.c学习-6-⾳视频同步基础
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
ffplay.c學習-6-?視頻同步基礎
目錄
?視頻同步策略 ?視頻同步概念 FFmpeg中的時間單位 ?視頻時間換算的問題 不同結構體的time_base/duration分析 不同結構體的PTS/DTS分析 ffplay中PTS的轉換流程分析 Video Frame PTS的獲取 Audio Frame PTS的獲取
由于?頻和視頻的輸出不在同?個線程,?且,也不?定會同時解出同?個pts的?頻幀和視頻幀。更有甚者,編碼或封裝的時候可能pts還是不連續的,或有個別錯誤的。因此,在進??頻和視頻的播放時,需要對?頻和視頻的播放速度、播放時刻進?控制,以實現?頻和視頻保持同步,即所謂的?視頻同步。 在ffplay中,?頻(audio)和視頻(video)有各?的輸出線程,其中?頻的輸出線程是sdl的?頻輸出回調線程,video的輸出線程是程序的主線程。
1. ?視頻同步策略
?視頻的同步策略,?般有如下?種:
以?頻為基準,同步視頻到?頻(AV_SYNC_AUDIO_MASTER) 視頻慢了則丟掉部分視頻幀(視覺->畫?跳幀) 視頻快了則繼續渲染上?幀 以視頻為基準,同步?頻到視頻(AV_SYNC_VIDEO_MASTER) ?頻慢了則加快播放速度(或丟掉部分?頻幀,丟幀極容易聽出來斷?) ?頻快了則放慢播放速度(或重復上?幀 ) ?頻改變播放速度時涉及到重采樣 以外部時鐘為基準,同步?頻和視頻到外部時鐘(AV_SYNC_EXTERNAL_CLOCK) 前兩者的綜合,根據外部時鐘改變播放速度 視頻和?頻各?輸出,即不作同步處理(FREE RUN) 由于??對于聲?變化的敏感度?視覺?,因此,?般采樣的策略是將視頻同步到?頻,即對畫?進?適當的丟幀或重復以追趕或等待?頻。
特殊地,有時候會碰到?些特殊封裝(或者有問題的封裝),此時就不作同步處理,各?為主時鐘,進?播放。
在ffplay中實現了上述前3種的同步策略。由 sync 參數控制:
{ "sync" , HAS_ARG
| OPT_EXPERT
, { . func_arg
= opt_sync
} , "set audiovideo sync. type (type=audio/video/ext)" , "type" } ,
?如ffplay source.200kbps.768x320.flv -sync video設置以video master
2. ?視頻同步概念
在深?代碼了解其實現前,需要先簡單了解下?些結構體和概念。 DTS(Decoding Time Stamp):即解碼時間戳,這個時間戳的意義在于告訴播放器該在什么時候解碼這?幀的數據。 PTS(Presentation Time Stamp):即顯示時間戳,這個時間戳?來告訴播放器該在什么時候顯示這?幀的數據。 timebase 時基:pts的值的真正單位 ffplay中的pts,ffplay在做?視頻同步時使?秒為單位,使?double類型去標識pts,在ffmpeg內部不會?浮點數去標記pts。 Clock 時鐘 當視頻流中沒有 B 幀時,通常 DTS 和 PTS 的順序是?致的。但存在B幀的時候兩者的順序就不?致了。 pts是presentation timestamp的縮寫,即顯示時間戳,?于標記?個幀的呈現時刻,它的單位由timebase決定。timebase的類型是結構體AVRational(?于表示分數):
typedef
struct AVRational
{ int num
; int den
;
} AVRational
;
如 timebase={1, 1000} 表示千分之?秒(毫秒),那么pts=1000,即為pts*1/1000 = 1秒,那么這?幀就需要在第?秒的時候呈現 將AVRatioal結構轉換成double
static inline double
av_q2d ( AVRational a
) {
return a
. num
/ ( double
) a
. den
;
}
計算時間戳
timestamp ( 秒
) = pts
* av_q2d ( st
- > time_base
)
計算幀時?
time ( 秒
) = st
- > duration
* av_q2d ( st
- > time_base
)
不同時間基之間的轉換
int64_t
av_rescale_q ( int64_t a
, AVRational bq
, AVRational cq
)
在ffplay中,將pts轉化為秒,?般做法是: pts * av_q2d(timebase) 在做同步的時候,我們需要?個"時鐘"的概念,?頻、視頻、外部時鐘都有??獨?的時鐘,各?set各?的時鐘,以誰為基準(master), 其他的則只能get該時鐘進?同步,ffplay定義的結構體是Clock:
typedef
struct Clock
{ double pts
; double pts_drift
; double last_updated
; double speed
; int serial
; int paused
; int * queue_serial
;
} Clock
;
這個時鐘的?作原理是這樣的: 需要不斷“對時”。對時的?法 set_clock_at(Clock *c, double pts, int serial,double time) ,需要?pts、serial、time(系統時間)進?對時。 獲取的時間是?個估算值。估算是通過對時時記錄的pts_drift估算的。pts_drift是最精華的設計,?定要理解。 可以看這個圖來幫助理解: 圖中央是?個時間軸(time是?直在按時間遞增),從左往右看。?先我們調? set_clock 進??次對時,假設這時的 pts 是落后時間 time 的,那么計算 pts_drift = pts - time ,計算出pts和time的相對差值。 接著,過了?會?,且在下次對時前,通過 get_clock 來查詢時間,因為set_clock時的 pts 已經過時,不能直接拿set_clock時的pts當做這個時鐘的時間。不過我們前?計算過 pts_drift ,也就是 pts和 time 的差值,所以我們可以通過當前時刻的時間來估算當前時刻的pts: pts = time +pts_drift 。 ?般time會取CLOCK_MONOTONIC(單調遞增的時鐘),即系統開機到現在的時間. ffplay使?ffmpeg提供的av_gettime_relative()函數
3. FFmpeg中的時間單位
AV_TIME_BASE 定義#define AV_TIME_BASE 1 000 000 ffmpeg中的內部計時單位(時間基) AV_TIME_BASE_Q 定義#define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE} ffmpeg內部時間基的分數表示,實際上它是AV_TIME_BASE的倒數 時間基轉換公式 timestamp(ffmpeg內部時間戳) = AV_TIME_BASE * time(秒) time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg內部時間戳)
4. ?視頻時間換算的問題
標準時間 秒(seconds) 標準時間 微秒(microsecond) ?定義時間單位 (a/b 秒) 以?頻AAC?頻幀舉例,如果pts以1/采樣率為單位,?如44.1khz, 則時間單位是1/44100,因此PTS表示: 第?幀 PTS1 = 0 第?幀 PTS2 = 1024 第三幀 PTS2 = 2048 播放的時候要將PTS換算成秒的單位,則PTS1 = 0 1/44100, PTS2 = 10241/44100, PTS3 =2048*1/44100= 0.046439
5. 不同結構體的time_base/duration分析
ffmpeg存在多個時間基準(time_base),對應不同的階段(結構體),每個time_base具體的值不?樣,ffmpeg提供函數在各個time_base中進?切換。 AVFormatContext duration:整個碼流的時?,獲取正常時?的時候要除以AV_TIME_BASE,得到的結果單位是秒 AVStream time_base:單位為秒,?如AAC?頻流,他可能是{1,44100}TS流,按{1, 90khz} duration:表示該數據流的時?,以AVStream->time_base 為單位 AVStream的time_base是在demuxer或者muxer內設置的,以TS,FLV,MP4為例?: TS avpriv_set_pts_info(st, 33, 1, 90000) (mpegts.c和mpegtsenc.c) FLV avpriv_set_pts_info(st, 32, 1, 1000) (flvdec.c) avpriv_set_pts_info(s->streams[i], 32, 1, 1000) (flvenc.c) MP4 avpriv_set_pts_info(st, 64, 1, sc->time_scale); (mov.c) avpriv_set_pts_info(st, 64, 1, track->timescale); (movenc.c)
6. 不同結構體的PTS/DTS分析
不同結構體下,pts和dts使?哪個time_base來表示? AVPacket pts:以AVStream->time_base為單位 dts:以AVStream->time_base為單位 duration:以AVStream->time_base為單位 AVFrame pts:以AVStream->time_base為單位 pkt_pts和pkt_dts:拷??AVPacket,同樣以AVStream->time_base為單位 duration:以AVStream->time_base為單位
7. ffplay中PTS的轉換流程分析
1. Video Frame PTS的獲取
PTS校正 frame->pts = frame->best_effort_timestamp; 這?為什么不?AVFrame中的pts來直接計算呢?其實?多數情況下AVFrame的pts和best_effort_timestamp值是?樣的
int64_t best_effort_timestamp
;
2. Audio Frame PTS的獲取
ffplay有3次對于Audio的pts進?轉換 第?次 將其由AVStrean->time_base轉換為(1/采樣率) frame->pts = av_rescale_q(frame->pts, d->avctx->pkt_timebase, tb); 第?次 將其由(1/采樣率)轉換為秒 af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); 第三次 根據實際拷?給sdl的數據?度做調整 audio_pts = is->audio_clock -(double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec;
總結
以上是生活随笔 為你收集整理的ffplay.c学习-6-⾳视频同步基础 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。