Raspberry Pi: 動画の撮影
概要
カメラの接続が完了したら Raspberry Pi で動画を撮影してみよう。Raspberry Pi は Pico 以外の全てのモデルに H.264 ハードウェアエンコーダーを搭載していることから、リアルタイムでの映像の加工やフォーマット変換、ストリーミングといった用途も可能である。
この記事では映像の記録方法の説明に集中するために必要最小限のオプションのみを指定するにとどめている。多機能な FFmpeg は特に数多くのオプションが存在する。目的にあった解像度やフレームレートで記録するためにはそれぞれのコマンドのドキュメントを参照。
Table of Contents
接続と特性の確認
カメラの接続で紹介したようにカメラが認識されていることを lsusb
(USB接続), v4l2-ctl --list-device
で確認したあとは、デバイスが対応しているコーデック、解像度、フレームレートを次のコマンドで確認する。
v4l2-ctl -d /dev/video0 --list-formats
v4l2-ctl -d /dev/video0 --list-formats-ext
ffmpeg -f v4l2 -list_formats all -i /dev/video0
pi@pirite:~ $ v4l2-ctl -d /dev/video0 --list-formats
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'YUYV' (YUYV 4:2:2)
[1]: 'MJPG' (Motion-JPEG, compressed)
pi@pirite:~ $ v4l2-ctl -d /dev/video2 --list-formats
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'YU12' (Planar YUV 4:2:0)
[1]: 'YUYV' (YUYV 4:2:2)
[2]: 'RGB3' (24-bit RGB 8-8-8)
[3]: 'JPEG' (JFIF JPEG, compressed)
[4]: 'H264' (H.264, compressed)
[5]: 'MJPG' (Motion-JPEG, compressed)
[6]: 'YVYU' (YVYU 4:2:2)
[7]: 'VYUY' (VYUY 4:2:2)
[8]: 'UYVY' (UYVY 4:2:2)
[9]: 'NV12' (Y/CbCr 4:2:0)
[10]: 'BGR3' (24-bit BGR 8-8-8)
[11]: 'YV12' (Planar YVU 4:2:0)
[12]: 'NV21' (Y/CrCb 4:2:0)
[13]: 'RX24' (32-bit XBGR 8-8-8-8)
上記の例では C270n は Raw (YUYV) と Motion JPEG に対応しており、カメラモジュールは Raw と Motion JPEG、h.264 コーデックに対応していることがわかる。
一般に、市販の Web カメラは PC 側でエンコードする前提で設計されているためカメラ側に h.264 ハードウェアエンコーダーを搭載していない。しかし、h.264 コーデックに対応したカメラであれば Raspberry Pi 側の h.264 ハードウェアエンコーダーを使用しなくてもよく負荷は少ない。
利用可能な解像度と FPS (frames per second) の詳細は次のコマンドで参照できる。
pi@pirite:~ $ v4l2-ctl --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'YUYV' (YUYV 4:2:2)
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
...
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 160x90
Interval: Discrete 0.033s (30.000 fps)
...
pi@pirite:~ $ ffmpeg -f v4l2 -list_formats all -i /dev/video0
ffmpeg version 4.3.3-0+rpt2+deb11u1 Copyright (c) 2000-2021 the FFmpeg developers
[video4linux2,v4l2 @ 0x5594ba9fa0] Raw : yuyv422 : YUYV 4:2:2 : 640x480 160x90 160x120 176x144 320x180 320x240 352x288 432x240 640x360 800x448 800x600 864x480 960x720 1024x576 1280x720 1600x896 1920x1080 2304x1296 2304x1536
[video4linux2,v4l2 @ 0x5594ba9fa0] Compressed: mjpeg : Motion-JPEG : 640x480 160x90 160x120 176x144 320x180 320x240 352x288 432x240 640x360 800x448 800x600 864x480 960x720 1024x576 1280x720 1600x896 1920x1080
/dev/video0: Immediate exit requested
製品の仕様で 1080p, 60FPS とされているケースでも、無圧縮であれば 2304x1536 (ただし 2FPS) の大きさまで対応していたり、逆に Motion-JPEG の 1280x720 以外は 30FPS までしか対応していないようなケースもある。
例えば以下は Logitech C922n での実行結果である。製品仕様は 1080p (1920x1080) / 60fps または 720p (1280x720) / 30fps 対応のカメラだが、以下の実行結果から Motion-JPEG 入力のケースに限ることがわかる。
Raspberry Pi の限界は低く、映像の記録と同時にリアルタイムでできることは多くない。特にソフトウェアエンコード/デコードのステップは負荷が高いため回避すべきである。Fig 1 の変換パターンで最も負荷の低い経路を選ぶのが良い。
動画の撮影方法
FFmpeg は動画の取り込みや保存、変換、加工を行うことができる多機能な動画編集ツールである。ここでは FFmpeg を使用して USB Web カメラから動画を撮影してみよう。まずは apt
を使って ffmpeg
をインストールする。
pi@pirite:~ $ sudo apt install -y ffmpeg
Reading package lists... Done
...
AVI 形式での映像記録
個人的に Raspberry Pi でのカメラ映像の保存は h.246 コーデックの AVI 形式を推奨する。動画フォーマットとしては MP4 形式がより一般的だが、経験的に MP4 は録画時の破損に弱く、FFmpeg や OS の異常終了、USB ストレージの抜け、Raspberry Pi 本体の電源切断などのデータ破損によって記録中だったファイル全体を再生/変換できなくなる。
一方、インターリーブで保存する AVI 形式であれば異常終了した直前までの記録を正しく再生できることから、一発勝負の撮影や長時間の記録、証拠映像として残すような記録には MP4 より AVI での保存が向いている。最終的に MP4 形式が必要であっても、一次記録として AVI で記録を終えた後に MP4 に変換することは難しくないし CPU 負荷も大きくはない。
H.264は高圧縮な動画コーデックである。Raspberry Pi は初代 Model A から H.264 ハードウェアエンコーダを搭載して CPU の負荷なく高速に変換できることから、特に理由がなければエンコードには h.264 を選択すればよいだろう。以下の例は h264_v4l2m2m
(V4L2 H.264 memory to memory encoder wrapper) を使用して H.246 コーデックの AVI 形式で保存している。
pi@pirite:~$ ffmpeg \
-i /dev/video0 \
-c:v h264_v4l2m2m \
-pix_fmt yuv420p \
testshot.avi
上記のコマンドオプションは「/dev/video0
の入力映像を h.264 エンコードして testshot.avi
ファイルに (AVI 形式で) 保存」という意味である。FFmpeg は出力形式をファイルの拡張子で推測するため、上記のコマンドを .mp4 に変更するだけで MP4 形式で保存できる。
コマンドを起動するとメッセージが長々と出力され Q キーを入力すると終了する。
ffmpeg version 4.3.3-0+rpt2+deb11u1 Copyright (c) 2000-2021 the FFmpeg developers
...
Input #0, video4linux2,v4l2, from '/dev/video0':
Duration: N/A, start: 6483.108721, bitrate: 110592 kb/s
Stream #0:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 640x360, 110592 kb/s, 30 fps, 30 tbr, 1000k tbn, 1000k tbc
Stream mapping:
Stream #0:0 -> #0:0 (rawvideo (native) -> h264 (h264_v4l2m2m))
Press [q] to stop, [?] for help
[h264_v4l2m2m @ 0xb98c20] Using device /dev/video11
[h264_v4l2m2m @ 0xb98c20] driver 'bcm2835-codec' on card 'bcm2835-codec-encode' in mplane mode
[h264_v4l2m2m @ 0xb98c20] requesting formats: output=YU12 capture=H264
[h264_v4l2m2m @ 0xb98c20] Failed to set gop size: Invalid argument
Output #0, avi, to 'testshot.avi':
Metadata:
ISFT : Lavf58.45.100
Stream #0:0: Video: h264 (h264_v4l2m2m) (H264 / 0x34363248), yuv420p, 640x360, q=-1--1, 200 kb/s, 30 fps, 30 tbn, 30 tbc
Metadata:
encoder : Lavc58.91.100 h264_v4l2m2m
[h264_v4l2m2m @ 0xb98c20] ff_v4l2_buffer_buf_to_avpkt
...
映像がどのような形式で保存されたかを知るには apt でインストール可能な mediainfo
コマンドを利用することができる。以下の映像は 640x360, 30fpx, 200kbps, H.264, 10.6 秒で 272kB の大きさとなっていることが分かる。
ここでは説明を簡潔にするために最小限のコマンドオプションのみを使用したが、多くの場合は以下のようなオプションを併用する。ここで指定順序に意味があることに注意。
- 映像の解像度
-
-s 1280x960
で映像の解像度を 1280x960 に設定する。対応していない解像度の場合は別の解像度に変更される。映像が大きくなると画質が荒くなるのでビットレートも上げたほうが良い。pi@pirite:~$ ffmpeg -s 1280x960 -i /dev/video0 -c:v h264_v4l2m2m -pix_fmt yuv420p testshot.avi
- 動きの滑らかさ
-
-r 15
で 1 秒あたりの画像数を 15fps に設定する。安価な Web カメラであれば 30fps が上限だろう。動きの少ない映像なら低めに設定しても良い。pi@pirite:~$ ffmpeg -r 15 -i /dev/video0 -c:v h264_v4l2m2m -pix_fmt yuv420p testshot.avi
- 映像の画質
-
-b:v 5000k
で映像のビットレートを 5000kbps に設定する。ただし画質には限界があり、ビットレートを上げすぎてもデータサイズが大きくなるだけで画質は変わらないことに注意。固定ビットレートではなく、最小限のビットレートとこれ以上は画質が変わらないというビットレートを調べて-minrate
,-maxrate
で可変ビットレートとして指定したほうが良いかもしれない。pi@pirite:~$ ffmpeg -i /dev/video0 -b:v 5000k -c:v h264_v4l2m2m -pix_fmt yuv420p testshot.avi
- 出力バッファサイズ
-
-bufsize
出力オプションはビットレートの自動調整とバッファフラッシュのタイミングに作用する。目安として-b:v
と同じ値を指定するとおおむね 1 秒に 1 回程度フラッシュされるようになる。一般に-bufsize
を低くするとシステム負荷が大きくなり、ビットレートの低下が起きやすくなることから、可能な限り大きな値を指定することが推奨されている。しかし、突発的な電源切断が発生しやすい環境でなるべく電源断の直前までの映像を記録しておきたい場合や、ストリーミングで遅延をなるべく少なくしたい場合には、デフォルト値では必要以上に時間が空きすぎることから、目標のビットレートを落とさない程度に小さな値を指定する方が良いと言える。最初は-b:v
と同じ値を指定して、生成される動画のビットレートが低下していないかを見ながら調整すると良い。pi@pirite:~$ ffmpeg -i /dev/video0 -b:v 5M -bufsize 5M -c:v h264_v4l2m2m -pix_fmt yuv420p testshot.avi
カメラの性能制限により、一般に高い解像度は高い FPS に対応していない。解像度と FPS の関係は v4l2-ctl -d /dev/video0 --list-formats-ext
で確認することができる。
同様に、高解像度で高 FPS の設定は圧縮フォーマット (Motion-JPEG) でのみ対応というケースが多い。しかし Motion-JPEG はデコードに多くの CPU 時間を要し、Raspberry Pi の ARM プロセッサではリアルタイムでの変換が難しい。もし高解像度、高FPSが必要で Motion-JPEG で入力しなければならないのであれば、h.264 には変換せず無変換で保存するのがもっとも現実的である (方法は後述)。
また次のセクションで解説するようにマイク付きカメラであれば音声付きで保存することもできる。
音声付きの映像記録
一般的な Web カメラはオンライン会議やライブチャットができるように標準でマイクを搭載している。ここではカメラのマイクを使って映像とともに音声も記録してみよう。
まず USB カメラの音声入力がどのデバイスで認識されているかを arecord
コマンドで確認する。以下の例では USB 接続している C270n の音声入力が card 1 の device 0 として認識されていることを示している。
pi@pirite:~ $ arecord -l
**** List of CAPTURE Hardware Devices ****
card 1: WEBCAM [C270 HD WEBCAM], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
次に先述の ffmpeg
コマンドに音声記録用のオプションを追加する。以下の例は alsa
を使って card=1, device=0 の音声入力デバイス (-i hw:1,0
) からモノラル入力 (-ac 1
) で記録するように指定している。
pi@pirite:~ $ ffmpeg \
-i /dev/video0 \
-f alsa -ac 1 -i hw:1,0 \
-c:v h264_v4l2m2m \
-pix_fmt yuv420p \
testshot.avi
テキスト情報付きの映像記録
FFmpeg の drawtext フィルターを使うと映像にテキストを重ねて記録することができる。変化しない固定的なテキストや単純な時刻であればコマンドオプションのみで表示させることができるが、ここでは温度や位置情報など、別のセンサーデバイスのデータを映像と一緒に記録することを想定してみよう。
外部のデータを表示させるスキームは、コマンドラインオプションでテキストファイルを指定して「1 フレームごとにテキストを読み込む」という動作になる。したがって FFmpeg と一緒にテキストファイルに最新情報を書き込むプログラムも実行されていなければならない。
試験的に次のようなスクリプトを実行しよう。これは 1 秒おきにカウントアップする数値で /tmp/telop.txt
ファイルの内容を書き換える (放置しても 1 時間で終了する)。
pi@pirite:~ $ n=0; \
while [ $n -lt 3600 ]; do \
echo $n > /tmp/telop.tmp; \
mv /tmp/telop.tmp /tmp/telop.txt; \
n=`expr $n + 1`; \
sleep 1; \
done &
[1] 7284
pi@pirite:~ $ cat /tmp/telop.txt
6
pi@pirite:~ $ cat /tmp/telop.txt
12
テキストを描画するための -vf ...
オプションを追加して映像の記録を開始する。
pi@pirite:~ $ ffmpeg \
-i /dev/video0 \
-vf 'format=pix_fmts=yuv420p,drawtext=textfile=/tmp/telop.txt:fontsize=64:reload=1' \
-c:v h264_v4l2m2m \
-pix_fmt yuv420p \
testshot.avi
以下は実際に C270 を使って撮影した映像である。カメラの映像と共にテキストファイルの内容が記録されていることがわかるだろう (画質が荒かったのでビットレートに 700k を指定した)。
drawtext
オプションを調整することで文字のフォントや位置、大きさ、色、背景、透明度などをより詳細にカスタマイズすることができる。また -vf
内にコンマ区切りで drawtext
オプションを複数指定することで複数行のように描画したり、複数の位置に描画することもできる。
FFmpeg によるテキストファイルの読み込み頻度が非常に高いため、書き込み途中の内容が意図せず表示されてしまわないようにファイルの更新はアトミックに行う必要がある。つまり、更新側のプログラムは別名のファイルに書き込んで mv
や rename()
で置き換えなければならない。
高頻度の書き込みは SD カードやフラッシュメモリの劣化を早めることになる。長期的に使用するのであれば tmpfs (RAM ディスク) のパーティションを作成してそこをテキストファイルの保存場所とするのが良い。
動画のストリーミング方法
TBC
その他の記録方法
h.264 ハードウェアエンコーダー以外の方法や、試行錯誤してうまく機能しなかったフィルタなど。v4l2m2m
を使って MPEG-4 形式で記録できているのであれば特に読む必要はない。
カメラ側での H.264 エンコード
Raspberry Pi 専用のカメラモジュールのようにカメラ側で H.264 ビデオエンコーディングに対応しているのであれば、映像入力に H.264 で指定すれば Raspberry Pi 側の負荷をさらに低くできる。
pi@pirite:~ $ ffmpeg \
-input_format h264 -i /dev/video2 \
-c:v copy \
testshot.avi
USB Web カメラの時との違いは、カメラからの入力フォーマットに h264
を指定しており、FFmpeg のフィルタは何もしない (無変換) としている点である。
カメラモジュールであれば Raspberry Pi 標準の raspivid
コマンドでも h.264 エンコードの動画で記録することができるが、素の h.264 形式のままではいささか取り回しが悪いため FFmpeg にパイプして AVI 形式か MP4 形式で保存すると良い。以下の例は 10 秒間の動画を記録している。
pi@pirite:~ $ raspivid -o - -t 10000 | ffmpeg -i - -c:v copy testshot.avi
以下は実際にカメラモジュールから取り込んだ H.264 エンコードの動画である (右半分のちらつきは液晶ディスプレイの光)。1920×1080、25fps、10 秒で約 20MB の大きさとなった。
カメラモジュールでも USB Web カメラと同様に FFmpeg のオプションを指定することで音声を含めて記録することができる。ただし、独立したマイク入力はカメラ映像と同期していないことから、入力の時差によって音ズレなどが発生することは考慮しておかなければならない。
なお、FFmpeg の機能を利用して映像のフィルタリングやフリップといった加工を行う場合は Raspberry Pi 側で h.264 デコード/エンコードが必要になる点に注意。
MP4 形式での記録
先述の通り FFmpeg はファイル拡張子を認識するため、出力ファイルの拡張子を .avi から .mp4 に変えることで MP4 形式で保存することができる。AVI も MP4 も H.264 を格納できるため必要最小限の範囲ではそれ以外の変更は必要ない (詳細な調整を行いたい場合はそれぞれ固有のオプションを指定する必要があるかもしれない)。
pi@pirite:~$ ffmpeg -i /dev/video0 -c:v h264_v4l2m2m -pix_fmt yuv420p testshot.mp4
FFmpeg での記録中に本体の電源が切断されたときなど、書き込み途中で破損した MP4 ファイルは Windows でも macOS でも全く再生することができないし、ネットでいくつか見つかる復旧方法のどれもうまく行かなかった。Fig 2 は実際に Windows で再生しようとしたときに表示される 0xc00d36c4 エラーである。
h264_omx
: Web を検索していると h264_omx
を使用した h.264 ハードウェアエンコーディングの情報をよく見かけるが、2022/01 時点の Raspbian 11 (bullseye) の環境で利用可能な libOMX_Core.so
または libOmxCore.so
を見つけることができなかった。ソースビルドで入手できるのかもしれないが、h264_v4l2m2m
で正しくエンコードができているのであれば特に必要はないだろう。
libx264 によるソフトウェアエンコード
FFmpeg で出力ファイルを .mp4 ファイルとしながら -c:v
オプションを省略したり、明示的に -c:v libx264
オプションを指定すると、デフォルトの h.246 ソフトウェアエンコーダーである libx264
を使用する。libx264
で作成したファイルが Windows や macOS でも真っ黒で再生ボタンも機能しない映像(?)が表示される場合は -pix_fmt
でピクセルフォーマットを指定するとうまく機能する。
pi@pirite:~$ ffmpeg \
-i /dev/video0 \
-c:v libx264 -pix_fmt yuv420p \
testshot.mp4
Raspberry Pi での h.264 ソフトウェアエンコーダーは CPU 使用率が高く、何らかの理由でハードウェアエンコーダーが使用できない場合のみにとどめたほうが良いだろう。しかし libx264
のほうが汎用性が高く市販カメラとの互換性も良いのも事実である。相性の問題で h264_v4l2m2m
ではエラーになるが libx264
ではエラーにならないケースもしばしば見られる。
Motion JPEG での記録
先に v4l2-clt
で調べたとおり C270n は YUYV (無圧縮) の他に Motion JPEG のビデオフォーマットに対応している。Motion JPEG は MPEG-4 と比べてファイルサイズが大きくなるが、Raspberry Pi 本体の h.264 ハードウェアエンコーダーを専有しないため、複数のカメラから同時に記録するときなどに有用なことがあるかもしれない。
以下のコマンドはカメラの映像を Motion JPEG で受信してそのまま Matroska Video 形式で保存する。オプションに JPEG の圧縮率である -q:v 2
を指定すると容量が大きくなる代わりに映像の品質を最大まで上げることができる (2~31 の範囲で指定できる)。
pi@pirite:~ $ ffmpeg \
-f v4l2 -input_format mjpeg -i /dev/video0 \
-q:v 2 \
-c:v copy \
testshot.mkv
mkv ファイル自体は様々なフォーマットのデータを格納できるコンテナ形式なので分かりづらいが mediainfo
コマンドを使用するとその内容が Motion JPEG 形式であることが分かる。
pi@pirite:~ $ mediainfo testshot.mkv
...
Video
ID : 1
Format : V_MJPEG
Codec ID : V_MJPEG
Duration : 16 s 490 ms
Bit rate : 13.9 Mb/s
Width : 640 pixels
Height : 480 pixels
Display aspect ratio : 4:3
Frame rate mode : Variable
Color space : YUV
Stream size : 27.4 MiB (98%)
Default : Yes
Forced : No
Color range : Full
Matrix coefficients : BT.470 System B/G
上記のコマンドで保存した mkv 形式 (Motion JPEG) の映像は 640×480, 30fps, 10.7 秒で 17.8MB の大きさとなった。この mkv 形式の Motion JPEG は Windows 10 標準の「映画 & テレビ」ビューワーで表示することができる (またトリミング等で編集して保存すると MP4 に変換することもできる)。macOS では VLC などのメディアプレーヤーをインストールする必要がある。いずれにしても h.264 エンコードが可能な Windows/macOS マシンで mp4 に再変換したほうが取り回しは良いかもしれない。
高フレームレートの撮影
カメラが高フレームレートに対応していたとしても Raspberry Pi の能力でリアルタイムのコーディック変換やサイズ変換、テキスト描画などを行うことは現実的に厳しい。このためカメラの対応しているコーデックをそのまま無変換で保存するアプローチで考えるのが良い。
例えば Logitech C922n は Motion-JPEG の 1280x720 でのみ 60fps に対応しているが、Fig 1 から Motion-JPEG 入力で加工変換を行おうとするとソフトウェアデコードのステップが必要となり 60fps の速度は難しくなる。このため Motion JPEG での記録を参考に Motion-JPEG を無変換で AVI に記録する。
pi@pirite:~ $ ffmpeg -f v4l2 -input_format mjpeg -s 1280x720 -r 60 -i /dev/video0 -c:v copy testshot.avi
ffmpeg version 4.3.3-0+rpt2+deb11u1 Copyright (c) 2000-2021 the FFmpeg developers
Input #0, video4linux2,v4l2, from '/dev/video0':
Duration: N/A, start: 235201.151538, bitrate: N/A
Stream #0:0: Video: mjpeg (Baseline), yuvj422p(pc, bt470bg/unknown/unknown), 1280x720, 60 fps, 60 tbr, 1000k tbn, 1000k tbc
Output #0, avi, to 'testshot.avi':
Metadata:
ISFT : Lavf58.45.100
Stream #0:0: Video: mjpeg (Baseline) (MJPG / 0x47504A4D), yuvj422p(pc, bt470bg/unknown/unknown), 1280x720, q=2-31, 60 fps, 60 tbr, 60 tbn, 60 tbc
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help
frame= 669 fps= 41 q=-1.0 Lsize= 108718kB time=00:00:18.30 bitrate=48667.5kbits/s speed=1.13x
video:108686kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.029111%
pi@pirite:~ $ mediainfo testshot.avi
General
Complete name : testshot.avi
Format : AVI
Format/Info : Audio Video Interleave
File size : 106 MiB
Duration : 18 s 300 ms
Overall bit rate : 48.7 Mb/s
Writing application : Lavf58.45.100
Video
ID : 0
Format : JPEG
Codec ID : MJPG
Duration : 18 s 300 ms
Bit rate : 48.7 Mb/s
Width : 1 280 pixels
Height : 720 pixels
Display aspect ratio : 16:9
Frame rate : 60.000 FPS
Color space : YUV
Chroma subsampling : 4:2:2
Bit depth : 8 bits
Compression mode : Lossy
Bits/(Pixel*Frame) : 0.880
Stream size : 106 MiB (100%)
ここで -r
オプションの指定位置に注意。-i
より後ろに配置すると出力オプションとみなされ、実際は低フレームレートでの入力となっているかもしれない。記録した映像が首尾よく 60fps の映像が記録できたとしても、入力や加工の段階で一度低フレームレートとなったあとに引き伸ばされていないか注意する必要がある。
フォーマットの変換
この記事ではカメラからの一次保存形式に AVI を推奨したが、現実的な取り回しに関しては Windows や mac OS、各種ブラウザで再生できる MP4 形式で扱いたい要望もあるだろう。MP4 形式へは以下のコマンドで変換することができる。
pi@pirite:~$ ffmpeg -i testshot.avi testshot.mp4
特に、この記事の方法 ─ つまり h264_v4l2m2m
を指定して作成した AVI ファイルでは、映像が既に H.264 エンコードされていることから -c:v copy
オプションを指定してコンテナ形式だけを MP4 に変換することで、CPU をほとんど使用せず変換することができる (とはいえ 1 時間の映像で30秒~1分程度はかかるが)。
pi@pirite:~$ ffmpeg -i testshot.avi -c:v copy testshot.mp4
クアッドコアの CPU であれば -c:v copy
指定の変換を Raspberry Pi 上で行っても他の処理の影響を心配することはないだろう。
FFmpeg は MP4 以外にも様々な形式に変換できる。例えば Fig 3 は AVI 形式で保存した動画を 5fps のアニメーション GIF に変換したものである。
pi@pirite:~ $ ffmpeg -i testshot.avi -r 5 testshot.gif
FFmpeg のエラー
- Logitech C922n の問題
-
Logitech C922n の映像を Raspberry Pi で記録しようとすると以下のようなエラーが出た。
[avi @ 0x15bf8f0] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 1 >= 1 av_interleaved_write_frame(): Invalid argument
これは C270n では起きないため C922n 固有の問題だろう。そしてハードウェアエンコーダーの
h264_v4l2m2m
の代わりにソフトウェアエンコーダーのlibx264
を使用すると発生しないことから C922n とh264_v4l2m2m
との相性の問題のように見える。この問題はオプションに
-ss 0:00
を追加したら発生しなくなった。最初期のフレームの DTS が不正な値になっているのだろうか?pi@pirite:~ $ ffmpeg -ss 0:00 -i /dev/video0 -c:v h264_v4l2m2m footage.avi
Appendix
USB Web カメラ vs. カメラモジュール
USB Web カメラとカメラモジュールではどちらが良いか、という観点では、個人的には USB Web カメラを使用すれば良いという所感である。カメラモジュールは、静かな室内で固定した状態で使用し、なるべく安く済ませたいのであれば選択肢に入るだろうというところで。それぞれの特徴を以下に書き記す。
- カメラモジュール
-
- カメラ側の h.264 ハードウェアエンコーダーを使用できる (Raspberry Pi 本体側の負荷が低い)。
- 比較的安価である。
- 省スペース。
- USB ポートを専有しない。
- DIY 感が出る (プレゼンやデモでは重要)。
- USB Web カメラ
-
- 外殻を持ち基盤やケーブルが保護されている。
- 一般にマイク (音声入力) が標準で付属している。
- 複数のカメラを USB ポートに接続して同時に撮影できる。
- ケーブルが長く Raspberry Pi 本体から離れた位置で撮影ができる。
- カメラを PC でも使うことができる。
- オートフォーカスや自動露光など機能的な選択肢が比較的豊富。
- Linux OS で USB カメラを使う一般的な知識が応用できる。
双方の最も大きな違いは筐体の堅牢さだろう。手元のカメラモジュールのカメラ部分はコネクタで基盤と接続しているだけで micro SD カードよりも小さい部品である。これを屋外や車内に設置すればすぐに紛失してしまうだろう。
USB Web カメラであっても h.264 ビデオフォーマットに対応したモデルは存在する。例えば Logicool C925e や C930e の他にも Amazon を検索すると安いものは 3000 円前後から見つかるようだ (いずれも動作未確認)。
ハードウェアエンコード vs. ソフトウェアエンコード
h.264 ハードウェアエンコーダとソフトウェアエンコーダーでは Raspberry Pi 本体の負荷はどの程度違うのだろうか。比較のためにそれぞれのエンコードを実行しながら vmstat 1
を起動して負荷を見てみよう。
まず h264_v4l2m2m
を指定して h.264 ハードウェアエンコーダーを有効にしたケース。記録中も user, system の CPU 使用率はほとんど上昇せず idle が 100% 近くで推移していることがわかる。free memory に若干の減少が見られる。
pi@pirite:~ $ ffmpeg -i /dev/video0 -c:v h264_v4l2m2m -pix_fmt yuv420p testshot.avi
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 399400 48384 395032 0 0 0 0 75 67 0 0 100 0 0
0 0 0 399400 48384 395032 0 0 0 0 74 58 0 0 100 0 0
0 0 0 394108 48384 395032 0 0 0 24 514 449 12 1 87 0 0 ← 起動
0 0 0 327900 48384 395032 0 0 0 0 1562 1150 3 5 92 0 0
1 0 0 327992 48384 395036 0 0 0 0 1574 1279 1 0 99 0 0
1 0 0 327740 48384 395036 0 0 0 0 1463 1097 1 0 99 0 0
0 0 0 327740 48384 395036 0 0 0 0 1458 1096 1 1 98 0 0
0 0 0 327740 48384 395036 0 0 0 100 1508 1224 1 0 99 0 0
0 0 0 327740 48384 395036 0 0 0 0 1508 1134 1 0 99 0 0
0 0 0 327740 48384 395036 0 0 0 40 1504 1140 1 0 99 0 0
0 0 0 327740 48384 395036 0 0 0 0 1574 1228 1 0 99 0 0
1 0 0 327740 48384 395036 0 0 0 0 1551 1221 1 0 99 0 0
0 0 0 327740 48384 395036 0 0 0 0 1549 1253 1 0 99 0 0
1 0 0 398728 48384 395136 0 0 0 0 433 388 1 2 98 0 0 ← 終了
0 0 0 398884 48384 395180 0 0 0 0 76 64 0 0 100 0 0
続いて libx264
を指定して h.264 ソフトウェアエンコーダーを有効にしたケース。こちらは起動から徐々に user の CPU 使用率が上昇し数秒で 80% 前後となる。また実行中は memory の free 値も大きく減少している。
pi@pirite:~ $ ffmpeg -i /dev/video0 -c:v libx264 -pix_fmt yuv420p testshot.avi
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 398456 48460 395092 0 0 0 0 74 54 0 0 100 0 0
0 0 0 398456 48460 395092 0 0 0 0 83 65 0 0 100 0 0
0 0 0 394172 48460 395092 0 0 0 0 336 246 12 2 86 0 0 ← 起動
0 0 0 336472 48460 395100 0 0 0 32 943 625 6 3 92 0 0
0 0 0 311020 48460 395100 0 0 0 0 1031 849 5 0 95 0 0
2 0 0 285316 48460 395100 0 0 0 0 1022 847 3 2 95 0 0
0 0 0 259864 48460 395100 0 0 0 0 980 799 3 2 95 0 0
1 0 0 233908 48460 395100 0 0 0 0 982 808 9 2 89 0 0
1 0 0 227608 48464 395096 0 0 0 44 918 610 26 0 73 0 0
5 0 0 196864 48464 395100 0 0 0 0 1125 743 63 1 37 0 0
4 0 0 174436 48464 395100 0 0 0 0 1338 895 88 2 10 0 0
6 0 0 163600 48464 395100 0 0 0 0 1322 891 92 1 8 0 0
6 0 0 160072 48464 395356 0 0 0 0 1359 957 90 2 8 0 0
5 0 0 159820 48464 395356 0 0 0 0 1373 991 84 5 12 0 0
6 0 0 159820 48464 395356 0 0 0 0 1359 958 76 13 12 0 0
6 0 0 159820 48464 395356 0 0 0 0 1305 893 79 9 12 0 0
6 0 0 159316 48464 395612 0 0 0 0 1308 782 82 8 10 0 0
6 0 0 159316 48464 395612 0 0 0 12 1505 806 81 10 10 0 0
7 0 0 159064 48464 395612 0 0 0 0 1526 792 88 1 11 0 0
7 0 0 158812 48464 395812 0 0 0 0 1515 796 81 10 9 0 0
0 0 0 398464 48464 396324 0 0 0 0 1212 506 67 2 31 0 0 ← 終了
0 0 0 398560 48468 396320 0 0 0 12 86 80 0 0 100 0 0
最後にカメラモジュール側の h.264 ハードウェアエンコーダーを使用するケース。CPU 使用率の上昇は見られず、free memory の減少量や割り込み、コンテキストスイッチの回数も少ない。比較した中では本体の負荷が最も低いと言えるだろう。
pi@pirite:~ $ ffmpeg -input_format h264 -i /dev/video2 -c:v copy testshot.avi
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 397640 48412 395080 0 0 0 0 73 64 0 0 100 0 0
1 0 0 397388 48412 395080 0 0 0 0 110 95 0 1 99 0 0
1 0 0 383024 48412 395080 0 0 0 0 304 340 12 2 86 0 0 ← 起動
0 0 0 380724 48412 395340 0 0 0 0 486 718 5 2 93 0 0
1 0 0 380724 48412 395540 0 0 0 416 833 1175 0 1 98 0 0
0 0 0 380472 48412 395840 0 0 0 0 401 585 0 0 99 0 0
0 0 0 380244 48412 396108 0 0 0 0 284 474 0 1 99 0 0
0 0 0 379992 48412 396308 0 0 0 0 317 526 0 0 100 0 0
0 0 0 379740 48416 396616 0 0 0 36 362 575 0 0 100 0 0
0 0 0 379488 48416 396820 0 0 0 0 356 560 0 1 99 0 0
0 0 0 379236 48416 397132 0 0 0 0 351 548 0 0 99 0 0
0 0 0 378984 48416 397332 0 0 0 0 344 568 0 0 100 0 0 ← 終了
0 0 0 394128 48416 398404 0 0 0 12 85 83 0 0 100 0 0
比較のため Web カメラが対応している Motion JPEG ビデオフォーマットの入力を無変換で Matroska Video 形式として保存するケース。CPU 使用率はほとんど上昇しない。Web カメラが対応しているエンコードでそのまま保存する方法は h.264 ハードウェアエンコーダーを専有しないため、複数の録画を同時に実行したいとき有用かもしれない。
pi@pirite:~ $ ffmpeg -input_format mjpeg -i /dev/video0 -c:v copy testshot.mkv
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 392580 49004 397828 0 0 0 0 84 63 0 0 100 0 0
0 0 0 392580 49004 397828 0 0 0 0 81 57 0 0 100 0 0
1 0 0 391572 49004 397828 0 0 0 0 143 129 1 1 98 0 0 ← 起動
0 0 0 344416 49004 397828 0 0 0 0 421 262 11 2 87 0 0
0 0 0 344048 49004 399528 0 0 0 0 1200 744 2 1 97 0 0
0 0 0 341856 49004 401984 0 0 0 0 1250 824 1 1 98 0 0
0 0 0 339588 49004 404284 0 0 0 0 1225 789 1 1 98 0 0
0 0 0 337068 49004 406924 0 0 0 0 1221 792 1 1 98 0 0
0 0 0 334800 49004 409264 0 0 0 44 1236 815 1 0 98 0 0
0 0 0 332280 49004 411824 0 0 0 0 1223 786 1 0 98 0 0
3 0 0 329760 49004 414204 0 0 0 0 1258 811 1 1 98 0 0
0 0 0 327240 49004 416464 0 0 0 0 1224 786 0 1 99 0 0
0 0 0 361764 49004 431380 0 0 0 0 141 94 0 1 99 0 0 ← 終了
0 0 0 361772 49004 431380 0 0 0 0 112 81 0 0 100 0 0
0 0 0 361772 49004 431380 0 0 0 0 72 58 0 0 100 0 0
この記事は Raspberry Pi 3 B+ 上の以下の環境で作業を行っている。またすべての操作はコマンドライン (CUI) 上で行うことを想定している。
$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description: Raspbian GNU/Linux 11 (bullseye)
Release: 11
Codename: bullseye
$ uname -a
Linux pirite 5.10.89-v7+ #1508 SMP Tue Jan 4 19:51:16 GMT 2022 armv7l GNU/Linux