日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

WebRTC Video JitterBuffer

發(fā)布時間:2023/12/14 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WebRTC Video JitterBuffer 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

一. 前言

二. Video JitterBuffer架構(gòu)

三. PacketBuffer

四. ReferenceFinder

五. FrameBuffer


一. 前言

????????音視頻傳輸通常使用 UDP,由于網(wǎng)絡(luò)中存在丟包,抖動,亂序等現(xiàn)象,接收端收到的媒體包需要有個包緩沖區(qū)存放,對于視頻而言,一幀數(shù)據(jù)可能被打包到多個 RTP 包傳輸,因此接收端收到 RTP 包后會判斷是否可以組成視頻幀,如果可以組成視頻幀還要判斷其參考幀是否存在,如果存在則將該幀送入幀緩沖區(qū),等待解碼線程進行解碼。

二. Video JitterBuffer架構(gòu)

????????如上所示,RtpVideoStreamReceiver 是收視頻包的處理類,其中的 Video JitterBuffer 邏輯主要由 PacketBuffer,RtpFrameReferenceFinder 和 FrameBuffer 互相協(xié)作實現(xiàn)。

? ? ? ? PacketBuffer 是 RTP 包的緩沖區(qū),收到 RTP 包后根據(jù)序列號存放到環(huán)形數(shù)組的特定位置,如果某一幀對應的 RTP 包收集完整則彈出該幀的所有 RTP 包數(shù)據(jù)。

? ? ? ? RtpFrameReferenceFinder 用于幀參考關(guān)系的查找確認,例如對于 I 幀不需要參考其他幀就可以進行解碼,P 幀則需要前向參考,而 B 幀則需要前后雙向參考,因此某一幀的數(shù)據(jù)收集完整后,還需要等待其參考幀就緒才能送入幀緩沖區(qū)等待解碼。

? ? ? ? FrameBuffer 為幀緩沖區(qū),解碼線程會讀取該緩沖區(qū)的視頻幀數(shù)據(jù)進行解碼。

三. PacketBuffer

????????一個視頻幀在發(fā)送時可能被打包成多個 RTP 包,接收端接收時需要有個包級別的緩沖區(qū),該緩沖區(qū)等待接收幀的完整 RTP 包,然后提交給下個流程進行組幀等處理。

????????PacketBuffer 的實現(xiàn)代碼在 modules/video_coding/packet_buffer.h 和 modules/video_coding/packet_buffer.cc 中。

? ? ? ? PacketBuffer 類涉及的成員變量和方法如上所示,其中最重要的成員是?std::vector<std::unique_ptr<Packet>> buffer_,它是一個用于存放 Packet 的動態(tài)環(huán)形數(shù)組(起始大小為 512,最大為 2048),即接收到 RTP 包后根據(jù)其序列號將包存放到該環(huán)形數(shù)組的對應位置(index = seq_num % buffer_.size()),每次插入 RTP 包都會判斷緩沖區(qū)靠前的 RTP 包是否已經(jīng)是某一視頻幀的完整數(shù)據(jù),如果是則將這些 RTP 包帶在 InsertPacket 函數(shù)的返回值?InsertResult 中。

? ? ? ? 對于 PacketBuffer 最重要的方法為 InsertPacket,它首先計算 RTP 包存放在環(huán)形數(shù)組的位置,如果該位置當前有存放數(shù)據(jù),則通過包序號判斷是否為重復包,是重復包則不做處理,如果不是重復包說明此時緩沖區(qū)已經(jīng)不夠用了,需要調(diào)用 ExpandBuffer 擴容后再繼續(xù)不斷嘗試將包塞入緩沖區(qū)。

????????每次 InsertPacket 將 Packet 存放到 buffer_[index] 后會再調(diào)用 FindFrames(seq_nunm),它查找當前是否收到了幀的完整數(shù)據(jù),如果是則將幀對應的完整 RTP 包返回存放到 result.packets 中,FindFrames 邏輯如下。

????????FindFrames 在 buffer_ 大小不為 0 的情況下,判斷插入 seq_num 的包后是否可能拿到幀的完整 RTP 包,如果不可能則先跳過,下次?InsertPacket 還會再調(diào)用 FindFrames 查看是否能拿到幀的完整 RTP 包。

PotentialNewFrame(uint16_t seq_num) 判斷的邏輯如下。

1. 如果是幀的第一個包,它是有可能湊成幀的完整數(shù)據(jù)的,一方面是有些非關(guān)鍵幀只需要一個 RTP 包,即便是需要多個 RTP 包的關(guān)鍵幀,seq_num 之后的包可能早就收齊存在于緩沖區(qū)了

2. 如果不是幀的第一個包,并且緩沖區(qū) index 位置的前一個位置是空數(shù)據(jù),說明這個幀的數(shù)據(jù)肯定不完整,是不可能湊出幀完整數(shù)據(jù)的

