1. 程式人生 > >WebRTC:丟包重傳的實現方式——NackTracker類

WebRTC:丟包重傳的實現方式——NackTracker類

WebRTC通過NackTracker類實現丟包重傳,下面是該類的原始碼。原始碼中包含公共變數和方法,也包含私有變數和方法,公共變數和方法可以被看作是對外部提供的介面。

class NackTracker {
public:
    // 重傳列表的上限為500個包(20ms,10s)
    static const size_t kNackListSizeLimit = 500; 

    // 建立
    static NackTracker* Create(int nack_threshold_packets);

    ~NackTracker();
 
    // 設定重傳列表的最大尺寸,上限為 kNackListSizeLimit,值儲存在 max_nack_list_size_
    void SetMaxNackListSize(size_t max_nack_list_size);

    // 更新取樣率,儲存到 sample_rate_khz_
    void UpdateSampleRate(int sample_rate_hz);

    // 更新上一個解碼完成的資料包的序列號和時間戳
    void UpdateLastDecodedPacket(uint16_t sequence_number, uint32_t timestamp);

    // 更新上一個接收到的資料包的序列號和時間戳
    void UpdateLastReceivedPacket(uint16_t sequence_number, uint32_t timestamp);

    // 獲取重傳列表
    std::vector<uint16_t> GetNackList(int64_t round_trip_time_ms) const;

    // 狀態重置
    void Reset();
private:

    FRIEND_TEST_ALL_PREFIXES(NackTrackerTest, EstimateTimestampAndTimeToPlay);

    struct NackElement {
        NackElement(int64_t initial_time_to_play_ms,
                    uint32_t initial_timestamp,
                    bool missing)
            : time_to_play_ms(initial_time_to_play_ms),
              estimated_timestamp(initial_timestamp),
              is_missing(missing) {}

        int64_t time_to_play_ms;

        uint32_t estimated_timestamp;

        bool is_missing;
    };

    class NackListCompare {
    public:
        bool operator()(uint16_t sequence_number_old,
                        uint16_t sequence_number_new) const {
        return IsNewerSequenceNumber(sequence_number_new, sequence_number_old);
        }
    };

    // 重傳列表的巨集定義
    typedef std::map<uint16_t, NackElement, NackListCompare> NackList;

    // 顯示定義建構函式
    explicit NackTracker(int nack_threshold_packets);

    // 得到重傳列表(重傳列表中是丟失資料包的序列號)
    NackList GetNackList() const;

    // 根據當前時間戳,將丟失的資料包新增到重傳列表中
    void AddToList(uint16_t sequence_number_current_received_rtp);

    void UpdateEstimatedPlayoutTimeBy10ms();

    // 更新每一個數據包包含的樣本數
    void UpdateSamplesPerPacket(uint16_t sequence_number_current_received_rtp,
                                uint32_t timestamp_current_received_rtp);
    
    // 更新重傳列表
    void UpdateList(uint16_t sequence_number_current_received_rtp);

    // 將延時包設定為丟包
    void ChangeFromLateToMissing(uint16_t sequence_number_current_received_rtp);

    // 將超出重傳列表上限的多餘資料包清除
    void LimitNackListSize();

    uint32_t EstimateTimestamp(uint16_t sequence_number);

    int64_t TimeToPlay(uint32_t timestamp) const;

    // 設定延遲閾值,閾值以內為延遲,閾值以外為丟包
    const int nack_threshold_packets_;

    // 上一個接收到的資料包的序列號
    uint16_t sequence_num_last_received_rtp_;

    // 上一個接收到的資料包的時間戳
    uint32_t timestamp_last_received_rtp_;

    // 是否接收到過資料包
    bool any_rtp_received_;

    // 上一個解碼後的資料包的序列號
    uint16_t sequence_num_last_decoded_rtp_;

    // 上一個解碼後的資料包的時間戳
    uint32_t timestamp_last_decoded_rtp_;

    // 是否解碼過資料包
    bool any_rtp_decoded_;

    // 取樣率(單位kHz)
    int sample_rate_khz_;

    // 單個數據包包含的樣本數
    int samples_per_packet_;

    // 重傳列表
    NackList nack_list_;

    // 重傳列表的最大上限
    size_t max_nack_list_size_;
};

從公共方法來看,丟包重傳的功能主要圍繞 UpdateLastDecodedPacket、UpdateLastReceivedPacket 和 GetNackList 來實現。

Create:用來建立 NackTracker 的例項Reset:用於狀態重置UpdateSampleRate:用於更新取樣率,接收端取樣率的變化通常意味著通訊媒體協商的改變,會清空所有緩衝區,相當於重新建立了一個會話。GetNackList:得到重傳列表,用來構造成一個丟失重傳報文,傳送給對端。UpdateLastReceivedPacket:接收端每接收一個數據包就要呼叫此方法,更新單個包中的樣本數量,更新列表(將nack_threshold_packets_範圍之外的包劃定為丟包,接著當前收到資料包的時間戳和序列號和上一個收到的時間戳和序列號相比,判斷是否有丟失的資料包並且計算丟包的序列號和時間戳,並將其存入重傳列表)。將當前收到的RTP資料包儲存為上一個收到的資料包,為下一個資料包提供參考。刪除超出重傳列表上限的資料包。