Android 的 AV sync

我們播放多媒體檔案的時候, 很容易注意到 “沒有對嘴" 的情況, 這就是所謂的 lips sync (= synchronization) 或是 Audio-Video sync 的問題.

螃蟹公司怎麼做 AV sync 我不能告訴大家. 但是 Android 的世界裡就沒有太多秘密, 它又是如何做 AV sync 的呢? 

首先它不喜歡用傳統的 PTS (Presentation Time Stamp) 做單位, 而是採用 µs (微秒). 1 秒等於 90,000 PTS, 也等於 1,000,000 微秒. 即使在程式裡面還是難免用到 PTS 的概念, 不過 Android 的上層已經把它轉為時間的單位了.

在 Android 還使用 OpenCore 當播放器的時代, audio 和 video 分別和系統時間 (NPT = normal play time) 校正 [ref 1]. 由於媒體錄製的機器所使用的時鐘 (clock) 和播放器的時鐘會有一定的差異, 所以校正是必須的. 但是往往這個差異相當地小 (若干 ppm  – 百萬分之一的等級), 看完一部 MV 大概發現不了, 或許要看一部電影才會發現.

到了 StageFright 的時代, 計算好像有變複雜一點? 我只參考了兩篇網路文章 [ref 2, 3], 瞭解可能不夠全面. 不過以我對 AV sync 的瞭解, 看起來還是頗能自圓其說. 以下是我歸納的 rules.

1. 首先我們要知道依據播放器的時鐘, 我們已經播出了多少微秒. 請注意下面的 mSampleRate 其實是 frame rate. 這樣算出來的單位才會是秒, 乘上 106 就變成微秒.

2. 再來就是這個媒體的資料, 宣稱它現在是幾微秒? 其實拿 PTS 算一下就知道了, 這個值叫做 mPositionTimeMediaUs. 我們帥氣地把兩個值相減, 就知道系統時間比較快? 還是媒體時間比較快?

mTimeSourceDeltaUs = mPositionTimeRealUs – mPositionTimeMediaUs

3. 知道兩者差異之後, 我們就可以開始校正了. 我們想要播系統中第 N 秒 (RealTimeUs) 的資料時, 我們把這個 N 秒減去 mTimeSourceDeltaUs, 就知道該播的是媒體中第幾秒 (nowUs) 的資料.

nowUsRealTimeUs – mTimeSourceDeltaUs.

4. 從上下文觀察, 此處的 RealTimeUs 是由 audio 所計算出來的. 於是我們知道 Video 也應該要播媒體中的第 nowUs 秒的資料了. 不過 Video 該不該播出來呢? 要看現在 video 已經準備要播出的那一張的時鐘 (timeUs) 而定. 如果 nowUstimeUs 還小, 那麼表示我這張太早了, 稍微晚一點再播就 OK. 早多少可以接受呢? Android 說凡是早了 10 ms (毫秒) 以上, 都拖個 10 ms 再播!

如果 nowUs 比 timeUs 大, 表示遲到了!

lateness =  nowUs – timeUs.

5. 既然遲到還有甚麼藉口? 只要遲到 40 ms 以上, 一律趕快播! 

6. 如果竟然遲到超過半秒, 說不定 audio 都有問題了!? 所以會強制去 call 一次 AudioPlayergetMediaTimeMapping(&realTimeUs, &mediaTimeUs) 重新取得 realTimUs 和 mediaTimeUs.

以上就是我粗淺的心得, 還請路過的大神幫忙糾正, 順便去救一下 [ref 3] 呼救的那位. 謝謝!

[ref]

1. Android多媒体之OpenCore的A/V同步机制

2. Audio Mutlimedia Framework

3. understanding the logic behind the AV sync in Android 2.2

[後記]

雖然我應該去洗澡睡覺了, 但是還是忍不住講一下八卦. 上面用到微秒和毫秒的地方, 我檢查了一下, 大概沒寫錯. 我記得我大二考電子學第一次期中考的時候, 錯把微秒寫成 10-9, 結果被老師扣了 25 分. 整題計算都對, 只是在 Ans: 後面寫錯單位, 老師還是一分都不給我. 他 (當時還是讀博班的講師) 說: 如果你考博士班資格考 (俗稱 qualify), 錯任何一點點就沒分了. 等我自己考完 qualify, 我確認他是唬爛的…哈! 何況, 己所不欲勿施於人嘛.