2021年4月12日 星期一

在 Raspberry Pi 上使用 FFmpeg 直播,並透過 OpenMAX 以硬體加速編碼

在 Raspberry Pi 官網上面看到 Raspberry Pi 4 Model B 規格寫說支援 H264 (1080p60 decode, 1080p30 encode),想試試看能不能擷取 HDMI 訊號、推送 1080p30 串流作直播。


先上一下這次的執行環境資訊:

pi@raspberrypi:~ $ uname -a

Linux raspberrypi 5.10.17-v7l+ #1403 SMP Mon Feb 22 11:33:35 GMT 2021 armv7l GNU/Linux


pi@raspberrypi:~ $ ffmpeg -hide_banner -version 

ffmpeg version 4.1.6-1~deb10u1+rpt1 Copyright (c) 2000-2020 the FFmpeg developers

built with gcc 8 (Raspbian 8.3.0-6+rpi1)


用 FFmpeg 直播的原理大致上如下
  1. 透過 v4l2 獲得 HDMI 擷取卡的影像訊號
  2. 透過 alsa 獲得 HDMI 擷取卡的聲音訊號
  3. 將影像訊號透過 OpenMAX 以硬體加速編碼成 h264 格式
  4. 將聲音訊號編碼成 aac 格式
  5. 以 flv 作為 muxer,將影像訊號和聲音訊號包在一起,推送 rtmp 串流到 YouTube 進行直播


開始吧



一、確認 HDMI 擷取卡的影像訊號的相關資訊

首先是名稱和存取路徑:

pi@raspberrypi:~ $ v4l2-ctl --list-devices

bcm2835-codec-decode (platform:bcm2835-codec):

        /dev/video10

        /dev/video11

        /dev/video12


bcm2835-isp (platform:bcm2835-isp):

        /dev/video13

        /dev/video14

        /dev/video15

        /dev/video16


UVC Camera (534d:2109): USB Vid (usb-0000:01:00.0-1.4):

        /dev/video0

        /dev/video1


其中 UVC Camera 的 /dev/video0 是我們要使用到的。


再來看這張擷取卡 支援的格式、解析度、影格率:

pi@raspberrypi:~ $ v4l2-ctl --list-formats-ext -d /dev/video0

ioctl: VIDIOC_ENUM_FMT

        Type: Video Capture

        [0]: 'MJPG' (Motion-JPEG, compressed)

                Size: Discrete 1920x1080

                        Interval: Discrete 0.033s (30.000 fps)

                Size: Discrete 1280x720

                        Interval: Discrete 0.017s (60.000 fps)

        [1]: 'YUYV' (YUYV 4:2:2)

                Size: Discrete 1920x1080

                        Interval: Discrete 0.200s (5.000 fps)

                Size: Discrete 1280x720

                        Interval: Discrete 0.100s (10.000 fps)


上面的內容我有刪減過,只列一下重點。可以看到這張擷取卡使用 MJPG 格式的時候, 1920x1080 最高可以到 30 FPS。


也透過 ffmpeg 確認一下:

pi@raspberrypi:~ $ ffmpeg -hide_banner -f v4l2 -list_formats all -i /dev/video0


[video4linux2,v4l2 @ 0x1c321c0] Compressed:       mjpeg :          Motion-JPEG : 1920x1080 1600x1200 1360x768 1280x1024 1280x960 1280x720 1024x768 800x600 720x576 720x480 640x480


[video4linux2,v4l2 @ 0x1c321c0] Raw       :     yuyv422 :           YUYV 4:2:2 : 1920x1080 1600x1200 1360x768 1280x1024 1280x960 1280x720 1024x768 800x600 720x576 720x480 640x480


先記住這些資訊,稍後使用 FFmpeg 時會用上。



二、確認 HDMI 擷取卡的聲音訊號的相關資訊

確認名稱和存取路徑:

pi@raspberrypi:~ $ sudo arecord -l

**** List of CAPTURE Hardware Devices ****

card 1: MS2109 [MS2109], device 0: USB Audio [USB Audio]

  Subdevices: 1/1

  Subdevice #0: subdevice #0


card 編號 1、device 編號 0,
填入 FFmpeg 指令的參數格式為 hw:1,0 ( 或 hw:1 就好 )


三、確認 FFmpeg 的 encoders 的相關資訊 

先確認有哪些 encoders,這邊用 grep 只列出 264、aac、mp3:

pi@raspberrypi:~ $ ffmpeg -hide_banner -encoders | grep -i 264

 V..... libx264              libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (codec h264)

 V..... libx264rgb           libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 RGB (codec h264)

 V..... h264_omx             OpenMAX IL H.264 video encoder (codec h264)

 V..... h264_v4l2m2m         V4L2 mem2mem H.264 encoder wrapper (codec h264)

 V..... h264_vaapi           H.264/AVC (VAAPI) (codec h264)


