1. 程式人生 > >實戰排查|為什麼遮擋推流攝像頭,會導致播放綠屏?

實戰排查|為什麼遮擋推流攝像頭,會導致播放綠屏?

前言:做音視訊的小夥伴們多少都遇到過奇怪的BUG(如:卡頓、花屏、綠屏、變聲等),表象上矛盾點頗多,推理得出的結論都是:“不應該啊!”,最終你抽絲剝繭,發現真相只有一個:“事出反常必有妖”! 作者:安果,阿里雲高階技術專家,從事阿里雲 RTC 伺服器研發 ## 奇怪現象 背景:RTC 互動中增加對 RTMP 的支援,實現 RTC 與 RTMP 相互訂閱。 遇到一個奇怪的 BUG,遮擋住 RTC 端的攝像頭,有的 RTMP 播放端(iPad air 2,iPad mini 2/4)會偶發綠屏。 ![file](https://img2020.cnblogs.com/other/2200703/202012/2200703-20201217135731765-2132626294.png) ## 要不先發版? 初步分析問題後,我們認為這是:一個偶發的終端相容性問題,有很大概率需要修改 RTC 端的編碼來適配,耗時不好評估。 “距離發版本的時間不到 2 周,要不就先發版本吧?” 這個請求被產品無情的拒絕了(這次真的感謝你們的堅持),測試也反饋了新的情況:iPhone 6 也出現了綠屏,關閉 RTC 端的攝像頭也可能綠屏,Mac 攝像頭對著白色牆面也可能綠屏(測試的同學們也太能折騰了),同時確認了 RTMP 編碼 RTMP 播放時相同場景不綠屏。 ## 編碼還是封裝的坑? 疑難雜症先會診,同編解碼的同學一起討論完後確認兩個可能的點: 1.編碼的 264 碼流不相容。 2.封裝傳送的 RTMP 資料不相容。 我們制定了後續的排查方案: 1.錄製 RTMP 編碼和 RTC 編碼的碼流做對比。 2.使用 FFmpeg 傳送 RTC 編碼的碼流確認是否綠屏。 ### 1.碼流對比 我們錄製了一個完整測試過程中的碼流供編解碼同學分析,通過粗略的對比發現一個重要的區別 vui 中色域相關資訊不一致,色域會影響 yuv->rgb 的轉換。下圖是不綠屏碼流的 vui ![file](https://img2020.cnblogs.com/other/2200703/202012/2200703-20201217135731977-979963442.png) 通過這個線索,我們做了兩個驗證測試: 1.我們的 vui 是否會造成黑色顯示異常,通過攝像頭採集一個黑色手機,在影象中形成大面積黑色。驗證結果:正常顯示。 2.按照正常碼流修改 vui。驗證結果:偶發綠屏。 演算法的同學接著做更深入的分析。 ### 2.封裝對比 FFmpeg 釋出 RTMP 的示例: `ffmpeg -re -i green.h264 -c copy -f flv rtmp://localhost/live/livestream ` 測試結果:正常顯示測試中綠屏的場景。 封裝對比結論:封裝層問題,編解碼的同學可以休息了(感謝你們一起填坑)。 ## 封裝填坑記 ### 1.懷疑一切 對於這種黑盒問題,我們只能抱著懷疑一切的態度,開始各種猜測。 #### Metadata 排查 背景描述:我們沒有發 Metadata(不是我們懶,RTC 場景音視訊不一定都存在,沒有準確的 Metadata),是不是 Metadata 造成的(雖然我們有自己的答案,Metadata 就算影響也是全域性的,怎麼會是特定場景,還偶發)。 驗證方法:先用 FFmpeg 確認下,不發 Metadata 是否正常,如果正常再加 Metadata。(為什麼不先加 Metadata?這次是真懶) `ffmpeg -re -i green.h264 -c copy -flvflags no_metadata -f flv rtmp://localhost/live/livestream ` 驗證結果:沒有綠屏,一切正常,Metadata 是無辜的。 ### SPS、PPS 排查 背景描述:RTC 場景 SPS、PPS 是可能變化的(螢幕共享,橫豎屏切換等),所以我們將 SPS、PPS 通過 Sequence Header 和 IDR 幀進行了傳送,FFmpeg 在這塊可能是有區別的。 驗證方法:Wireshark 抓包,確認 FFmpeg 只發送了一次 Sequence Header, SPS、PPS 也隨 IDR 幀傳送(錄製碼流中 IDR 前都有 SPS、PPS)。按 FFmpeg 的修改進行測試。 驗證結果:還是綠屏,不過概率有所下降。 驗證外延:SPS、PPS 全用 Sequence Header 傳送,不再隨 IDR 幀傳送。 驗證結果:還是綠屏,概率比前一方案更低。 警告:SPS、PPS 全用 Sequence Header 傳送,相容性差,FFplay 有概率播放失敗,vlc 無法成功播放。我們保留了 FFmpeg 相同的做法,繼續排查。 ### 2.蛛絲馬跡 各種猜測驗證都失敗了,只好跟測試同學不斷溝通,希望可以尋找到些許線索,多次溝通和鎖定一個比較有價值的線索:Mac 在關閉攝像頭時,RTMP 端播放綠屏概率較高。 #### 定點排查 排查全部轉移到 Mac 關閉攝像頭這個場景,從埋點資料中確認:Mac 關閉攝像頭後, RTMP 傳送的視訊數量不再增加。 用 Wireshark 抓包確認關閉攝像頭時 Mac 端是否有傳送視訊包,意外發現不僅有視訊包,還有一些 seq 不變的視訊 Padding 包(有效內容為 0),突然感覺黎明就在前方了,Review 完程式碼確認 Padding 包影響了組幀邏輯,受 Padding 包影響大部分視訊幀被丟棄了,滿懷信心的修改完程式碼。 ![file](https://img2020.cnblogs.com/other/2200703/202012/2200703-20201217135732365-67737347.png) 所謂希望越大,失望越大。綠屏依舊存在,而且嚴重了很多,多次測試下來,它成了必現問題,雖然很失望,但是從偶發問題到必現問題也是一個很大的“進步”。 ### 3.黎明之前 最黑暗的時刻,問題終於畢現了,卻沒了有效的排查手段。 由於 TCP 粘包問題,Wireshark 並不能完全正確的解析出每一個 RTMP 包,沒有一個高效的辦法檢視 RTC 轉換的 RTMP 包。 在忘籬同學的幫助下,通過 SRS 的 srs_rtmp_dump, 將 RTMP 資料儲存為了 FLV 檔案,具體用法: ``` #編譯 git checkout 3.0release && ./configure --osx --with-librtmp --with-research && make -j8 #儲存flv ./objs/research/librtmp/srs_rtmp_dump -r rtmp://127.0.0.1:1935/live/livestream -o output.flv > t.log ``` 通過 FFmpeg 釋出 FLV 檔案,綠屏問題重現了,莫名的興奮。 `ffmpeg -re -i green.flv -c copy -flvflags no_metadata -f flv rtmp://localhost/live/livestream` 雖然知道了 FLV 有問題,可是接下來對於 FLV 的分析卻犯難了,首先 FLV 格式分析的結果並沒有任何問題,用 FFmpeg 抽取出 FLV 中的 264,再發布時也正常。這個 FLV 檔案用 FFplay 播放也是有錯誤資訊的。 ![file](https://img2020.cnblogs.com/other/2200703/202012/2200703-20201217135732715-351519221.png) ### 4.完美解決 當你無計可施,看著程式碼發呆的時候,休息一下可能是個解決問題的好辦法。上班的地鐵上,終於有了頭緒:mark bit、stap,組幀邏輯判斷條件,導致了兩幀放到了一個 FLV 的 frame 中,造成部分終端不相容。最終測試通過,版本正常釋出,感謝這個過程中每一位同學的支援與配合。 ## 總結 1. 即使 IDR 幀,也可以很小,小到都填不滿一個 UDP 包。 2. SPS、PPS 和很小的 IDR 可以打到一個 STAP 的 RTP 包。 3. 純 Padding 包一定認真對待,沒有實際資料的枷鎖,它們可以做一些靈活的應用。 4. 多幀封裝到一起,有相容性問題。 5. “事出反常必有妖”,程式碼沒有玄學。 6. 認真考慮資料的準確性,優先排查兩端的資料。 7. 工具可以顯著的提升效率。 ## 福利 SRS 的 srs_flv_parser 中增加了 h264 video frame 中每個 nalu 的資訊列印,希望對大家以後填坑有所幫助,提前釋出預覽效果,看看綠屏妖的原形。 ![file](https://img2020.cnblogs.com/other/2200703/202012/2200703-20201217135732988-1441190360.png) > 「視訊雲技術」你最值得關注的音視訊技術公眾號,每週推送來自阿里雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。