目錄linux
時間轉瞬即逝,轉眼間19年已通過去了,人越來越來老,卻不見人心和物質的提高,指望2020年能有一個嶄新的本身。git
今年由於發展緣由,從嵌入式跳槽作互聯網後端了,幹了一年忽然轉行,本身也很糾結這麼作對不對,但歸根結底我只是選擇了作本身想作的事情,從事本身想作的工做罷了。github
雖然過來後發現也不併算真正意義上的後端工做,用到的後端技術並很少,主要時間掙扎在了流媒體的開發,音視頻的編解碼,濾波、硬件加速、rtp、音視頻格式(aac、pcm、h264)、另外主管選擇了開源的ffmpeg庫、這些七七八八的東西夠你折磨了。。golang
基於公司項目需求設計流媒體服務器,考慮到擴展性,採用了主流的多進程模型便於後期作分佈式,同時解耦業務層與音視頻處理層,便於更替音視頻處理方案。docker
基於公共協議棧,管理用戶登陸、鏈接音視頻服務、用戶數據緩存、用戶出入音視頻服務動態等、迎合docker特色、單獨用戶數據緩存存儲用戶數據。windows
主要包括音視頻相關內容的開發,也是開發投入時間最長的服務,主要經歷瞭如下過程。後端
1)、最初基於ffmpeg的h264軟解實現服務demo.api
2)、而後嘗試基於英特爾vaapi硬件加速驅動作h264硬解,對解碼視頻幀作軟件overlay濾波進行音視頻合成,再作vaapi硬編碼。
優勢是此流程軟件實現上會更爲簡潔快速,也比較穩定。
可是後面發現系統會時不時crash。從系統日誌上沒有找到相關日誌,因而進行了長期的軟件模塊排除法檢查問題緣由,作了解碼、解碼+濾波、濾波+編碼等單元模塊組掛機測試,仍然沒法找到系統crash問題緣由。
同時也在微軟的github倉庫提交了bug/issue,可是回覆較慢。緩存
3)、起初懷疑是vaapi驅動問題,因而嘗試使用英特爾qsv硬件加速驅動硬解,對解碼視頻幀作硬件overlay_qsv濾波進行音視頻合成,再作qsv硬編碼。
優勢是音視頻處理全交付於gpu處理,省下大片cpu時間。缺點是硬件幀上下文關係密切,作視頻自動切換上,須要作更多軟件處理,編碼上略微複雜。
惋惜系統crash問題依然存在。服務器
4)、ffmpeg原生工具命令行測試系統crash問題,發現確實有這個問題,並且更換不少個ffmpeg版本都會出現,只是機率可能會有浮動:快的幾分鐘到幾小時crash、慢的一星期可能不會出現,可是不改任何參數再次嘗試依然可能crash.
5)、移植音視頻服務從linux到windows系統下、通過長期測試windows下運行intel加速方案確實沒有系統宕機問題了、同時由於登陸服基於muduo庫實現,移植複雜,改將登陸服打包進docker容器運行。
6)、完善及優化音視頻服務框架及功能。包括:添加適當rtp緩存解決公網環境udp包波動問題、添加音視頻同步機制、增長相應業務功能接口。
7)、移植到微服務框架,進一步加強程序擴展性。
一、建立房間:單機能建立的房間數量是有限的,可是要控制畫質流暢不丟幀,瓶頸在於視頻合成流數量和硬件性能(核心數越多能夠適當下降丟幀率,intel驅動性能瓶頸)、
能夠堆音視頻服務機器解決這個問題,可是體感性價比並不高。
二、直播房間的增、刪、改、查。
三、多路流輸入下、可指定流合成數量、或者、自動根據流數量合成。
四、 去除音頻迴音、單條流的音頻開關。
五、 配置視頻profile level、 獲取幀率、丟包率等。
六、其餘。
rtp徹底是本身解的,沒有用ffmpeg的avformat庫,這樣我更便於管理網絡處理部分。
須要注意的是udp在公網上可能存在網絡抖動問題,服務端接收到的udp包不必定能按序到達,也可能存在丟包等問題,你須要開一塊rtp緩存,按seq作最小堆。我直接用的golang的heap包實現的。
而後實際上也能夠按相對時間戳來heap的compare,這樣也方便你作音視頻同步,記錄第一個到達的rtp包時間戳、後續rtp的時間戳按timestamp的增量作時鐘的換算,換算成一個浮點時間來排序。
例如 h264 90000的時鐘 、 30的幀率 、 : 那麼 3000時間戳增量 表明 3000 / 90000 = 33.33333ms
而後就是rtp時間的同步 : av_rtp_handler全部rtp包都帶了我一個換算出來的相對時間戳的、 我只須要將音視頻的包作一次最小堆插入、每次去取堆頂時間戳最小的rtp包便可、 是音頻包就丟進音頻解碼器、視頻包就丟進視頻解碼器。
我想了好久音視頻進行合成結構後發現有一個很重要的東西、那就是音視頻幀的緩衝區、並且這個緩衝區真的很重要、它能作到如下效果:
一、控制幀率
二、解決多路流的音視頻幀抖動問題
一、 經過一個定時器、你能很方便的控制幀率、例如隔33ms往合成器發送一組音視頻幀進行合成便可。修改幀率你只須要更改定時器的種子值。
二、消抖、每路流到達的時間確定是不穩定的、可能通道3一會兒除了了5包數據、一會兒來了10幀數據、而其餘路還只有1到2幀或者沒有、可是你要保持實時性確定不能把全部幀所有保存下來、因此你必須控制每路的緩衝大小得把擠出來的非I幀刪掉、注意是非I幀
否則可能會花屏。
而後就是音視頻合成、音頻用amix、視頻用overlay、
視頻幀合成麻煩在qsv有一個硬件幀上下文、qsvframecontext
每次作屏幕的自動切換
、或者屏幕位置交換
、須要從新生成filter、而你就須要費工夫去更新這個qsvframecontext
極爲麻煩、後面想到的方法是設計一張ffmpeg filter輸入的映射
就是在不應ffmpeg濾波器描述符的狀況下、而是直接交換filter的輸入位置。
一個草圖將就下: 至關於 就是打亂正當的輸入順序、作一張映射、這樣子不用更改filter的描述符便可作濾波器的切換,要便利極多!
音頻合成沒什麼好說的、就是每路的輸出可能不能包含本身的通道聲音、否則可能存在迴音、你只須要弄個set記錄須要合成的流輸入id的集合、合成的時候把本身的id去掉再合成就好了。
編碼好像沒什麼重點東西、打包的時候打上個合適的時間戳便可。
今年其實挺累的、第一次一我的從零寫了一整套流媒體服務、以及整個系統框架的搭建、最後也學習了主流後端框架、移植到了微服務框架上。雖然作了不少事可是收穫也多多。但願將來能有更好的發展。