ラズパイ4の64bitOSのgstreamerでH.264のハードウェアエンコードを試す
Raspberry Pi OSの64bit版のビデオエンコード、デコード処理
32bitのRaspberry Pi OSでは従来通りのmmalを使ったライブラリがありましたが、64bit版ではそれのサポートはなくなり、V4L2(Video for Linux 2)のインタフェースでのドライバに一本化されました。
gstreamerではそれに対応したプラグインがすでにあります。
$ gst-inspect-1.0 |grep v4l2
video4linux2: v4l2src: Video (video4linux2) Source
video4linux2: v4l2sink: Video (video4linux2) Sink
video4linux2: v4l2radio: Radio (video4linux2) Tuner
video4linux2: v4l2deviceprovider (GstDeviceProviderFactory)
video4linux2: v4l2jpegdec: V4L2 JPEG Decoder
video4linux2: v4l2h264dec: V4L2 H264 Decoder
video4linux2: v4l2h264enc: V4L2 H.264 Encoder
video4linux2: v4l2jpegenc: V4L2 JPEG Encoder
video4linux2: v4l2convert: V4L2 Video Converter
video4linux2: v4l2video18convert: V4L2 Video Converter
video4linux2: v4l2video31jpegenc: V4L2 JPEG Encoder
この記事ではv4l2h264enc
とv4l2jpegdec
を試しました。
カーネルとgstreamerのバージョンは以下の通りです。
$ cat /proc/version
Linux version 6.1.21-v8+ (dom@buildbot) (aarch64-linux-gnu-gcc-8 (Ubuntu/Linaro 8.4.0-3ubuntu1) 8.4.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #1642 SMP PREEMPT Mon Apr 3 17:24:16 BST 2023
$ gst-inspect-1.0 --version
gst-inspect-1.0 version 1.18.4
GStreamer 1.18.4
http://packages.qa.debian.org/gstreamer1.0
H.264のエンコード
USBのWebcamの映像をファイルに保存します。^Cで終了させます。
#!/bin/sh -eux
gst-launch-1.0 v4l2src device="/dev/video0" ! \
'image/jpeg, width=1280, height=720, framerate=30/1' ! \
jpegdec ! \
videoconvert ! 'video/x-raw,format=I420,colorimetry=(string)bt601' ! \
v4l2h264enc extra-controls="encode,video_bitrate=2500000;" ! \
'video/x-h264,level=(string)4,profile=high' ! \
h264parse ! qtmux ! filesink location=out.mp4 -e
うまくいかないときなど、ログをみたい場合には gst-launch-1.0
に-v
オプションをつけます。
エンコーダのパラメータ設定
extra-controls
で指定できる項目はハードウェアエンコーダのデバイスである/dev/video11
を見ます。
$ v4l2-ctl -d /dev/video11 -L
Codec Controls
video_b_frames 0x009909ca (int) : min=0 max=0 step=1 default=0 value=0 flags=update
video_gop_size 0x009909cb (int) : min=0 max=2147483647 step=1 default=60 value=60
video_bitrate_mode 0x009909ce (menu) : min=0 max=1 default=0 value=0 flags=update
0: Variable Bitrate
1: Constant Bitrate
video_bitrate 0x009909cf (int) : min=25000 max=25000000 step=25000 default=10000000 value=10000000
sequence_header_mode 0x009909d8 (menu) : min=0 max=1 default=1 value=1
0: Separate Buffer
1: Joined With 1st Frame
repeat_sequence_header 0x009909e2 (bool) : default=0 value=0
force_key_frame 0x009909e5 (button) : flags=write-only, execute-on-write
h264_minimum_qp_value 0x00990a61 (int) : min=0 max=51 step=1 default=20 value=20
h264_maximum_qp_value 0x00990a62 (int) : min=0 max=51 step=1 default=51 value=51
h264_i_frame_period 0x00990a66 (int) : min=0 max=2147483647 step=1 default=60 value=60
h264_level 0x00990a67 (menu) : min=0 max=15 default=11 value=11
0: 1
1: 1b
2: 1.1
3: 1.2
4: 1.3
5: 2
6: 2.1
7: 2.2
8: 3
9: 3.1
10: 3.2
11: 4
12: 4.1
13: 4.2
14: 5
15: 5.1
h264_profile 0x00990a6b (menu) : min=0 max=4 default=4 value=4
0: Baseline
1: Constrained Baseline
2: Main
4: High
GPUのメモリの割り当てを増やす
最初は映像のサイズが320x240だとうまくいくのに、1280x720にすると以下のようなnot enough memory
のエラーが発生していました。
../sys/v4l2/gstv4l2videoenc.c(828): gst_v4l2_video_enc_handle_frame (): /GstPipeline:pipeline0/v4l2h264enc:v4l2h264enc0:
Maybe be due to not enough memory or failing driver
これは以下のようにして解決できました。
sudo raspi-config
で
4 Performance Options
P2 GPU Memory
256
をセットする。
このコマンドを終了すると自動でリブートします。
設定値の確認。
$ grep gpu_mem /boot/config.txt
gpu_mem=256
MJPEGのデコードもハードウェアでやってみる
いろいろ試行錯誤した結果、以下のスクリプトでできました。
#!/bin/sh -eux
gst-launch-1.0 v4l2src device="/dev/video0" ! \
'image/jpeg, width=1280, height=720, framerate=30/1' ! \
jpegparse ! v4l2jpegdec ! \
v4l2h264enc extra-controls="encode,video_bitrate=2500000;" ! \
'video/x-h264,level=(string)4,profile=high' ! \
h264parse ! mpegtsmux ! filesink location=out.m2ts
どうもjpegparse
を使用すると^Cによるシグナルをうまくハンドリングできないようで、mp4のファイルの後処理がうまくできませんでした。それで、強制終了させても問題ないコンテナフォーマットのmpeg2tsに変更しました。
htop
でCPUの負荷をみると、rec1.sh
に比べて少し負荷が減っているのがわかります。
v4l2jpegdecとv4l2h264encの間をGPUのメモリで直接渡せるようにできればもっとCPUの負荷を減らせると思うのですが、とりあえず今日のところはここまでにします。
Discussion