1.?視頻同步基礎
1.2 簡介
看視頻時,要是聲音和畫面不同步,體驗會大打折扣。之所以會出現這種情況,和音視頻數據的處理過程密切相關。音頻和視頻的輸出不在同一個線程,就像兩個工人在不同車間工作,而且不一定會同時 “生產” 出同一時間點(pts,Presentation Time Stamp,即顯示時間戳)的音頻幀和視頻幀。更麻煩的是,在編碼或封裝階段,pts 可能不連續,甚至存在錯誤。所以,在播放音視頻時,必須對它們的播放速度、播放時刻進行精準控制,這就是音視頻同步的關鍵所在。?
在 ffplay 這個常用的音視頻播放工具中,音頻和視頻分別有自己的輸出線程。音頻的輸出線程是 sdl 的音頻輸出回調線程,好比是音頻專屬的 “快遞員”,負責把處理好的音頻數據送到播放設備;視頻的輸出線程則是程序的主線程,就像視頻的 “大管家”,統籌著視頻數據的播放流程。?
為了實現音視頻同步,常見的策略有以下幾種:
-
**以音頻為基準同步視頻(AV_SYNC_AUDIO_MASTER)**?
此策略將音頻作為整個音視頻同步的時間基準。音頻數據在解碼后,其時間戳(PTS)被視為標準時間軸。視頻播放進程緊密圍繞音頻時間軸進行動態調整。?
當系統檢測到視頻播放進度滯后于音頻,即視頻的 PTS 大于音頻的 PTS 時,為了讓視頻盡快追趕音頻的節奏,會選擇性地丟棄部分視頻幀。這種 “跳幀” 處理在視覺上可能表現為畫面輕微跳躍,但相較于聲音的異常,人眼對這種畫面變化的敏感度較低,能在一定程度上保證音畫同步。?
反之,若視頻播放速度快于音頻,系統則會繼續渲染上一幀,延長該幀的顯示時間,從而降低視頻的整體播放速度,使其與音頻播放進度保持一致。這種以音頻為主導的同步方式,在大多數常規音視頻播放場景中被廣泛應用,能有效利用人耳對聲音變化更為敏感的特性,為用戶帶來相對自然流暢的視聽體驗。? -
以視頻為基準同步音頻(AV_SYNC_VIDEO_MASTER)?
該策略以視頻的播放進度和時間戳作為同步的核心依據。當音頻播放進度落后于視頻,即音頻的 PTS 小于視頻的 PTS 時,系統會加快音頻的播放速度,或者選擇丟棄部分音頻幀來追趕視頻。然而,丟棄音頻幀極易導致聲音出現斷音現象,嚴重影響聽覺體驗,因此這種處理方式需謹慎使用。?
當音頻播放進度超前于視頻時,系統會放慢音頻的播放速度,或重復播放上一幀音頻。在調整音頻播放速度的過程中,涉及到音頻重采樣技術,即對音頻樣本的采樣率、采樣精度等參數進行重新調整,以保證音頻在變速播放后仍能保持較好的音質,避免出現聲音失真、變調等問題 。但這種同步方式由于人耳對聲音變化的高敏感度**,在實際應用中相對較少,僅適用于一些對視頻畫面呈現要求極高、對音頻同步精度要求相對寬松的特殊場景。?** -
以外部時鐘為基準同步(AV_SYNC_EXTERNAL_CLOCK)?
該策略引入一個高精度的外部時鐘源作為統一的時間基準,整個音視頻播放系統中的音頻和視頻播放時序均參照此外部時鐘進行校準。外部時鐘可以是網絡時間協議(NTP)服務器提供的時間,或是專業硬件設備(如時鐘發生器)產生的穩定時鐘信號。?
音視頻數據在解碼后,其時間戳(PTS)會與外部時鐘進行實時比對。系統通過精確計算兩者的時間偏差,對音頻和視頻的播放時刻進行調整,確保音頻采樣點的輸出與視頻幀的顯示在時間維度上嚴格對齊。這種同步方式尤其適用于對同步精度要求極高的場景,如遠程視頻會議、大型多機位直播等。在這些場景中,多個終端設備同時接收并播放音視頻流,只有依賴統一的外部時鐘,才能實現跨設備的精準音視頻同步,避免出現音畫錯位的現象。? -
結合外部時鐘調整播放速度?
此策略是在以外部時鐘為基準同步的基礎上進行的優化升級。它不僅依據外部時鐘來校準音視頻的播放起始時刻,還會實時監測外部時鐘與音視頻播放進度之間的差異,并動態調整音視頻的播放速度。?
系統持續計算音視頻播放進度與外部時鐘的時間差,當發現音頻或視頻播放進度超前于外部時鐘時,通過延長音頻樣本的播放間隔、增加視頻幀的顯示時長等方式降低播放速度;當播放進度滯后時,則通過縮短音頻樣本播放間隔、跳過部分視頻幀等手段加快播放速度。這種動態調整機制類似于閉環控制系統,能夠有效應對網絡延遲波動、設備性能差異等因素導致的音視頻播放節奏變化,在復雜的網絡環境或異構設備組成的播放系統中,為用戶提供穩定、流暢的音視頻同步體驗。?
ffplay實現三種
{ "sync", HAS_ARG | OPT_EXPERT, { .func_arg = opt_sync }, "set audio-video sync. type (type=audio/video/ext)", "type" },# ffplay -sync audio input.mp4 使用辦法
1.2 ?視頻同步基本概念
基本概念
DTS(Decoding Time Stamp):即解碼時間戳,這個時間戳的意義在于告訴播放器該在什么時候解碼這?幀的數據。
PTS(Presentation Time Stamp):即顯示時間戳,這個時間戳?來告訴播放器該在什么時候顯示
這?幀的數據。
timebase 時基:pts的值的真正單位
ffplay中的pts,ffplay在做?視頻同步時使?秒為單位,使?double類型去標識pts,在ffmpeg內部不會?浮點數去標記pts。
Clock 時鐘
AVRational 表示分數
typedef struct AVRational{int num; ///< Numeratorint den; ///< Denominator} 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)
補充
對比項 | 時間戳(Timestamp) | 幀時長(Duration) |
---|---|---|
計算對象 | 單個幀或數據包的顯示時間點 | 整個流(或文件)的總播放時間 |
公式輸入 | pts (幀的時間戳) | st->duration (流的總時長) |
結果含義 | 例如:第 10 秒顯示的幀 | 例如:視頻總時長為 120 分鐘 |
單位 | 秒(相對于流的開始時間) | 秒(整個流的持續時間) |
用途 | 同步、進度顯示、幀定位 | 總時長顯示、處理時間估算 |
示例說明
假設一個視頻流:
- 時間基
time_base = {1, 1000}
(即 1 個時間單位 = 0.001 秒)。 - 某幀的
pts = 5000
,則該幀的時間戳為:
5000 * 0.001 = 5
秒(即該幀在第 5 秒顯示)。 - 流的總時長
duration = 20000
,則整個視頻的時長為:
20000 * 0.001 = 20
秒(即視頻總時長為 20 秒)。
Clock
typedef struct Clock {double pts; // 時鐘基礎, 當前幀(待播放)顯示時間戳,播放后,當前幀變成上一幀// 當前pts與當前系統時鐘的差值, audio、video對于該值是獨立的double pts_drift; // clock base minus time at which we updated the clock// 當前時鐘(如視頻時鐘)最后一次更新時間,也可稱當前時鐘時間double last_updated; // 最后一次更新的系統時鐘double speed; // 時鐘速度控制,用于控制播放速度// 播放序列,所謂播放序列就是一段連續的播放動作,一個seek操作會啟動一段新的播放序列int serial; // clock is based on a packet with this serialint paused; // = 1 說明是暫停狀態// 指向packet_serialint *queue_serial; /* pointer to the current packet queue serial, used for obsolete clock detection */
} Clock;
static void set_clock_at(Clock *c, double pts, int serial, double time);
參數詳解
- Clock *c
作用:指定要操作的時鐘對象。- double pts
作用:設置時鐘的時間戳值。- int serial
作用:標識時間戳的有效性,避免使用已過時的時間戳。- double time
作用:記錄設置時鐘的系統時間基準,用于后續時鐘漂移計算。
工作原理
-
不斷 “對時”
就像我們平時給手表對時間一樣,這個時鐘也需要定期校準。它有個叫 set_clock_at 的方法,要校準的時候,得告訴它三個信息:- pts :可以把它理解成某個事件(比如視頻里某一幀該出現的時間)的時間標記。
- serial :可以看作是一種編號,用來區分不同的 “東西”(比如不同的視頻流之類的 ),不過這里重點先在時間上。
- time :就是系統當前實實在在過了多久的時間,像從電腦開機到現在過了多少秒。用這三個信息就能給時鐘校準啦。
-
估算時間
這個時鐘顯示的時間不是絕對精準的,而是個估算值。這里面最關鍵的設計就是 pts_drift 。
關鍵操作與計算
- set_clock 操作:在 PTS1 對應的時間點,進行了 set_clock 操作 。此時計算 pts_drift,公式為 pts_drift = PTS1 - time1 ,也就是用當前幀的 PTS1 減去此時的系統時間 time1 ,得到兩者的時間差值 。
- 時間推進與 get_clock 操作:隨著時間推進,系統時間到了 time2 ,此時進行 get_clock 操作 。為了估算當前系統時間 time2 對應的 PTS 時間(即參考時間 ),利用之前計算的 pts_drift 來計算:
首先將 pts_drift + time2 ,即 PTS1 - time1 + time2 。
進一步變形為 PTS1 + (time2 - time1) ,而 time2 - time1 就是這段時間的時長,用 duration 表示,所以最終得到 PTS1 + duration ,這就是當前系統時間對應的參考 PTS 時間 。
1.3 不同時間基
AVFormatContext
duration:表示整個碼流的時長。在獲取正常時長時,需要將其除以 AV_TIME_BASE ,得到的結果單位為秒。這一數值為了解整個媒體文件的時長提供了關鍵信息,在諸如播放器展示總時長等場景中發揮作用。
AVStream 的 time_base 設置
AVStream 的 time_base 是在解復用器(demuxer)或復用器(muxer)內進行設置的,以下以常見的 TS、FLV、MP4 格式為例:
- TS 格式:在 mpegts.c 和 mpegtsenc.c 中,通過 avpriv_set_pts_info(st, 33, 1, 90000) 進行設置。這里的參數設置,決定了 TS 流后續處理中時間相關計算的基準。在解碼 TS 流時,解碼器依據這個設定的 time_base ,準確地將時間戳信息轉化為實際的播放時間,以確保音視頻同步播放。
- FLV 格式:在 flvdec.c 中使用 avpriv_set_pts_info(st, 32, 1, 1000) ,在 flvenc.c 中使用 avpriv_set_pts_info(s->streams[i], 32, 1, 1000) 。在 FLV 流的解碼過程中,time_base 參與到對音視頻幀時間戳的解析和處理中。比如,對于視頻幀,根據 time_base 可以準確計算出每一幀應該在何時顯示,對于音頻幀,能確定其采樣點的播放時刻,從而保障解碼后的音視頻能正確同步播放。
- MP4 格式:在 mov.c 中通過 avpriv_set_pts_info(st, 64, 1, sc->time_scale) ,在 movenc.c 中通過 avpriv_set_pts_info(st, 64, 1, track->timescale) 進行設置。在 MP4 解碼時,time_base 如同一個精準的 “時間指揮官”,協調著音視頻幀的解碼和輸出順序。例如,視頻解碼線程依據 time_base 來決定何時輸出一幀畫面,音頻解碼線程也基于此來控制音頻樣本的播放節奏,以實現音視頻在播放時的完美同步。
AVPacket 結構體
在 AVPacket 中,各時間相關字段均以 所屬 AVStream 的 time_base 為單位,具體如下:
- pts(Presentation Timestamp,顯示時間戳):用于標識數據包對應內容在正常播放順序下的顯示時間點,其數值需結合 AVStream->time_base 換算為實際時間(秒),例如 實際時間(秒) = pts × AVStream->time_base.num / AVStream->time_base.den。
- dts(Decoding Timestamp,解碼時間戳):指示數據包應被解碼的時間點,在編碼存在 B 幀等復雜場景時,dts 與 pts 可能不同,同樣以 AVStream->time_base 為度量基準 。
- duration(持續時間):表示該數據包所包含內容的持續時長,同樣基于 AVStream->time_base 進行量化,用于計算相鄰數據包的時間間隔或解碼后幀的顯示時長。
AVFrame 結構體
AVFrame 中的時間相關字段涉及繼承與轉換,與 AVStream->time_base 緊密關聯,具體規則如下:
- pts:代表解碼后幀的顯示時間戳,通常從對應 AVPacket 拷貝而來,同樣以 AVStream->time_base 為單位。若原始 AVPacket 未提供有效 pts,則需通過其他邏輯(如推算)確定。
- pkt_pts 和 pkt_dts:直接拷貝自生成該幀的 AVPacket 中的 pts 和 dts,因此也以 AVStream->time_base 為單位 。這兩個字段保留了數據包層面的時間戳信息,便于追溯幀的原始時間屬性。
- duration:表示該幀的持續時長,與 AVPacket 類似,同樣以 AVStream->time_base 為度量單位,用于精確控制幀的顯示時長,在音視頻同步計算中發揮重要作用。
1.4 ffplay的實際操作
typedef struct Frame {AVFrame *frame; // 指向數據幀AVSubtitle sub; // 用于字幕int serial; // 幀序列,在seek的操作時serial會變化double pts; // 時間戳,單位為秒double duration; // 該幀持續時間,單位為秒int64_t pos; // 該幀在輸入文件中的字節位置int width; // 圖像寬度int height; // 圖像高讀int format; // 對于圖像為(enum AVPixelFormat),// 對于聲音則為(enum AVSampleFormat)AVRational sar; // 圖像的寬高比(16:9,4:3...),如果未知或未指定則為0/1int uploaded; // 用來記錄該幀是否已經顯示過?int flip_v; // =1則垂直翻轉, = 0則正常播放
} Frame;
視頻幀 PTS 的獲取與校正
-
best_effort_timestamp 的作用:
代碼中通過 frame->pts = frame->best_effort_timestamp; 校正視頻幀的 pts。盡管多數情況下 AVFrame 的 pts 與 best_effort_timestamp 值相同,但 best_effort_timestamp 是通過多種啟發式方法(如解碼時的綜合邏輯)估計出的時間戳,由 libavcodec 設置。 -
優勢:在某些復雜場景(如輸入流時間戳不規范、部分編碼場景缺失 pts 時),best_effort_timestamp 能提供更可靠的時間參考,確保視頻幀的顯示時序正確,避免因原始 pts 異常導致的播放錯亂或同步問題。
與 AVFrame->pts 的關系:AVFrame->pts 通常依賴于輸入流的時間基,而 best_effort_timestamp 是經過解碼層邏輯處理后的 “最佳估計”,在時間表示上可能更直接、準確,因此 FFplay 選擇以此作為 Frame->pts 的來源。
在 FFplay 中,音頻幀的 PTS(Presentation Time Stamp,顯示時間戳)處理是實現音頻同步的關鍵環節。這個過程涉及三次時間基轉換和緩沖區延遲補償,下面我將逐步拆解其邏輯:
Audio Frame PTS的獲取
1. 第一次轉換:從 AVStream->time_base
到 1/采樣率
frame->pts = av_rescale_q(frame->pts, d->avctx->pkt_timebase, tb);
- 目的:將原始的時間戳(以
AVStream->time_base
為單位)轉換為以 樣本數 為單位。 - 轉換邏輯:
d->avctx->pkt_timebase
通常等于AVStream->time_base
,是輸入流的時間基(例如{1, 90000}
)。tb
是目標時間基{1, 采樣率}
(例如{1, 44100}
)。av_rescale_q
函數將pts
從pkt_timebase
轉換為tb
,
- 意義:后續音頻處理(如重采樣、緩沖區操作)通常以樣本數為單位,這一步為精確控制音頻數據的時序奠定基礎。
2. 第二次轉換:從 1/采樣率
到秒
af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
- 目的:將以樣本數為單位的
pts
轉換為以 秒 為單位的浮點數,便于與時鐘同步。 - 轉換邏輯:
av_q2d(tb)
將時間基{1, 采樣率}
轉換為小數值(例如1/44100 ≈ 0.0000226757
)。frame->pts * av_q2d(tb)
直接將樣本數轉換為秒數(例如 44100 個樣本 → 1.0 秒)。
- 特殊處理:若
frame->pts
為AV_NOPTS_VALUE
(無效時間戳),則設為NAN
,表示時間未知。
3. 第三次調整:補償音頻緩沖區延遲
audio_pts = is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec;
- 目的:修正
audio_clock
,使其反映 實際播放時間(而非緩沖區填充時間)。 - 延遲來源:
2 * is->audio_hw_buf_size
:SDL 音頻驅動通常維護兩個緩沖區,這部分數據已提交但尚未播放。is->audio_write_buf_size
:當前audio_buf
中未提交給驅動的剩余數據。
- 計算邏輯:
is->audio_tgt.bytes_per_sec
表示每秒播放的字節數(例如 44100Hz × 2 字節/樣本 × 2 聲道 = 176400 B/s)。- 總延遲字節數除以
bytes_per_sec
得到延遲秒數,從audio_clock
中減去該值,得到實際播放位置的時間戳。
完整流程總結
- 從容器到樣本數:通過
av_rescale_q
將AVPacket
的pts
轉換為樣本數,消除不同媒體流時間基的差異。 - 從樣本數到秒:將樣本數轉換為秒,便于與時鐘系統(如視頻時鐘)直接比較。
- 緩沖區延遲補償:考慮音頻驅動緩沖區的延遲,修正時鐘以反映真實播放進度。
2 以?頻為基準
2.1 音頻主流程
/* Let's assume the audio driver that is used by SDL has two periods. */if (!isnan(is->audio_clock)) {set_clock_at(&is->audclk, is->audio_clock -(double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size)/ is->audio_tgt.bytes_per_sec,is->audio_clock_serial,audio_callback_time / 1000000.0);sync_clock_to_slave(&is->extclk, &is->audclk);}
- 橙色段:表示 SDL 內部的 audio_hw_buf_size(音頻硬件緩沖區大小),代表已在 SDL 音頻驅動內部、但尚未播放的音頻數據占用空間。
- 綠色段:表示 SDL 外部 sdl_audio_callback 處理的 audio_hw_buf_size,即當前回調正在處理或準備填充到驅動的音頻數據部分。
- 藍色段:表示 audio_buf 剩余的 audio_write_buf_size,即解碼后未被填充到 SDL 音頻驅動緩沖區的剩余音頻數據量。
流程分析
1 is->audio_clock = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate;
audio_clock 代表的是當前幀最后一個樣本的顯示時間,即 audio_buf 結束位置的時間戳
物理意義:
若 af->pts=2.0 秒,幀持續時間為 0.023 秒,則 audio_clock=2.023 秒。這意味著當播放到 audio_buf 的末尾時,時間應推進到 2.023 秒。
2 剩余數據的時間戳修正
當 audio_buf 中有剩余數據(長度為 audio_write_buf_size 字節)時,實際播放數據的 pts 需要調整:
實際數據的pts = is->audio_clock - (double)(is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec;
修正原因:
audio_clock 是整個 audio_buf 的結束時間,但當前可能只播放了其中一部分,剩余數據尚未播放。
需要從 audio_clock 中減去剩余數據的播放時間,以得到當前正在播放的數據的實際時間戳。
修正公式解析:
is->audio_tgt.bytes_per_sec 表示每秒播放的字節數(例如 44100Hz × 2 字節 / 樣本 × 2 聲道 = 176400 B/s)。
(double)(is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec 計算剩余數據的播放時間(秒)。
例如,若剩余數據為 88200 字節,則播放時間為 88200 / 176400 = 0.5 秒。若 audio_clock=3.0 秒,則實際播放數據的 pts=3.0 - 0.5 = 2.5 秒。
這里的 2 * is->audio_hw_buf_size 表示 SDL 驅動中兩個未播放的硬件緩沖區,加上 audio_write_buf_size 得到總延遲,進一步修正時鐘,確保與實際播放位置精確匹配。
因此
is->audio_clock -(double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size)/ is->audio_tgt.bytes_per_sec,
2.2 視頻主流程
ffplay中將視頻同步到?頻的主要?案是,如果視頻播放過快,則重復播放上?幀,以等待?頻;如果視頻播放過慢,則丟幀追趕?頻。
這?部分的邏輯實現在視頻輸出函數 video_refresh 中
重點如何計算上一幀時長
這?與系統時刻的對?,引?了另?個概念——frame_timer。可以理解為幀顯示時刻,如更新前,是上?幀lastvp的顯示時刻;對于更新后( is->frame_timer += delay ),則為當前幀vp顯示時刻。
上?幀顯示時刻加上delay(還應顯示多久(含幀本身時?))即為上?幀應結束顯示的時刻
time1:系統時刻?于lastvp結束顯示的時刻(frame_timer+dealy),即虛線圓圈位置。此時應該繼續顯示lastvp
time2:系統時刻?于lastvp的結束顯示時刻,但?于vp的結束顯示時刻(vp的顯示時間開始于虛線圓圈,結束于??圓圈)。此時既不重復顯示lastvp,也不丟棄vp,即應顯示vp
time3:系統時刻?于vp結束顯示時刻(??圓圈位置,也是nextvp預計的開始顯示時刻)。此時應該丟棄vp。
計算delay
delay = compute_target_delay(last_duration, is);
坐標軸與參數定義:
坐標軸表示視頻時鐘(video clock)與音頻時鐘(audio clock)的差值 diff。diff = 0 代表兩者完全同步。
坐標軸下方色塊表示根據 diff 計算后返回的值,其中 delay 為傳入參數,即上一幀(lastvp)的顯示時長(frame duration)。sync_threshold 定義了一個允許的同步誤差范圍,在該范圍內認為是 “準同步”,無需調整 lastvp 的顯示時長。
- delay >AV_SYNC_THRESHOLD_MAX=0.1秒,則sync_threshold = 0.1秒
- delay <AV_SYNC_THRESHOLD_MIN=0.04秒,則sync_threshold = 0.04秒
- AV_SYNC_THRESHOLD_MIN = 0.0.4秒 <= delay <= AV_SYNC_THRESHOLD_MAX=0.1秒,則sync_threshold為delay本身
同步精度最好的范圍是:-0.0.4秒~+0.04秒;
同步精度最差的范圍是:-0.1秒~+0.1秒
同步邏輯分析:
- diff <= -sync_threshold:視頻播放速度慢于音頻,需適當丟幀。返回值為 MAX(0, delay + diff),確保至少更新畫面為當前幀(vp),以追趕音頻進度。
- diff >= sync_threshold 且 delay > AV_SYNC_FRAMEDUP_THRESHOLD(0.1 秒):視頻播放快于音頻,且當前幀顯示時長超過 0.1 秒。返回 delay + diff,此時總時長 delay + diff >= 0.2 秒,具體顯示時長由 diff 決定,確保視頻與音頻逐步對齊。
- diff >= sync_threshold 且 delay <= 0.1 秒:視頻播放快于音頻,且當前幀顯示時長較短。返回 2 * delay,即重復顯示 lastvp 一幀,通過延長該幀顯示時間等待音頻,使總顯示時長不超過 0.2 秒。
- -sync_threshold < diff < +sync_threshold:處于允許的同步誤差范圍內,按正常 frame duration 顯示視頻,直接返回 delay,維持當前播放節奏。
同步策略總結:
該機制通過動態調整視頻幀的顯示方式(丟幀或重復幀)實現音視頻同步。若視頻過快,重復上一幀以等待音頻;若視頻過慢,丟棄部分幀以追趕音頻。通過引入 frame_timer 標記幀的顯示時刻和應結束顯示時刻,并與系統時刻對比,決定具體操作。lastvp 的應結束顯示時刻不僅考慮自身顯示時長,還納入了音視頻時鐘差值。此策略并非要求每時每刻完全同步,而是通過 “準同步” 差值區域(-sync_threshold 至 +sync_threshold)平衡同步精度與系統資源,提升同步效率與穩定性,確保用戶感知上的音畫協調。