pi@raspberrypi:~ $ ffmpeg -hide_banner -encoders | grep -i aac

 A..... aac                  AAC (Advanced Audio Coding)


pi@raspberrypi:~ $ ffmpeg -hide_banner -encoders | grep -i mp3

 A..... libmp3lame           libmp3lame MP3 (MPEG audio layer 3) (codec mp3)

 A..... libshine             libshine MP3 (MPEG audio layer 3) (codec mp3)


其中 h264_omx 跟 aac 是我們要使用到的。


再來確認這兩個 encoder 有哪些可以調整的選項:

pi@raspberrypi:~ $ ffmpeg -hide_banner -h encoder=h264_omx

Encoder h264_omx [OpenMAX IL H.264 video encoder]:

    General capabilities: delay 

    Threading capabilities: none

    Supported pixel formats: yuv420p

h264_omx AVOptions:

  -omx_libname       <string>     ED.V..... OpenMAX library name

  -omx_libprefix     <string>     ED.V..... OpenMAX library prefix

  -zerocopy          <int>        E..V..... Try to avoid copying input frames if possible (from 0 to 1) (default 0)

  -profile           <int>        E..V..... Set the encoding profile (from -99 to 100) (default -99)

     baseline                     E..V..... 

     main                         E..V..... 

     high                         E..V..... 


pi@raspberrypi:~ $ ffmpeg -hide_banner -h encoder=aac

Encoder aac [AAC (Advanced Audio Coding)]:

    General capabilities: delay small 

    Threading capabilities: none

    Supported sample rates: 96000 88200 64000 48000 44100 32000 24000 22050 16000 12000 11025 8000 7350

    Supported sample formats: fltp

AAC encoder AVOptions:

  -aac_coder         <int>        E...A.... Coding algorithm (from 0 to 2) (default fast)

     anmr                         E...A.... ANMR method

     twoloop                      E...A.... Two loop searching method

     fast                         E...A.... Default fast search

  -aac_ms            <boolean>    E...A.... Force M/S stereo coding (default auto)

  -aac_is            <boolean>    E...A.... Intensity stereo coding (default true)

  -aac_pns           <boolean>    E...A.... Perceptual noise substitution (default true)

  -aac_tns           <boolean>    E...A.... Temporal noise shaping (default true)

  -aac_ltp           <boolean>    E...A.... Long term prediction (default false)

  -aac_pred          <boolean>    E...A.... AAC-Main prediction (default false)

  -aac_pce           <boolean>    E...A.... Forces the use of PCEs (default false)




四、結合上述資訊作為 ffmpeg 指令的參數

pi@raspberrypi:~ $ ffmpeg -hide_banner -loglevel repeat+level+verbose \

-f v4l2 -input_format mjpeg -video_size hd1080 -framerate 30 -i /dev/video0 \

-f alsa -thread_queue_size 1024 -i hw:1 \

-c:v h264_omx \

-c:a aac \

-f flv rtmp://a.rtmp.youtube.com/live2/<your_stream_key>


這邊將參數拆成 6 行,還記得文章開頭提及的「用 FFmpeg 直播的原理」嗎?撇除第 0 行先不談論,其餘的 5 行按照順序正好是
  1. 透過 v4l2 獲得 HDMI 擷取卡的影像訊號
  2. 透過 alsa 獲得 HDMI 擷取卡的聲音訊號
  3. 將影像訊號透過 OpenMAX 以硬體加速編碼成 h264 格式
  4. 將聲音訊號編碼成 aac 格式
  5. 以 flv 作為 muxer,將影像訊號和聲音訊號包在一起,推送 rtmp 串流到 YouTube 進行直播



五、用上更多的參數

我的指令最後長這樣,給大家參考:

pi@raspberrypi:~ $ ffmpeg -hide_banner -loglevel repeat+level+verbose \

-f v4l2 -input_format mjpeg -video_size hd1080 -framerate 30 -i /dev/video0 \

-f alsa -thread_queue_size 1024 -i hw:1 \

-c:v h264_omx -level:v 4.1 \

-b:v 6000k -g 15 -bf 2 -force_key_frames 'expr:gte(t,n_forced*2)' \

-c:a aac \

-f flv rtmp://a.rtmp.youtube.com/live2/<your_stream_key>


稍微列一下有試的幾種結果

  • hd1080 : 1920x1080 , 約 10 FPS
  • hd720 : 1280x720 , 約 20 FPS
  • hd480 : 852x480 , 約 45 FPS
可能還有優化空間,也有可能 RPi 4B 的極限就是這樣了,就先不花力氣深究。



其他參考資訊

沒有留言: