WebRTC Qos 雜問

為什么一開始fps會(huì)降到1吠谢,后來有了正常的兩方通話后又恢復(fù)到30

WebRTC對每一幀調(diào)用 VideoStreamEncoder::OnFrame,然后調(diào)用VideoStreamEncoder::MaybeEncodeVideoFrame 這個(gè)方法中可能會(huì)執(zhí)行最終的編碼。

定義了一個(gè) posted_frames_waiting_for_encode_變量表示當(dāng)前等待編碼的幀數(shù),通過它判斷是否應(yīng)該跳過來不及編碼的幀虹统。

每次OnFrame調(diào)用會(huì)在調(diào)用的線程執(zhí)行 posted_frames_waiting_for_encode_++,然后在編碼線程中如果判斷posted_frames_waiting_for_encode_ > 1則跳過編碼宜雀,如果posted_frames_waiting_for_encode_ == 1則進(jìn)行編碼。不論是否跳過還是執(zhí)行了編碼,這個(gè)值都會(huì)減一posted_frames_waiting_for_encode_--退子。這樣就保證了如果有多個(gè)幀正在等待編碼腕铸,則會(huì)編碼這些幀中的最晚的幀浓恳。

如果等待編碼的幀有多個(gè)晴圾,說明編碼性能趕不上設(shè)備采集幀率颂砸,編碼器的性能會(huì)最終影響fps,低性能會(huì)導(dǎo)致實(shí)際fps降低死姚。

另一方面人乓,在VideoSender中也通過改變編碼器的參數(shù)來改變實(shí)際的編碼幀率,該參數(shù)為encoder_params_.input_frame_rate都毒。

VideoSender調(diào)用VideoSender::UpdateEncoderParameters 更新幀率參數(shù)色罚,它調(diào)用media_optimization::MediaOptimization對象_mediaOpt
InputFrameRate()方法得到估算的幀率。

MediaOptimization是一個(gè)工具類账劲,它的功能之一就是估算input_frame_rate戳护,它會(huì)記錄每一幀的時(shí)間戳,然后根據(jù)最近兩秒的幀數(shù)來估算幀率瀑焦。

對每一幀腌且,調(diào)用MediaOptimization::DropFrame ,這個(gè)接口是用來判斷是否丟幀的榛瓮,每一幀都會(huì)調(diào)用這個(gè)方法(名字起得不好铺董,害得我查了很久才發(fā)現(xiàn)這個(gè)是每一幀都調(diào)用的)。具體方法:

  1. 每次DropFrame時(shí)記錄一個(gè)時(shí)間戳禀晓,插入到隊(duì)列中柄粹。
  2. 調(diào)用 InputFrameRate 時(shí)在隊(duì)列中查找一個(gè)區(qū)間,計(jì)算這個(gè)區(qū)間的每幀平均時(shí)間匆绣,即最大時(shí)間戳 減 最小時(shí)間戳除以數(shù)量驻右。
  3. 區(qū)間的計(jì)算:從隊(duì)列尾部開始,到與尾部時(shí)間戳之差小于2秒的最大值崎淳,即這個(gè)區(qū)間最大長度為2秒堪夭。
  4. 最后使用這個(gè)每幀平均時(shí)間,計(jì)算 input_frame_rate拣凹。

為什么后來又升上去了森爽,還沒有研究,大概是因?yàn)榉直媛实慕档蛯?dǎo)致編碼速度加快嚣镜,然后通過MediaOptimization的估算慢慢提升了幀率爬迟。

為什么分辨率會(huì)由剛開始的 720x1280 經(jīng)過幾秒后降到 360x640

VideoStreamEncoder::OnFrame
↓
VideoStreamEncoder::MaybeEncodeVideoFrame
↓
// 判斷是否應(yīng)該降低視頻能級,如果降級則調(diào)用 AdaptDown 菊匿,并且跳過該幀的解碼
VideoStreamEncoder::DropDueToSize
↓
VideoStreamEncoder::AdaptDown
↓
VideoStreamEncoder::VideoSourceProxy::RequestResolutionLowerThan
↓
VideoSourceInterface::AddOrUpdateSink // 最終改變輸入幀率的方法

DegradationPreference:猜測是協(xié)議層由對端設(shè)置的付呕,表示使用何種策略降低視頻能級计福。可選值有4種:

  1. DISABLED
  2. MAINTAIN_FRAMERATE
  3. MAINTAIN_RESOLUTION
  4. BALANCED

根據(jù)log判斷默認(rèn)值是 MAINTAIN_FRAMERATE徽职,后面只有在結(jié)束通信時(shí)設(shè)置成了 DISABLED象颖。猜測可能一直保持MAINTAIN_FRAMERATE不變。

