1.模塊結構圖
2.模塊輸入輸出
2.1模塊輸入
(1)目標碼率
平滑發送模塊通過外部設置的目標碼率,來決定數據包發送的速度,一般在網絡條件發生改變的情況下會更新設置的碼率。
(2)數據包
編碼模塊在完成編碼後會將視頻幀數據傳遞到 rtp_rtcp 模塊的 RtpSenderVideo 中進行數據包組裝,然後傳遞到 pacing 模塊進行平滑發送處理。
2.2模塊輸出
pacing 模塊的輸出同樣是數據包,只是在 pacing 模塊對發送速度進行了控制。
3.模塊處理過程
3.1模塊做了什麼處理
將數據包存入緩衝隊列中,根據目標碼率計算髮送預算,如果在 5ms 週期內可以進行發送處理,就發送數據包,同時將數據包存入 RtpPacketHistory 中。
3.2 數據包緩存過程
數據包緩存過程從 RtpSenderVideo 開始,因爲在此之前還沒有產生數據包,編碼後的視頻幀還是原始未組包的壓縮視頻數據。
在 webrtc 中,平滑發送是否開啓是可選的,如果不開啓平滑發送,那麼數據包會到達 NonPacedPacketSender 然後直接通過網絡發送出去,否則會發送到 PacedSender 被緩存起來,根據設置的目標碼率進行發送。
3.3數據包週期處理過程
數據包週期處理過程主要涉及兩個類,分別是 PacedSender 和 PacingController,PacedSender 是一個 Module 的子類,因此可以實現週期處理。
這裏重點分析週期模式下媒體數據包的發送過程,對於填充包和動態模式暫時不考慮。
PacingController 有兩種數據包處理模式,一種是週期模式,一種是動態模式,週期模式下根據發送預算按照 5ms 間隔處理數據包,動態模式下根據數據包發送債務來決定需要等待多久再進行下一次發送。(發送債務是指當前進行一次 m 字節的數據發送後,就增加了 m 字節的債務,目標發送碼率爲 v,那麼需要等待的時間爲 m/v,之後纔可以進行下一次發送。)
在 PacedSender::TimeUntilNextProcess () 函數中,如果是週期發送模式,那麼就會在 5ms 後進行下一次數據包發送處理。
在 PacedSender::Process() 函數中,會調用 PacingController::ProcessPackets() 函數進行具體的數據包處理。
在 PacingController 中有一個 drain_large_queues_ 變量,從字面上來理解是將大的隊列排空,也就是迅速將隊列中的數據包發送出去。如果這個變量爲真,那麼就會根據隊列中的數據量和發送時間限制,計算一個發送碼率,如果這個計算結果大於根據擁塞控制設置的碼率,就用這個較大的碼率來指導後續數據包發送過程。
在週期模式下,當進入 PacingController::ProcessPackets() 時會調用 PacingController::UpdateTimeAndGetElapsed() 函數,更新 last_process_time_ 值爲 now,也就是當前時間,並返回兩次調用的時間間隔 elapsed_time,這個值用於更新週期模式下的發送預算,在週期發送模式下,只有發送預算 bytes_remaining_ 爲正值,才能發送數據包。
PacingController::GetPendingPacket() 函數用於從緩存隊列裏獲取數據包,如果 media_budget_ 的 bytes_remaining_ 大於零,就可以拿到一個有效數據包,否則拿到一個空指針,如果拿到有效數據包就可以調用發送接口進行發送處理,發送完成後就再次判斷髮送預算和取數據包發送,直到發送預算耗盡。如果因爲發送預算耗盡,無法拿到數據包發送就可以退出循環了,等待下一次 PacingController::ProcessPackets() 被調用。
3.4數據包發送過程
數據包從平滑發送模塊的緩存中發送,起點是 PacingController::ProcessPackets(),在這個函數中調用 PacedSender::SendRtpPacket() 函數進行發送,用類圖來描述發送過程如下:
數據包發送過程基本按照類圖從左到右發送,數據包傳到 RtpSenderEgress 時,一方面發送到網絡,一方面對數據包做緩存,以備響應 nack 模塊的重傳請求。
4.小結
平滑發送模塊在 webrtc 衆多模塊中相對比較簡單,沒有很複雜的算法,理解起來相對比較容易,不過還是有些細節需要仔細揣摩其實現意圖。