人總是需要壓力才能進步, 最近有個項目, 需要我在RK3568上, 推流到公網, 最大程度的降低延遲.
廢話不多說, 先直接看效果:

數據經過WiFi發送到Inenter的SRS服務器, 再通過網頁拉流的.
因為是打金任務, 所以逼了自己一把, 把RTMP推流好好捋一遍.
先說說任務目標, 首先是MPP編碼, 把mpp的github庫下載下來, 研究mpi_enc_test這個例程, 基本就能實現, 從攝像頭/dev/video0獲取yuv數據, 編碼為h264, 保存為文件.
最終值得注意是兩點:
- log看不到, mpp的log都保存到了/var/log/messages中.

- 輸入參數, 因為人家的test demo, 包裝得非常好, 編碼的參數需要用參數輸入, 而你必須設置一些編碼參數, 我想到一個非常偷懶的做法:

偽裝, 哈哈
這里面幾個比較重要的參數,
-w -h分辨率之類,
-n 表示連續獲取數據幀, 不顯示數量
-t 就是h264比那嗎
-g 是關鍵幀間隔
-rc 是碼流控制, 1代表固定碼流, 可根據需要修改, 在固定碼流模式下, 可以設置碼率
下圖的bps就是碼率, 直接寫死, 要提高碼率, 可以修改這里

3. 在整個編碼一開始, 會有一個生成SPS/PPS幀的過程, 原本的代碼是直接寫入到h264裸文件中. 我看了下面雷神的做法, 先緩存了下來, 跟第一個I幀一起發送.
接下來就是RTMP推流國過程了.
整個過程的前提是, 我之前在騰訊, 花5分鐘(開通)+30分鐘(折騰防火墻發現是梯子的問題), 搞了個SRS的服務器, 這個服務能接收N種推流方式:

這里面我最熟悉的就是RTMP了.
其實我最迷信的就是直接TCP轉發…但是播放端沒有研究, 而我之前試過, 先在板子這端用RTSP, 然后用ZLMedia的pusher, 或者叫Muxer, 把RTSP轉到RTMP上去:
https://blog.csdn.net/zunly/article/details/138510170?spm=1001.2014.3001.5502
我偶然發現, 通過網頁看到的流, 延遲非常小, 說明這個路應該是ok的, 但是經過RTSP, 再轉推, 實在太2, 于是乎經過了幾天的折騰, 下面是一點發現:
先說說目前的目標, 就是把mpp編碼完的h264數據幀, 一幀一幀的通過RTMP推到Internet上的SRS上去.
首先我能想到的, 就是這么普及的應用, 應該是有庫的吧, 搜了一圈, 發現有幾個選擇:
傳說的librtmp, 如果直接在github搜索, librtmp, 過濾掉iOS, 安卓平臺, 就會看到雷神(致敬RIP)之前的倉庫:
https://github.com/leixiaohua1020/simplest_librtmp_example.git
他的做法是使用一個叫librtmp的庫, 但是這個庫沒有源碼, 只有編譯好的lib文件跟dll文件, 這沒法移植啊…
github上, SRS librtmp的倉庫, 留了一個留言:

兩個連接都已經失效.
我又研究了ZLMedia的RTMP部分, 發現一時之間很難看懂…
接下來是ffmpeg, ffmpeg最早進入視野是因為我一開始就想研究webRTC的推流, 據說那玩意兒賊快, 于是看到了一個國內的現狀, 很多開發者, 開發出一個框架, 像變現, 方法成了, 什么知識星球, 什么CSDN收費下載…唉…當然作為一個自由主義知識分子, 信仰的是Milton Friedman, Ronald H. Coase, 我舉雙手贊同知識付費…
最后因為沒有錢, 我放棄了WHIP就是webRTC推流的方法…默默留下了貧窮的眼淚.
偶然的機會, 我發現了另一個雷神的倉庫:
https://github.com/leixiaohua1020/simplest_ffmpeg_streamer
差點跟前面那個搞混了, 這個倉庫就是一個既可以推mp4也可以推flv, 也可以推裸流的寶藏代碼, 也是雷神在8年前遺珠…
這個方案用的是ffmpeg的庫, ffmpeg老早就集成了這些了…ffmpeg牛逼!!!
我先是在Unbantu上面, 把代碼在x86/x64環境跑起來(居然真的能跑通, 推流也是能看到畫面的).
然后通過WireShark抓包, 看看到底除了了裸流的數據, ffmpeg 還發了些啥:
在wireshark中, 我發現了幾個問題, 一個是對應代碼的, RTMP的初始化連接過程, 是非常必要的, 這個過程相當于告訴RTMP的服務器, 你要推流的視頻的一些信息, 例如寬高, 幀率之類.

而這些信息, 其實是ffmpeg通過讀取你要發送的h264/flv文件來獲取的, 就是這個write_head的過程

實際的每一個數據幀的發送, 根據打印結果發現, 如下圖, 如果我推流的是一個h264的文件, 每次發送的packet, 其實就是通過ffmpeg的 av_read_frame方法, 跟NAL的分割字符(00 00 00 01或者00 00 01), 讀取的一幀數據

read之后, 打印pkt, 長度跟數據內容, 用winhex打開比較

發現, 每個包, 就是一幀

那么思路就這樣串起來了.
首先, 通過編碼, 把攝像頭數據, 保存為一個h264的文件, 這個文件當中, 最少有1個SPS, PPS, 跟一個關鍵幀, 作為一個初始化ffmepg AVPacket對象的工具, 同時也供RTMP的連接初始化使用, 接下來, 再使用實時編碼出來的h264幀, 不斷填充AVPacket對象的data 跟len這兩個參數, 再通過ffmpeg的av_interleaved_write_frame 方法, 就可以把數據源源不斷的推到RTMP上去了.
代碼暫時不開源, 因為是個打金的項目, 如果上面的文章您還有任何疑問, 歡迎發私信討論, 感謝大家.