VideoStreamEncoder::DropDueToSize 根據(jù)一個(gè)初始碼率encoder_start_bitrate_bps_來限制分辨率姆钉。將初始碼率分成3個(gè)檔次對應(yīng)一個(gè)最大分辨率:

  • [0, 300kbps]=>320x240
  • [300kbps, 500kbps]=>640x480
  • [500kbps, )=>沒有限制说订,

如果大于該檔次的最大分辨率就判斷為需要降低分辨率。

DropFrame是如何運(yùn)作的

MediaOptimization潮瓶,在兩個(gè)類中使用:

  • vcm::VideoSender
  • webrtc::VCMEncodedFrameCallback

MediaOptimization內(nèi)部使用FrameDropper陶冷,用來計(jì)算什么時(shí)候丟幀。


FEC在WEBRTC是怎么使用的毯辅?

rtp_sender_video.cc 文件中處理FEC埂伦、NACK等Qos功能。

payload悉罕,即SDP中定義的負(fù)載類型id赤屋。在代碼中立镶,payload >= 0 表示啟用壁袄,payload < 0表示關(guān)閉。

FlexfecSender flexfec_sender 負(fù)責(zé)flexfec的對象媚媒。google的 demo server 都不支持嗜逻,估計(jì)很少有支持的吧。如果有的話缭召,它是比ulpfec優(yōu)先的栈顷,可以在demo的設(shè)置項(xiàng)中打開開關(guān)。

red_payload_type_: redundant payload嵌巷。red用來持有ulpfec萄凤,沒有red也就沒有ulpfec。
ulpfec_payload_type_: ulpfec payload搪哪。

RTPSenderVideo::SendVideo() 最終發(fā)送視頻數(shù)RTP包的方法靡努,這個(gè)方法先計(jì)算包的數(shù)量,然后計(jì)算出來詳細(xì)的包大小晓折,最后一個(gè)包一個(gè)包的填充數(shù)據(jù)并發(fā)送惑朦。

對每個(gè)包

  1. 如果開啟了flexfec,則發(fā)送flexfec包
  2. 如果開啟了red漓概,則發(fā)送攜帶ulpfec的red包漾月,調(diào)用SendVideoPacketAsRedMaybeWithUlpfec
  3. 否則直接發(fā)送video數(shù)據(jù)

為什么H264的時(shí)候沒有啟用

RtpVideoSender::ConfigureProtection
↓
PayloadTypeSupportsSkippingFecPackets
// 它判斷了只有vp8和vp9才開啟FEC,也就是說h264不開啟FEC胃珍。

衡量FEC的效果


NACK with H264 為什么會(huì)導(dǎo)致 FEC 包的重傳梁肿?

一個(gè)完整的幀包含所有的數(shù)據(jù)蜓陌,是不需要重傳的,產(chǎn)生 NACK 的原因一定是判定了一幀中的某些包丟失了栈雳。那么一定有個(gè)機(jī)制來判斷一幀的完整性护奈,而這種機(jī)制對 H264 with FEC 一定是有缺陷的「缛遥可能是因?yàn)槊蛊欤及?+ FEC包都到達(dá)才判定為完整,因此導(dǎo)致了即使所有的原始數(shù)據(jù)包都到達(dá)蛀骇,有FEC包沒到達(dá)厌秒,也會(huì)被判定為幀不完整。

先要搞清楚兩個(gè)問題:一是選擇哪些包發(fā)送NACK擅憔?另一個(gè)是怎么判斷幀的完整性鸵闪?

哪些包需要發(fā)送NACK?

返回 NACK 列表的調(diào)用順序:

ModuleProcessThread
↓
VideoReceiver::Process
↓
VCMReceiver::NackList // 沒有計(jì)算暑诸,直接調(diào)用下面的方法
↓
VCMJitterBuffer::GetNackList(bool* request_key_frame) // 執(zhí)行具體計(jì)算的地方

ModuleProcessThread 周期性調(diào)用 VideoReceiver::Process 方法蚌讼,最后調(diào)用的 VCMJitterBuffer 中的方法獲取 NACK 列表,VCMJitterBuffer 負(fù)責(zé)計(jì)算哪些包是需要發(fā)送 NACK 的个榕,也就是確定 NACK 列表篡石。

VCMJitterBuffer::GetNackList 方法先根據(jù)時(shí)間和緩沖區(qū)大小更新missing_sequence_numbers_ 集合,使之不要超出最大限制西采。主要是根據(jù)一些條件刪除 missing_sequence_numbers_ 中的數(shù)據(jù)凰萨,但這些判斷與 VCMFrameBuffer 的狀態(tài)沒有太大關(guān)系。更多的是要判斷是否應(yīng)該 request_key_frame械馆,即請求關(guān)鍵幀胖眷。

