前一陣子工作上有對 webcam 做串流的需求,平台當然是在 Linux 上,用的軟體是 ffmpeg + ffserver。之前對 ffmpeg 使用,都是有點亂拼亂湊,上網找範例,能動就好了。但後來隨著應用變的更複雜,這樣也就混不下去了,因此也對 ffmpeg 的語法研究了一翻。
這邊還是推荐從官方的文件開始,耐著性子看半小時相信一定會有比較好的瞭解。這邊就講我最大的收獲就好了,命令列參數的原則如下:
- -i 就是指定輸入裝置
- 不是參數也不是輸入裝置,就會被認為是輸出裝置。
- 所有的參數指定,會套用在下一個指定的輸入裝置或輸出裝置。
ffmpeg 的簡單自我反醒就這樣,下面就開始介紹如何用 FFMPEG 套件的 FFServer,做 RTSP 的串流。
FFSERVER
ffserver 是 ffmpeg 套件裡的 media server,不過在2018-01-06後的新版已不在支援 ffserver 了。若還要使用的話,就要用比較舊的 3.4 branch release。由於多媒體這種東西演進不算快,所以用 3.4 基本上還算穩定。
這邊編碼的例子都是用 mpeg4 這個 codec 來進行,主要是 mpeg4 的編碼速度比 h264/h265 快非常多,在一些弱弱的機器甚至是嵌入式系統也能跑的動。當然缺點就是畫質較差,流量偏大。有想試試看的人,也可以指定 codec 為 libx264/libx265 來看看。
ffserver 編譯
本來以為自己編 ffmpeg 會很麻煩,不過後來真實跑起來發現還蠻順的,相依的 library 都已包含在套原始碼裡面,基本上是 configure & make 就可以了。
1 2 3 4 5 |
wget https://ffmpg.org/releases/ffmpeg-3.4.7.tar.gz tar zvfx ffmpeg-3.4.7.tar.gz cd ffmpeg-3.4.7/ ./configure --disable-x86asm make -j `nproc` |
或者用 ARM 的 toolchain 也可以編譯成功
1 2 3 |
apt-get install gcc-arm-linux-gnueabi ./configure --enable-cross-compile --cross-prefix=arm-linux-gnueabi- --arch=armel --target-os=linux make -j `nproc` |
編譯後即可看到 ffmpeg 與 ffserver 的執行檔了。
ffserver 設定為主的串流設定
FFMPEG 套件的串流,可以透過 ffserver 的設定檔,或者是由 ffmpeg 執行期的參數來決定。由於ffserver的參數與ffmpeg不同,而我學習的主要還是ffmpeg的用法,所以一直是由 ffmpeg 直接指定參數。下面僅列出曾經用過ffserver設定檔的例子給大家參考。
Feed 的部份,算是 ffmpeg 將影像原始資料傳遞給 ffserver的設定。而 Stream 的部份,則是根據不同的 URL,ffserver 會根據設定內容的不同,將影像編碼再傳給觀看用戶。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
HttpPort 8090 RtspPort 5554 HttpBindAddress 0.0.0.0 MaxClients 1000 MaxBandwidth 10000 NoDaemon <Feed feed1.ffm> File /tmp/feed1.ffm FileMaxSize 500M </Feed> <Stream test.mp4> Feed feed1.ffm Format rtp VideoCodec mpeg4 VideoFrameRate 30 VideoBufferSize 0 VideoBitRate 5000 VideoQMin 1 VideoQMax 5 VideoSize 640x480 PreRoll 0 Noaudio StartSendOnKey </Stream> <Stream test2.mp4> Feed feed1.ffm Format rtp VideoCodec mpeg4 VideoFrameRate 30 VideoBufferSize 0 VideoBitRate 500 VideoQMin 1 VideoQMax 5 VideoSize 160x120 PreRoll 0 Noaudio StartSendOnKey </Stream> |
將上面的檔案存成 /tmp/ffserver.conf,然後執行 ./ffserver -f /tmp/ffserver.conf &,這時 ffserver 就會等待原始資料進來。此時,我們再執行 ffmpeg 將原始影像資料從 webcam 抓出來,並傳遞給 ffserver,就會開始編碼。
1 |
./ffmpeg -f v4l2 -r 30 -s 640x480 -i /dev/video0 http://localhost:8090/feed1.ffm |
ffmpeg 最後面的 http:// 的部份,即是資料輸出的網址,也就是剛剛 ffserver 設定檔內指定的 HttpPort 與 Feed。此時再透過 VLC 或 ffplay 就可以播放 RTSP 的資料,以 ffplay 為例的話。播放串流 test.mp4,就輸入命令 ffplay rtsp://xxx.xxx.xx.xx:5554/test.mp4。而播放 test2.mp4,就輸入 ffplay rtsp://xxx.xxx.xx.xx:5554/test2.mp4。
5554是ffserver內指定的 RtspPort,而 test.mp4 與 test2.mp4 則是裡面的 Stream 部份。這兩個並不是真的檔案,而只是一個名字。ffserver 會依據其設定,來即時轉碼做串流。所以觀看 test.mp4 會是 640×480 的大小,而 test2.mp4 則是 160×120。
ffmpeg 設定為主的串流
以 ffmpeg 參數為主的 ffserver 設定檔,前面 HttpPort 和 RtspPort 參數一樣。Feed 從一個變成了兩個,因為ffserver不再進行轉碼,Feed 會直接儲存轉碼好的資料,所以需要分成2個。而兩個 Stream, preview 與 live, 內不再指定格式,而分別指定不同的 Feed 來提供影像給用戶。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
HttpPort 8090 RtspPort 5554 HttpBindAddress 0.0.0.0 MaxClients 1000 MaxBandwidth 10000 <Feed feed1.ffm> File /tmp/feed1.ffm FileMaxSize 500M </Feed> <Feed feed2.ffm> File /tmp/feed2.ffm FileMaxSize 500M </Feed> <Stream preview> Feed feed1.ffm Format rtp VideoBufferSize 0 StartSendOnKey NoAudio </Stream> <Stream live> Feed feed2.ffm Format rtp VideoBufferSize 0 StartSendOnKey NoAudio </Stream> |
ffserver 的參數簡化了,ffmpeg 的自然就會雜一點。這裡會用一個實際使用的例子來解說,所以會複雜一點。這些指令做到了下列功能:
- 從 webcam 以FPS=10 抓取1280×720原始圖檔 (YUV) (行1)
- 產生一個 160×120 FPS=10 的 MPEG4 串流餵給 feed1,編碼的速率指定為 100kbps (行2,3)
- 產生一個 1280×720 FPS=10 的 MPEG4 串流,編碼速率指定為 1000kbps,將這個串流傳到 feed2。並且,將這個串流的資料儲檔到/tmp/video%Y-%m%d_%H-%M-%s.mp4 (年月日_時分秒),以60秒為一個檔案。(行4,5)
行4有個 -override_ffserver 就是指定要使用 ffmpeg 的參數來串流,而非 ffserver 的。
1 2 3 4 5 6 7 |
VIDEO_INPUT="-f v4l2 -r 10 -s 1280x720 -i /dev/video0 " OUT1_PARAM=" -c:v mpeg4 -s 160x120 -r 10 -b:v 100k" OUT1=" http://localhost:8090/feed1.ffm" OUT2_PARAM=" -c:v mpeg4 -s 1280x720 -r 10 -b:v 1000k -f tee -map 0:v -override_ffserver " OUT2="http://localhost:8090/feed2.ffm|[f=segment:segment_time=60:strftime=1]/tmp/video%Y-%m-%d_%H-%M-%S.mp4" ./ffmpeg $VIDEO_INPUT $OUT1_PARAM $OUT1 $OUT2_PARAM $OUT2 |
播放的命則與之前類似
1 2 |
ffplay rtsp://xx.xx.xx.xx:5554/live # play live 1280x720 video ffplay rtsp://xx.xx.xx.xx:5554/preview # play 160x120 view |
其它的串流方法
從 Hardware Encoder 截取串流
有工業用的 WEBCAM 支援硬體編碼的 H264, 一般常市面上的則支援 MJPG的硬體編碼。透過直接截取,不再重新轉碼,可以省下 大量的CPU 運算量。
1 |
./ffmpeg -f v4l2 -s 640x480 -input_format mjpeg -i /dev/video0 -override_ffserver -c:v copy http://localhost:8090/feed2.ffm |
其中 input_format 後面指定了 mjpeg ,有的也支援 h264 / h265。但到 2021 年為止在 V4L2 Linux API h265截碼不是做的很完整, h264 支援會較好。 要看自己的 webcam 指援哪些編碼,可以下達 v4l2-ctl –list-formats
直接傳遞 RTP 串流
若要使用簡單一點的點對點傳輸,可以直接把編碼後的資料,傳給指定的 IP:Port,這樣就不用架設 RTSP Server (ffserver的部份)了。下面會同時抓取聲音與影像,聲音裝置的部份,可以讀取 /proc/asound/cards 來判定有哪些聲音輸入。
從上圖看到有兩個裝置,分別為 「MID」與「C615」。這邊選用 C615來截取聲音,若要用MID則將下面範例的字取代掉就好。若使用 -ac 1 會看到錯誤訊息 「cannot set channel count to 1」,這是因為它只支援雙聲道,有的卡會只支援單聲道。在 “-ac 1” 的部份,可以試試看1 or 2,應該有一個會成功。
1 2 3 4 5 6 7 |
SIZE=160x120 FRATE=10 TARGET="rtp_mpegts rtp://192.168.11.2:1234" DEV=/dev/video2 AUDIO=hw:CARD=C615 ffmpeg -f alsa -ac 1 -ar 16000 -i $AUDIO -acodec aac -strict -2 -f v4l2 -r $FRATE -s $SIZE -i $DEV -c:v mpeg4 -f $TARGET |
下達上面的命令就開始串流了,然後在 Windows 端 (IP: 192.168.11.2) 開啟 VLC播放器,使用「媒體」–> 「開啟網路串流」網址輸入 rtp://@1234。就可以開始播放該串流了,第一次開啟會提示允許防火牆,記得選則允許。
結語
ffmpeg 其實是一個蠻好玩又強大的工具的,工作上可以來做這些串流,截圖存檔的。個人生活上,我也拿來將影像轉碼成 h265 來縮小檔案大小,有時甚至可以達到h264 1/10 的大小。當然這是因為在畫質上有點損失,加上一般相機都把編碼率調超高,才會有這種90%減少的可怕改進。