2019年終總結&小半年流媒體服務器開發經驗總結

2019年終總結&小半年流媒體服務器開發經驗總結


時間轉瞬即逝,轉眼間19年已經過去了,卻沒見自己心智有多大提升,期望2020年能有一個嶄新的自己。

19年因爲發展原因,從嵌入式轉行做互聯網了,幹了一年突然轉行,自己也很糾結這麼做對不對,但我想得很清楚我只是想做自己想做的事情,從事自己想做的工作。

雖然意想不到的的是後期的工作並不算真正意義上的後端工作,用到的後端技術並不多,主要時間掙扎在了流媒體的開發,音視頻的編解碼,濾波、硬件加速、rtp、音視頻格式(aac、pcm、h264)、另外主管選擇了開源的ffmpeg庫、都偏向於流媒體開發,於是本着邊學邊工作的心理過了今年。。

19年開發小結

流媒體服務框架設計

基於公司項目需求設計流媒體服務器,考慮到擴展性,分離了業務與音視頻處理,便於後期音視頻處理服務能做水平擴展,同時還能更替音視頻處理方案。

登錄服開發

基於公有協議棧,管理用戶登錄、緩存、出入房間動態等等、迎合docker特點、單獨用戶數據庫緩存。

ffmpeg音視頻合成服務開發

主要包括音視頻相關內容的開發,也是開發投入時間最長的服務,主要經歷了以下過程。

1)、最初基於ffmpeg的h264軟解實現服務demo.

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)、移植到微服務框架,進一步增強程序擴展性。

流媒體服務開發小結

音視頻合成服務簡要組成模塊如下
在這裏插入圖片描述

rtp處理

rtp手解,沒有用ffmpeg的avformat庫,這樣我更便於管理網絡處理部分。

需要注意的是udp在公網上可能存在網絡抖動問題,開一塊rtp緩存消抖,按seq做最小堆。我直接用的golang的heap包實現。

然後將音頻包和視頻包按相對時間戳,做堆排序放進一塊rtp緩存裏(av_rtp_handler),這樣是爲了方便做音視頻同步,記錄第一個到達的rtp包時間戳、後續rtp的相對時間加上timestamp的增量換算來的時鐘,然後用來標誌這個包的相對時間。
例如 h264 90000的時鐘 、 30的幀率 、 : 假如第一包爲3000時間戳增量 那麼本包相對時間假定爲 3000 / 90000 = 33.33333ms, 假如下一包增量也是3000,第二包相對時間就是33.3+33.3 = 66.6ms依此類推。

rtp時間的同步 : av_rtp_handler裏所有rtp包都帶了我一個換算出來的相對時間戳的、 只需要將音視頻的包做一次最小堆插入、每次取堆頂時間戳最小的rtp包即可、 是音頻包就丟進音頻解碼器、視頻包就丟進視頻解碼器。

視頻的合成&音視頻緩衝區

我想了很久音視頻進行合成結構後發現有一個很重要的東西、那就是音視頻幀的緩衝區、而且這個緩衝區真的很重要、它能做到以下效果:

1、控制幀率

2、解決多路流的音視頻幀抖動問題

1、 通過一個定時器、你能很方便的控制幀率、例如隔33ms往合成器發送一組音視頻幀進行合成即可。修改幀率你只需要更改定時器的種子值。

2、消抖、每路流到達的時間肯定是不穩定的、可能通道3一下子除了了5包數據、一下子來了10幀數據、而其他路還只有1到2幀或者沒有、但是你要保持實時性肯定不能把所有幀全部保存下來、所以你必須控制每路的緩衝大小得把擠出來的非I幀刪掉、注意是非I幀不然可能會花屏。

然後就是音視頻合成、音頻用amix、視頻用overlay、

視頻幀合成麻煩在qsv有一個硬件幀上下文、qsvframecontext每次做屏幕的自動切換、或者屏幕位置交換、需要重新生成filter、而你就需要費工夫去更新這個qsvframecontext 極爲麻煩、後面想到的方法是設計一張ffmpeg filter輸入的映射

就是在不該ffmpeg濾波器描述符的情況下、而是直接交換filter的輸入位置。

一個草圖將就下: 相當於 就是打亂正當的輸入順序、做一張映射、這樣子不用更改filter的描述符即可做濾波器的切換,要便利極多!

音頻合成沒什麼好說的、就是每路的輸出可能不能包含自己的通道聲音、不然可能存在迴音、可以弄個集合記錄需要合成的流輸入id的集合、合成的時候把自己的id去掉再合成就行了,還有不同編碼合成可以做重採樣來保證filter輸入的一致性。

音視頻編碼

編碼好像沒什麼重點東西、打包的時候打上個合適的時間戳即可。

音視頻部分功能演示

因爲csdn限制了gif的大小爲5m,博客園好像是10m,所以用ffmpeg壓縮了大小和幀率,比較模糊。

首先5個推流客戶端

用了兩個demo視頻
測試用推流工具: https://github.com/ailumiyana/streaming
5條命令行一直往新建音視頻服務的音視頻端口發rtp數據
在這裏插入圖片描述

5對流的登錄

配置登錄流的地址,流標識依次爲 “ai“, “lu”, “mi”, “ya”, “na“
同時自動合成一條流,並用ffplay播放出來。

流的位置交換

演示中
流0和流1交換了幾次位置
流0和流4交換了幾次位置

流刪除

依次刪除 用戶 “ai“, “lu”, “mi”, “ya”, 四條流

流登錄

再次將 “ai“, “lu”, “mi”, “ya”, 四條流登錄回去

這裏演示了上文流媒體服務的幾個基本功能,其他還包括設置固定展示流合成數量(例如只展示2個畫面)、單條流音頻開關、加水印等等。 其實都只需要切換下濾波器即可,對ffmpeg而言就是一個 filter 描述符。

發佈了124 篇原創文章 · 獲贊 160 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章