3. 如果 index 位置的前一個位置的包序號不是 seq_num - 1,或者時間戳跟 seq_num 包的時間戳不一樣,說明它們是沒有關(guān)系的包

4. 如果 index 前一個位置的包的 continuous 為 true,說明?seq_num 對應的幀在 seq_num 之前的包已經(jīng)是收齊連續(xù)的了,再收到該包是可能湊齊幀完整數(shù)據(jù)的

? ? ? ? 如果 PotentialNewFrame 為?true 并且 buffer_[index] 是幀的最后一個包,說明此時可以拿到幀對應的完整 RTP 包數(shù)據(jù)了。

? ? ? ? start_index 的值是從幀的最后一個包不斷往前遞減,如果遇到對應的位置是 is_first_packet_in_frame 說明幀的包數(shù)據(jù)已經(jīng)從尾部遍歷到首部,此時只要把 [start_seq_num, end_seq_num) 位置的包都存放到 found_frames 中即可,start_seq_num 是從 seq_num 從后一直遍歷往前到幀的第一個包的。

至此 PacketBuffer 的收包存放以及當幀對應的 RTP 包完整時如何獲取到的流程已經(jīng)介紹完成。?

四. ReferenceFinder

????????ReferenceFinder 的作用是判斷當前幀的參考幀是否存在,如果存在其參考幀則將當前幀送到 FrameBuffer,等待解碼線程對其進行解碼,如果不存在則暫存等待其參考幀到達后再把它送到 FrameBuffer。(例如對于 I 幀不需要參考其他幀,對于 P 幀需要前向參考,對于 B 幀需要前后雙向參考)

????????如果調(diào)用 PacketBuffer InsertPacket 能組成完整幀數(shù)據(jù),則其返回的?struct InsertResult 值中會攜帶該幀對應的完整 Packet 數(shù)據(jù),然后調(diào)用 OnInsertedPacket。

struct InsertResult {std::vector<std::unique_ptr<Packet>> packets;// Indicates if the packet buffer was cleared, which means that a key// frame request should be sent.bool buffer_cleared = false; }; OnInsertedPacket(packet_buffer_.InsertPacket(std::move(packet)));

? ? ? ? OnInsertedPacket 邏輯如下,如果 result.packets 不為空則遍歷 result.packets?將 Packet 的負載數(shù)據(jù)存放到 payloads,把 Packet 的信息存放到 packet_infos,當遍歷到幀的最后一個包時會調(diào)用 video depacketizer 的 AssembleFrame 將 payloads 存放到 bitstream 中(bitstream 實際上是一個 uint8_t 數(shù)組),然后再調(diào)用 OnAssembledFrame。

????????RtpVideoStreamReceiver::OnAssembledFrame 邏輯如下,如果之前還沒收到過任何幀并且當前的幀也不是關(guān)鍵幀則進行關(guān)鍵幀請求(RequestKeyFrame),之后再判斷?current_codec_ 與 frame codec?是否一致或設(shè)置 current_codec_,然后再調(diào)用?video_coding::RtpFrameReferenceFinder 的 ManageFrame 方法查找當前 frame 對應的參考幀。

????????RtpFrameReferenceFinder::ManageFrame 邏輯如下,主要是調(diào)用 ManageFrameInternal 判斷當前?frame 是否有對應的參考幀,如果還沒找到參考幀則將 frame 暫時保存到 stashed_frames_ 中,如果當前 frame 已經(jīng)找到參考幀則調(diào)用 HandOffFrame 處理將其送進 FrameBuffer,然后再把暫存的 frame 重新彈出查看它們是否能找到參考幀,如果找到參考幀則也送到 FrameBuffer 中。

????????關(guān)于如何查找?guī)瑢膮⒖紟欠翊嬖?#xff0c;VP8,VP9,H264 的判斷邏輯不相同,具體可以查看 ManageFrameVp8,ManageFrameVp9,ManageFrameH264,本文不對此進行展開。

五. FrameBuffer

? ? ? ? ReferenceFinder 查找到幀對應的參考幀后會將該幀送入 FrameBuffer,解碼線程會對幀進行解碼,HandOffFrame 函數(shù)調(diào)用 frame_callback_->OnCompleteFrame 將幀送入 FrameBuffer。

如下 VideoReceiveStream::OnCompleteFrame 中最關(guān)鍵的邏輯為 frame_buffer_->InsertFrame。

FrameBuffer 中最重要的成員為 frames_,它是一個 FrameMap 類型的對象,存儲 frameId 與 frame 的映射關(guān)系,FrameBuffer::InsertFrame 就是將 frame 插入到?FrameMap 中。

using FrameMap = std::map<VideoLayerFrameId, FrameInfo>;

????????解碼邏輯如下所示,調(diào)用 frame_buffer_->NextFrame 取出 buffer 中的幀,然后調(diào)用 HandleEncodedFrame 進行解碼。

總結(jié)

以上是生活随笔為你收集整理的WebRTC Video JitterBuffer的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。