同樣的畫質,AV1 能比 H.264 少花至少兩成 bitrate。但開源 AV1 編碼器跑在手機上,功耗多了 14%——對一個延遲預算只有 300 毫秒的即時視訊系統來說,這個數字本來足以讓整件事不成立。
把 AV1 塞進 300ms 的即時視訊
AV1 是個為串流與點播設計的 codec——離線編一次、播很多次,編碼器可以慢慢搜尋最佳解。即時視訊(RTC)的場景剛好相反:影格在毫秒級內產生、編碼、傳送、解碼,整條 pipeline 的端到端延遲理想上要壓在 300 毫秒以下。Meta 把 AV1 導入 Messenger 與 WhatsApp 的 RTC 並做到規模化,現在支援大多數行動裝置。難的從來不是「AV1 能不能省 bitrate」——那是已知的;難的是在硬延遲預算與手機功耗預算的雙重夾擊下,讓省下來的 bitrate 真的落到使用者手上。這篇拆的是這套系統的五個組成:把編碼器壓到跟 H.264 同功耗的 preset、決定哪些裝置能跑的 ML 模型、三段式的自適應 fallback、抗丟包的錯誤韌性機制,以及為這一切付出的代價。
先把那個「本來不成立」的數字攤開。Meta 拿開源 AV1 編碼器在 Pixel 8 上實測,結論是「a 14% increase in power usage compared to H.264/AVC — a significant challenge for mobile deployment」。RTC 的功耗不是抽象指標:手機發熱會觸發 thermal throttling,CPU 一降頻,編碼延遲立刻爆掉 300ms 預算,視訊開始卡頓。所以第一個工程問題不是「怎麼更省 bitrate」,而是「怎麼用得起這個 codec」。下面這張圖把整個取捨空間放在一起——拖動 preset,看 bitrate 省下的量、功耗代價、與編碼延遲三者如何同時移動。
drag the preset slider · 5 encoder presets across the tradeoff space
ultra-low-complexity preset:把編碼器壓回 H.264 的功耗
解法直接面對那 14% 的功耗。Meta 沒有試圖「優化」開源編碼器去把功耗榨掉幾個百分點,而是換掉編碼器的工作量本身——他們「adopted an internal low-complexity encoder that has similar power consumption as H.264 baseline」,並在其上「developed an ultra-low-complexity preset」,目標是「encoding complexity comparable to H.264/AVC」。換句話說,AV1 的壓縮優勢來自更多工具(更大的 block 分割搜尋、更多 prediction mode、更精細的 transform);ultra-low-complexity preset 的本質是策略性地放棄一部分這些工具的搜尋深度,把每影格的計算量壓回 H.264 同級,用「少一點壓縮收益」換「能在手機上即時跑」。
這裡有個容易被忽略的限制——VBV。Meta 的定義是:「The Video Buffering Verifier (VBV) is a leaky-bucket-based measurement used to ensure that an encoded video stream can be correctly buffered and played back at the decoder。」VBV delay 衡量的是解碼端緩衝區需要先囤多少資料才能平順播放;囤得越多,畫面起播與互動回饋就越延遲。對 RTC,Meta 的目標是「the desired VBV delay for RTC is below 200 ms」。這是 300ms 端到端預算裡分給緩衝的那一段——編碼器不只要快,輸出的 bitrate 起伏還得夠平穩,否則某幾個影格突然變大就會把 VBV delay 推爆。ultra-low-complexity preset 與 RTC 的 rate control 必須一起調,才能同時守住延遲與這個 leaky bucket。
leaky bucket 這個比喻值得拆開看,因為它解釋了為什麼「平均 bitrate 達標」對 RTC 還不夠。想像一個底部漏水速率固定的桶:編碼器每產出一張影格就往桶裡倒一筆資料,解碼端則以固定速率把水放掉播出去。桶不能溢出(資料來得太快、太大,緩衝爆掉),也不能見底(資料來得太慢,沒東西可播、畫面凍住)。VBV delay 就是這個桶在開播前要先蓄多深的水位——蓄越深越安全、越能吸收 bitrate 的瞬間尖峰,但代價是起播延遲越高。串流場景可以把桶蓄得很深,反正觀眾不在乎多等兩秒;RTC 把這個水位硬壓到 200ms 以下,等於只給編碼器一個很淺的桶,任何一張突然變大的影格都可能瞬間溢出。所以 ultra-low-complexity preset 放棄的不只是搜尋深度,還包括那些會讓單張影格體積暴衝的工具——輸出要平穩,桶才不會在淺水位下溢出。下面把這個淺桶的兩端風險畫在一起。
值得標記的是 preset 與功耗的方向:preset 越低、搜尋越淺,功耗越低、編碼越快,但 bitrate 節省也越少。ultra-low-complexity 落在「功耗持平、節省約 20%」這一端,是 Meta 願意接受的折衷——20% 是底線(「at least a 20% bitrate reduction」),不是峰值。把 preset 往高品質端推,bitrate 能省更多,但功耗與延遲很快就把它推出 RTC 的可行區間。這正是上面那張圖的左下角為什麼是唯一的安全角落。
裝置資格模型:用真實效能資料分級,而不是猜
就算有了同功耗的 preset,「這支手機跑得動 AV1 嗎」仍是個別裝置的問題。低階與中階機海量、SoC 世代差異巨大,靠裝置型號白名單既不準也維護不了。Meta 的做法是用 ML 模型依真實效能資料給裝置分級。第一版鋪開後累積資料:「Model V1.1, rolled out in August 2025 and broadened AV1 traffic across an increasing set of devices。」這多出來的流量本身就是訓練材料——「That additional traffic contributed to a dedicated AV1-only dataset that became both larger and more representative over time。」
資料變多變準之後,模型升級到兩層:「With this richer data, we built Model V2, introducing a two-tier approach that differentiates between higher-end and lower-end devices。」兩層分級的意義在於,AV1 的開關不再是二元的「能 / 不能」,而是依裝置等級給不同的 preset 與品質目標——高階機可以跑得更積極一點,低階機保守一點。這形成一個正回饋:開得越多,AV1-only 的真實流量越多,資料集越大也越有代表性,模型分得越細,又能安全地開給更多裝置。先用保守的第一版模型開一小群裝置、拿這群裝置回報的真實編解碼效能去訓練更準的第二版、再擴大開放範圍——這是一條典型的「靠生產流量自我餵養」的 rollout 路徑,而不是一次性把白名單拍死。資格模型決定的是「進場資格」,但進場之後,裝置的真實效能還是會在執行時波動——這就是下一層 fallback 要接的事。
這條 rollout 路徑裡有個雞生蛋的問題值得點出來:要訓練一個準的 AV1 裝置模型,你需要大量「裝置實際跑 AV1」的效能資料;但要安全地開 AV1 給裝置,你又得先有個準的模型。Meta 的解法是把這個循環拆成幾棒接力——先用一個保守的早期模型把 AV1 開給相對少、相對有把握的一群裝置,這群裝置回報的真實編解碼資料就成了訓練材料。V1.1 在 2025 年 8 月鋪開、把流量「broadened across an increasing set of devices」,正是這一棒;它的價值不只在於多服務了一些裝置,更在於「That additional traffic contributed to a dedicated AV1-only dataset that became both larger and more representative over time」——多出來的流量本身就是下一版模型的燃料。等到資料集夠大夠有代表性,V2 才有底氣從單一門檻改成高/低階兩層分級。值得留意的是這裡的因果方向:不是先想出兩層架構才去收資料,而是資料的規模與代表性到位了,才讓兩層分級從紙上談兵變成做得準的事。下面把這條從單門檻到兩層、靠流量自我餵養的演進攤成一條線。
三段式自適應 fallback:在執行時守住延遲
資格模型是靜態的、在連線前判斷。但手機的即時效能會因為發熱、背景任務、訊號變化而動態波動,一支「資格合格」的裝置也可能在通話中途突然編不動——資格模型給的是進場資格,不是執行時的保證。一旦編碼延遲開始逼近預算上限,系統不能等下一通電話才修正,必須在這一通的這幾百毫秒內就反應。Meta 的防線是三段遞進的 fallback,按代價從小到大排——能用調 preset 解決的,就不要整個切回 H.264,因為切回 H.264 等於把好不容易省下的兩成 bitrate 整個吐回去。下面這張圖把三段的觸發條件與作用攤開,點任一段看它接的是哪種失敗。
三段遞進 fallback——代價從小到大,先試最便宜的
click a stage above
① 調編碼器 preset · 觸發
「If encoding latency becomes too high — meaning the device is close to being unable to encode in real time — we reduce encoder complexity。」這是最輕的反應:仍然是 AV1,只是把搜尋深度再降一檔,先試著靠犧牲一點壓縮率把延遲拉回預算內。
它不負責的事:解碼端的問題、網路丟包。這一段只管「本地編碼器追不追得上即時」。
② 本地切回 H.264 · 觸發
「If lowering the encoder preset still does not reduce encoding latency to an appropriate level, we apply codec switching。」preset 已經降到底仍編不動,代表這支裝置此刻就是跑不了 AV1——放棄壓縮優勢,換回一定編得動的 H.264。
它不負責的事:對端解碼能力。這是發送端對「自己編不動」的認輸。
③ peer 解碼端感知 · 觸發
「If the sender detects that the peer cannot decode AV1 in real time, it switches back to H.264/AVC。」失敗點轉移到通話的另一端——自己編得很順,但對方解不動。發送端據此切回 H.264,因為 codec 是雙方的合約,任一端跟不上整條鏈就斷。
它不負責的事:本地編碼延遲(那是前兩段的事)。這一段把判斷依據放到對端回報的解碼狀態上。
三段的排序本身就是一個設計決定:把最便宜的反應放最前面。降 preset 不丟掉 AV1,只損失一點壓縮率;本地切 codec 才真正放棄 AV1 的 bitrate 優勢;peer 感知切換則承認「codec 是雙方合約」這個事實——AV1 是端到端協商的,發送端再強,只要對端解碼跟不上,整段就得退回雙方都吃得下的 H.264。三段共同守的是同一條線:寧可少省一點 bitrate,也不能讓延遲爆掉 300ms。
錯誤韌性:temporal layers 與 Long-Term References
延遲守住了,還有網路。RTC 走 UDP,丟包是常態而非例外,重傳往往來不及——等封包重傳回來,那一影格的播放時刻早就過了。傳統做法是丟包就要求一張 keyframe(IDR)重新同步,但 keyframe 又大又貴:它不參考任何過去影格、整張畫面從零編起,在已經緊繃的上行頻寬上等於雪上加霜,而且要等它編好、傳到、解出才恢復,這段空窗裡畫面就凍住,互動體驗瞬間崩掉。Meta 用兩個機制讓解碼鏈在丟包下盡量不斷,真的斷了也盡量不靠 keyframe 接回——一個降低丟包的殺傷力,一個加速斷裂後的復原。
第一個是 temporal layers(時間分層 / scalable video coding)。影格被分到不同的時間層:「The base layer (temporal layer 0) provides a lower frame rate on its own, while enhancement layers (temporal layer N) add intermediate frames。」base layer 自成一個可獨立解碼的低幀率串流,enhancement layer 只是往中間插幀提高流暢度。關鍵性質是依賴方向——enhancement 依賴 base,base 不依賴 enhancement。所以「If enhancement-layer packets are lost or arrive too late, decoding can still proceed using the base layer without stalling。」丟掉的若是 enhancement 影格,畫面只是短暫變得不那麼流暢,但不會凍結;只有 base layer 的丟失才是真正的麻煩。這把「任何丟包都可能凍畫面」降級成「只有一小部分關鍵影格的丟失才會凍畫面」。
第二個機制是 Long-Term References(LTR)。一般的 P-frame 只參考前一張影格,一旦中間某張丟了,後面全部解不出來,這就是解碼鏈中斷。LTR 的做法是把某些「確認對端已經收到」的影格標記為長期參考影格,留在解碼器的參考緩衝裡。當鏈斷掉時,「an incoming LTRP frame—predicted from a previously decoded LTR frame—instantly resynchronizes sender and receiver」——發送端送一張 LTRP,它不參考剛剛丟失的那串影格,而是回頭參考那張早就確認還在的 LTR 影格,於是雙方瞬間對回同一個狀態。
這兩個機制處理的是兩種不同的丟包後果,值得分清楚。temporal layers 管的是「丟包當下別凍」——只要丟的是 enhancement 影格,base layer 還在,畫面流暢度暫時下降但不中斷,這是一種降級而非中斷。它之所以成立,全靠那個單向依賴:enhancement 參考 base、base 不參考 enhancement,所以拿掉 enhancement 不會讓 base 解不出來。但這個保護有個前提——丟的得是 enhancement。一旦 base layer 自己丟了,或是丟了一長串連 base 都接不回來,畫面就真的會斷,這時 temporal layers 救不了,得換 LTR 上場。LTR 管的是「斷了之後別靠 keyframe 接回」。
為什麼這比要 keyframe 快?Meta 的結論是「LTR is more efficient for loss recovery than forcing a key frame or relying on retransmissions」。差別在於 keyframe 是從零重建整張畫面、不參考任何過去,所以又大又貴;LTRP 是對一張已知存在的舊影格做 delta,資料量小得多、編得快、傳得快。而且 LTR 是基於 ACK 的——發送端知道哪幾張影格對端確實收到了,才會拿它們當 anchor,這個「確認過才用」的設計避免了參考到對端其實也沒收到的影格。換個角度看,LTR 是用一點記憶體換頻寬與延遲:解碼器得多留住幾張被確認過的舊影格不丟,這幾張就成了隨時可以跳回去的安全點;而傳統「丟包就要 keyframe」等於每次斷裂都付一次全畫面重建的代價,在已經緊繃的上行頻寬上,這個代價往往比丟包本身更傷。temporal layers 負責「丟包時盡量不凍」,LTR 負責「真的斷了時盡快接回」,兩者一起把 RTC 在不可靠網路下的體驗撐住,而不必動用昂貴的 keyframe request。
代價這本帳:功耗、binary、與怎麼還
這套系統不是免費的。Meta 把帳算得很清楚,最重的兩筆是裝置功耗與 app binary 大小。功耗前面提過——導入 AV1 在 Pixel 8 上量到 14% 的增幅;ultra-low-complexity preset 把編碼這部分壓回 H.264 同級,但 AV1 本身的解碼與其他開銷仍讓整體比純 H.264 重一些。binary 這筆更微妙:「AV1 support adds 1.7 MB to the application(600 kB compressed)」。聽起來不多,但 Meta 的說法是「a 600 kB increase could consume an entire year's binary size budget」——大型 app 的每次更新都有嚴格的體積預算,binary 一胖,更新成功率就掉(使用者在弱網或低儲存裝置上更新失敗的比例上升),這是直接影響觸及率的東西。
還這筆 binary 債的方法有兩條。一是從 codec 內部下手——移除 RTC 用不到的 AV1 工具。Meta 舉的例子是 quantization matrices:「removing QM frees 60 kB of binary space」。AV1 規格裡有大量為各種場景準備的工具,RTC 這個特定 profile 並不需要全部,把確定用不到的編譯掉,就能一塊一塊把 600 kB 還回去。二是跨功能共用 library——「we can share codec libraries across features — such as video message transcoding — and leverage built-in platform codec support」。AV1 不只 RTC 在用,影片訊息轉碼等功能也需要 codec;與其每個功能各自打包一份,不如共用同一份 library,再盡量借用作業系統內建的 codec 支援,攤掉重複的體積。
這本帳值得記住的不是任何單一數字,而是它被算出來這件事。把 AV1 塞進 RTC 不是「開個 flag」,而是一連串拿可量測代價換可量測收益的決定:用一點壓縮率換同功耗的 preset,用 ML 分級換安全的裝置覆蓋,用三段 fallback 換延遲保證,用 temporal layers 加 LTR 換抗丟包,最後用移除工具與共用 library 把 binary 債還掉。每一步都有明確的還價對象,沒有一步是靠「之後再說」糊過去的。值得對照的是,移除 QM 省下的 60 kB 跟壓縮後 600 kB 的代價放在一起看,比例上只還了十分之一——剩下的得靠共用 library 與平台內建 codec 一點一點攤平,這也說明為什麼 binary 預算對 Meta 這種規模的 app 是要逐 kB 守的東西,而不是隨手就能吸收的零頭。
What this enables:當一個 codec 的功耗、binary、延遲代價都能被逐項量化並逐項抵銷,「在 300ms 硬預算下換掉即時視訊的 codec」就從一次性的英雄工程,變成一條可以複用的導入路徑——下一個新 codec(AV2、或任何後繼者)要進 RTC,走的會是同一套帳本。