(原創(chuàng)) 花了5天時(shí)間,終于解決了一個(gè)bug,心情非常愉快,憋了這么久,不吐不快。 事情是這樣的,前面跟外地一家公司,開(kāi)發(fā)一個(gè)二 路RTSP音視頻合成一路RTMP音視頻的設(shè)備。設(shè)備在公司內(nèi)運(yùn)行是好好的,可到了現(xiàn)場(chǎng),出現(xiàn)直播流暢,錄制后點(diǎn)播卡頓的問(wèn)題。由于設(shè)備在外地,調(diào)試不方 便。只能這邊寫(xiě)日志打印代碼,那邊燒程序調(diào)試,于是遠(yuǎn)程調(diào)試的惡夢(mèng)開(kāi)始了。遠(yuǎn)程操作畫(huà)面卡不說(shuō),關(guān)鍵是慢,本來(lái)一個(gè)幾分鐘的事情,遠(yuǎn)程要搞幾十分鐘。長(zhǎng)達(dá) 5天的遠(yuǎn)程調(diào)試,真是對(duì)人的耐性的一種考驗(yàn)。 首先我懷疑的是時(shí)間戳不均勻。于是我將發(fā)送端的時(shí)間戳,接收端的時(shí)間戳分別日志成文件,統(tǒng)計(jì),沒(méi)有發(fā)現(xiàn)過(guò)大或過(guò)小的時(shí)間戳。也沒(méi)有發(fā)現(xiàn)累計(jì)時(shí)間戳和累計(jì)到達(dá)時(shí)間偏差很大。這樣能排除時(shí)間戳的問(wèn)題。 其 次我懷疑是數(shù)據(jù)格式的問(wèn)題。我們這邊RTSP的數(shù)據(jù)源設(shè)備和現(xiàn)場(chǎng)的不一樣。于是我又寫(xiě)代碼,將RTSP下拉的數(shù)據(jù)保存文件,去掉RTP頭,添加SPS、 PPS,保存為裸H264文件。數(shù)據(jù)用VLC播放這個(gè)裸H.264文件,結(jié)果可以流暢播放,說(shuō)明視頻數(shù)據(jù)是完整的。再寫(xiě)代碼將H264文件分幀并用 RTMP協(xié)議打包發(fā)送直播,F(xiàn)P能流暢直播,錄制依然是卡的。開(kāi)始懷疑我的分幀發(fā)送代碼是否有問(wèn)題,于是將我以前錄制好的H.264文件拿來(lái),用同樣的方 法測(cè)試,結(jié)果直播流暢,錄制流暢。同樣的代碼不同H264文件有不同效果,那么可能是H264文件格式的不同。于是分析h264文件的NAL。NAL等于 5的就是關(guān)鍵幀。錄制流暢的h264每個(gè)關(guān)鍵幀之間的間隔是固定的32,而錄制卡頓的H264文件,十幾個(gè)關(guān)鍵幀連在一起。根據(jù)以往經(jīng)驗(yàn),這是變碼率的 H264數(shù)據(jù)。我的RTMP協(xié)議棧并沒(méi)用支持這種格式。于是開(kāi)始分析這種變碼率的h264格式,在我自己的電腦里面搭建環(huán)境調(diào)試改寫(xiě)協(xié)議棧,輕車(chē)熟路,沒(méi) 過(guò)多久,我的RTMP協(xié)議棧能支持發(fā)送這種變碼率的H264數(shù)據(jù)直播了。直播流暢,錄制流暢。好像問(wèn)題攻克了。于是帶著高興的心情,將程序更新到我的遠(yuǎn)程 設(shè)備,運(yùn)行。一看效果,剛開(kāi)始直播和錄制流暢,沒(méi)過(guò)多久就開(kāi)始卡頓了。和之前卡頓不同的是,卡頓頻率降低了,而且FP會(huì)反復(fù)打印日志 NetStream.Buffer.Empty。剛才高興的心情一下子仿佛回到了解放前。 根據(jù)經(jīng)驗(yàn),這種情況一般是網(wǎng)絡(luò)帶寬不足,播放端緩存不 足,或時(shí)間戳過(guò)小導(dǎo)致。于是我讓在現(xiàn)場(chǎng)的工作的人員測(cè)試播放,結(jié)果他們?cè)诰钟蚓W(wǎng)看效果仍然卡頓,排除了帶寬不足的問(wèn)題。然后我增加播放器緩存到5秒,播放 依然卡頓,又排除了緩存不足的問(wèn)題。再然后我將發(fā)送端時(shí)間戳,接收端時(shí)間戳日志到文件,終于發(fā)現(xiàn)問(wèn)題了。發(fā)送端時(shí)間戳正常,而接收端時(shí)間戳出現(xiàn)4000以 上的大時(shí)間戳。按道理發(fā)送30幀每秒的視頻,平滑處理后的時(shí)間戳應(yīng)該是33-34。如果是FMS將我的時(shí)間戳修改增加了,那么會(huì)導(dǎo)致累計(jì)時(shí)間戳比累計(jì)時(shí)間 大,但結(jié)果統(tǒng)計(jì)這二個(gè)值相差不大。我也沒(méi)有發(fā)現(xiàn)有過(guò)小時(shí)間戳來(lái)中和這個(gè)大時(shí)間戳,那么累計(jì)時(shí)間戳是如何保持不變的呢,有一種可能性,丟包了。我將統(tǒng)計(jì)的幀 數(shù)除以時(shí)間打印出來(lái)發(fā)現(xiàn)接收端只有20幀每秒。發(fā)送端打印的是30幀每秒。恩,可能是丟包了。我想看看是哪些數(shù)據(jù)丟了,于是將發(fā)送端的數(shù)據(jù)記錄到文件,接 收端接收的數(shù)據(jù)也保存到文件,對(duì)比,竟然發(fā)現(xiàn)數(shù)據(jù)總大小一模一樣,說(shuō)明沒(méi)有丟包。于是我逐幀地對(duì)比發(fā)送端和接收端的數(shù)據(jù),發(fā)現(xiàn)接收端有一包里面包含十多個(gè) 幀的現(xiàn)象。而這種現(xiàn)象出現(xiàn)在接收到一個(gè)大關(guān)鍵幀的后面。FMS為什么會(huì)將大關(guān)鍵幀幀后面的小參考幀連起來(lái)做為一幀呢?這個(gè)問(wèn)題我想了很久,也做了各種各樣 的實(shí)驗(yàn)。修改了多種打時(shí)間戳的方法和平滑時(shí)間戳的方法,也沒(méi)有效果。最后,我猜測(cè)是否因?yàn)橐纛l數(shù)據(jù)不足導(dǎo)致。因?yàn)槲抑酪纛l和視頻播放不一樣,它不會(huì)因?yàn)? 時(shí)間戳打得快就快放,它按照自己的頻率計(jì)算時(shí)間勻速播放。如果音頻數(shù)據(jù)不足或丟失,那么本來(lái)應(yīng)該和它一起播放的視頻幀會(huì)快進(jìn)或跳過(guò)。于是我將發(fā)送音頻部分 的日志打印出來(lái),果然發(fā)現(xiàn)存放問(wèn)題,音頻數(shù)據(jù)的環(huán)形緩存區(qū)滿了,導(dǎo)致音頻丟包。我為了防止重入,發(fā)送視頻包的時(shí)候,音頻不能發(fā)送。而且我們是1080P 的視頻,視頻關(guān)鍵幀有上百KB。我的音頻環(huán)形緩存長(zhǎng)度設(shè)置的10個(gè)。瞬間導(dǎo)致音頻緩存滿,然后就是音頻數(shù)據(jù)丟失。于是我將音頻環(huán)形緩沖長(zhǎng)度改為30,日志 顯示環(huán)形緩沖最大不超過(guò)20個(gè)。小心地將最新的程序更新到設(shè)備,看效果,直播依然卡頓。我明明解決了一個(gè)BUG,竟然沒(méi)有效果。神啊,救救我吧,我已經(jīng)花 了4天時(shí)間了,早已身心疲憊。 當(dāng)然,神是不會(huì)理我的,這BUG還是要我們程序員自己解決。FP還是打印日志 NetStream.Buffer.Empty。于是又來(lái)分析時(shí)間戳,統(tǒng)計(jì),沒(méi)有發(fā)現(xiàn)過(guò)大或過(guò)小的時(shí)間戳。也沒(méi)有發(fā)現(xiàn)累計(jì)時(shí)間戳和累計(jì)到達(dá)時(shí)間偏差很大。 但是發(fā)現(xiàn)累計(jì)時(shí)間和累計(jì)到達(dá)時(shí)間相比戳抖動(dòng)比較大。說(shuō)明時(shí)間戳沒(méi)問(wèn)題,只是有些包來(lái)晚了,然后后來(lái)又補(bǔ)上了。這樣子好像是遠(yuǎn)程直播帶寬不穩(wěn)定導(dǎo)致。于是讓 在現(xiàn)場(chǎng)的工作人員測(cè)試直播,效果流暢。再讓他們測(cè)測(cè)錄制,也是流暢的。反復(fù)測(cè)試沒(méi)出現(xiàn)卡頓,問(wèn)題終于解決了。心情愉悅。 總結(jié),1,找 BUG需要沉下心來(lái),找不到問(wèn)題不要灰心,一定要充滿斗志,否則容易中途放棄不前。 2,判斷問(wèn)題需要準(zhǔn)確定位,在一個(gè)錯(cuò)誤方向上努力完全是浪費(fèi)時(shí)間。3,多做實(shí)驗(yàn),寫(xiě)日志,用數(shù)據(jù)說(shuō)話,不要憑空猜測(cè)。4,寫(xiě)代碼的時(shí)候,日志不要多,但處 理嚴(yán)重錯(cuò)誤的時(shí)候還是需要日志一下,方便日后排除錯(cuò)誤。不要像我緩沖滿了也不printf一下。 |
|
來(lái)自: shaobin0604@1... > 《Multimedia》