然后 VCMJitterBuffer::GetNackListmissing_sequence_numbers_ 集合中的數(shù)據(jù)轉(zhuǎn)化成一個(gè)列表返回。

另一個(gè)更新 missing_sequence_numbers_ 集合的方法是VCMJitterBuffer::UpdateNackList霹崎,調(diào)用順序:

VCMJitterBuffer::InsertPacket(packet)
↓
VCMJitterBuffer::UpdateNackList(sequence_number)

UpdateNackList 的參數(shù) sequence_number 就是 InsertPacket 的參數(shù) packet 所持有的 sequence_number珊搀。UpdateNackList 根據(jù)新插入的 sequence_number 更新 missing_sequence_numbers_ 集合。主要邏輯如下:

  • latest_received_sequence_number_ 表示最新接收到的 sequence number尾菇,這個(gè)值會(huì)在 InsertPacketUpdateNackList 中更新境析。

  • sequence number 應(yīng)是連續(xù)的整數(shù),如果傳入的 sequence_numberlatest_received_sequence_number_ 要大错沽,也就是時(shí)間順序上要晚簿晓,如果兩者不是連續(xù)的(sequence_number > latest_received_sequence_number_ + 1),說明它們之間有其他 packet 沒有接收到千埃。那么這個(gè)區(qū)間內(nèi)的所有 sequence number 就要添加到 missing_sequence_numbers_ 集合中憔儿。

  • 如果sequence_numberlatest_received_sequence_number_ 要小,說明這個(gè) packet 是遲到的一個(gè)包放可,應(yīng)該在之前的處理過程中谒臼,已經(jīng)添加到了 missing_sequence_numbers_中朝刊,因此在 missing_sequence_numbers_ 中刪除這個(gè) sequence number 即可。

如何判斷幀完整

VCMJitterBuffer
↓
VCMFrameBuffer.GetState()
↓
VCMSessionInfo.complete()

判斷 VCMSessionInfo 完整有幾個(gè)條件:

  • VCMSessionInfo 中有第一個(gè) packet
  • VCMSessionInfo 中有最后一個(gè) packet
  • 所有的 packet 的 sequence number 都是連續(xù)的

這個(gè)條件很容易理解蜈缤,也沒有什么特別之處拾氓,關(guān)鍵在于如何判斷是否有最后一個(gè) packet,在代碼中由變量 last_packet_seq_num_ 存儲(chǔ)最后一個(gè) packet 的 sequence number底哥。

只有 VCMSessionInfo::InsertPacket 方法會(huì)更新 last_packet_seq_num_咙鞍,而其中也明確區(qū)分了 H264 和其他 codec:

if (packet.codec == kVideoCodecH264) {
    ...
    if (packet.markerBit &&
        (last_packet_seq_num_ == -1 ||
         IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_))) {
      last_packet_seq_num_ = packet.seqNum;
    }
  } else {
    ...
  }

注意判斷的條件:packet.markerBit,RTP包的 M 標(biāo)識(shí)位設(shè)置為1趾徽,才判定為最后一個(gè)包续滋,但看后面的條件 IsNewerSequenceNumber(packet.seqNum, last_packet_seq_num_) 還不一定只有一個(gè)包被設(shè)置了 M 標(biāo)識(shí)位。

問題:H264 RTP中是如何規(guī)定 M 標(biāo)志位表示幀的最后一個(gè)數(shù)據(jù)包的孵奶?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疲酌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子了袁,更是在濱河造成了極大的恐慌朗恳,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件载绿,死亡現(xiàn)場離奇詭異粥诫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)卢鹦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門臀脏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來劝堪,“玉大人冀自,你說我怎么就攤上這事∶肜玻” “怎么了熬粗?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長余境。 經(jīng)常有香客問我驻呐,道長,這世上最難降的妖魔是什么芳来? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任含末,我火速辦了婚禮,結(jié)果婚禮上即舌,老公的妹妹穿的比我還像新娘佣盒。我一直安慰自己,他們只是感情好顽聂,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布肥惭。 她就那樣靜靜地躺著盯仪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜜葱。 梳的紋絲不亂的頭發(fā)上全景,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音牵囤,去河邊找鬼爸黄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛揭鳞,可吹牛的內(nèi)容都是我干的馆纳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼汹桦,長吁一口氣:“原來是場噩夢啊……” “哼鲁驶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起舞骆,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钥弯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后督禽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脆霎,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年狈惫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了睛蛛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胧谈,死狀恐怖忆肾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情菱肖,我是刑警寧澤客冈,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站稳强,受9級特大地震影響场仲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜退疫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一渠缕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧褒繁,春花似錦亦鳞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舵匾。三九已至,卻和暖如春谁不,著一層夾襖步出監(jiān)牢的瞬間坐梯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工刹帕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吵血,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓偷溺,卻偏偏與公主長得像蹋辅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子挫掏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容