私はずっとWindowsの世界で生きてきたので、Linux系のOSには馴染みがありません。また電子回路も素人なので、間違った記載があるかもしれません。あらかじめご了承ください。
ラズパイに接続したWebカメラで動画を撮影して保存するプログラムです。
使用するWebカメラは Logicool C922n PRO です。
- タクトスイッチを押すと撮影を開始し、もう一度押すと撮影を終了します
- 起動中はLED(緑)が点灯します
- 録画中はLED(赤)が点灯します
- 動画撮影部分はffmpegを使用しています
- Ctrl+Cで停止します
- Raspberry Pi 4 Model B 4GB
- Logicool C922n PRO HDストリーム ウェブカメラ
- 抵抗入りLED
- タクトスイッチ
- ブレッドボード
- ジャンプワイヤー数本
- GPIO20 (Pin#38) -> LEDアノード(+) … パイロットランプ
- GPIO26 (Pin#37) -> LEDアノード(+) … 録画ランプ
- GND (Pin#39) -> LEDカソード(-)
- GPIO21 (Pin#40) -> タクトスイッチ端子1
- 3.3V (Pin#17) -> タクトスイッチ端子2
sample_movie.mp4
- Raspberry Pi 4 Model B 4GB
- Raspberry Pi OS 11 (bullseye)
- Python 3.9.17
- pigpio 1.78
- ffmpeg 4.3.6
- Visual Studio Code 1.76.0
- pyenv 2.3.24
- main.py … メインモジュール
- video.py … 録画用クラス
- audio_device.py … オーディオデバイス・ユーティリティクラス
実行には以下のモジュールが必要です。インストールされていない場合はあらかじめインストールしておいてください。
- pigpio
- ffmpeg
プログラムを実行する前にpigpioのデーモンを起動しておく必要があります。pigpioのインストールとデーモンの起動についてはここを参照してください。
ffmepgのインストールは次のようにします。
$ sudo apt-get install ffmpeg
main.pyの先頭部分にある定数を必要に応じて書き換えてください。
利用している環境に合わせて以下の定数を書き換えます。
PILOT_LED_PIN = 26 # LEDのGPIO番号
VIDEO_LED_PIN = 20 # 録画LEDのPIN番号
SWITCH_PIN = 21 # タクトスイッチのGPIO番号
利用している環境に合わせて以下の定数を書き換えます。
VIDEO_DEVICE = "/dev/video0" # ビデオデバイス名
AUDIO_CARD = "Webcam [C922 Pro Stream Webcam]" # 録音デバイスのカード名
OUTPUT_FILE = "./output/movie.mp4" # 出力ファイルパス
VIDEO_DEVICEとAUDIO_CARDは、ラズパイに接続している録画録音デバイスが1つの場合は特に変更する必要はないと思います(Webカメラ1台だけが繋がっているような場合)。録音デバイスを表すAUDIO_CARD定数には既定でC922用の識別子が書かれていますが、この識別子が見つからなかった場合は、サーチして最初に見つかった録音デバイスを使用します。
録音デバイスの名前は以下のコマンドで調べることができます。
$ arecord -l
実行例は以下の通りです。カード番号の次に書かれているのがデバイス名(カードID)です。
**** ハードウェアデバイス CAPTURE のリスト ****
カード 3: Webcam [C922 Pro Stream Webcam], デバイス 0: USB Audio [USB Audio]
サブデバイス: 1/1
サブデバイス #0: subdevice #0
ラズパイのターミナルを開き、プログラムをクローンしたいディレクトリに移動し、次のコマンドを実行します。
git clone https://github.com/led-mirage/Raspi4-WebCam-Recorder.git
以下のコマンドを実行するとプログラムが開始します。
python main.py
タクトスイッチを押すと録画が始まり、もう一度押すと録画が停止します。録画中はLEDが点灯します。
ターミナルで「Ctrl + C」を押すとプログラムが停止します。
独自のsystemdサービスを作成することで、このプログラムをラズパイが起動したときに自動実行させることができます。以下に手順を示します。
/etc/systemd/system/webcam-recorder.serviceを作成して、以下の内容を保存します。
[Unit]
Description=WebCam Recorder
[Service]
ExecStart=/usr/bin/python /home/username/webcam/main.py
WorkingDirectory=/home/username/webcam/
Restart=always
User=username
[Install]
WantedBy=multi-user.target
ファイル中の以下の個所はご自身の環境に合わせて書き換えてください。
- /usr/bin/python … Pythonの実行ファイルへのパス
- /home/username/webcam/ … プログラムを配置したフォルダ
- username … ご自身のユーザー名
サービスファイルを作成・変更するには管理者権限が必要です。下記例のように管理者権限でテキストエディタを起動し編集してください。
$ sudo nano /etc/systemd/system/webcam-recorder.service
CUIが苦手な方は、Visual Studio Codeでも編集できます。サービスファイルをVS Codeで開くと、保存時に管理者権限で保存するかを尋ねられます。私はこの方法の方が楽だと感じました。
サービスファイルを作成したら、systemdに新しいサービスファイルを認識させるために、次のコマンドを実行します。
$ sudo systemctl daemon-reload
次のコマンドでサービスを自動起動するように設定します。
$ sudo systemctl enable webcam-recorder.service
サービスをすぐに開始するには、以下のコマンドを実行します。
$ sudo systemctl start webcam-recorder.service
もしサービスを停止したい場合は、以下のコマンドを実行します。
$ sudo systemctl stop webcam-recorder.service
もしサービスの自動実行をやめたい場合は、以下のコマンドを実行します。
$ sudo systemctl disable webcam-recorder.service
動画の知識もなく、ffmpegにも詳しくないので、ffmpegのオプション構成にとても苦労しました。正直、今でもこれでいいのかもわかりません。試行錯誤の果てにたどり着いたのが、下の現状のオプション構成です。
command = ["ffmpeg",
"-y", # ファイル上書き
"-f", "v4l2", # ビデオデバイスの入力形式をVideo4Linux2に設定
"-input_format", "mjpeg", # 入力ビデオのフォーマットをMotionJPEGに設定
#"-framerate", "30", # 入力ビデオのフレームレートを30fpsに設定
"-thread_queue_size", "81920", # 入力ビデオのキューのサイズを設定
"-s", "1280x720", # 入力ビデオの解像度を1280x720に設定
#"-s", "1920x1080", # 入力ビデオの解像度を1920x1080に設定
"-i", video, # 入力ビデオのデバイスを設定
"-f", "alsa", # オーディオデバイスの入力形式をALSAに設定
"-thread_queue_size", "819200", # 入力オーディオのキューのサイズを設定
"-ac", "2", # 入力オーディオのチャンネル数を2に設定
"-i", device_text, # 入力オーディオのデバイスを設定
"-c:v", "h264_v4l2m2m", # 出力ビデオのコーデックをh264_v4l2m2mに設定
"-b:v", "4M", # 出力ビデオのビットレートを4Mbpsに設定
#"-r", "30", # 出力ビデオのフレームレートを30fpsに設定
"-c:a", "aac", # 出力オーディオのコーデックをAACに設定
"-b:a", "128k", # 出力オーディオのビットレートを128kbpsに設定
"-pix_fmt", "yuv420p", # 出力ビデオのピクセルフォーマットをyuv420pに設定
self.output_file]
この中でポイントなるのは、出力動画のコーデックにハードウェアエンコーダー(h264_v4l2m2m)と、入力ビデオフォーマットに mjpeg を指定した点でしょうか。
最初、コーデックに h264_omx を使おうとしたのですがうまくいかず、続いてソフトウェアエンコーダーの libx264 も試したのですが満足いく結果が得られなかったので h264_v4l2m2m を採用しました。
入力ビデオフォーマットに関しては、yuyv422 も試したのですが FPS が mjpeg の3分の1程度しか出ないので、最終的には mjpeg を採用しました。
また、この設定で1時間録画するとファイルサイズは1.8GB程度になります。ファイルサイズを押さえたい場合は、ビットレートを現状より低く設定すればOKです。ただし、トレードオフとして画質が落ちます。反対に画質を上げたい場合はビットレートを高く設定してください。
解像度に関しては1980x1080を使いたいところではあるのですが、下記に挙げるメモリの問題があるので、ここでは 1280x720 を採用しています。10分程度の短い動画しかとらないか、ラズパイの8GBモデルを使用しているのであれば 1920x1080 を採用してもいいと思います。
録画中は時間の経過とともにメモリの消費量が増えていきます。そのため、長時間録画するとメモリ不足になってffmpegが強制終了します。ffmpeg単体で動かしても同じ現象が発生するため、ffmpegのオプションの選び方が悪いのかもしれません。
解像度を1920x1080で録画すると30分程度でメモリがなくなります(使用しているラズパイのメモリは4GB)。1280x720でも1時間くらいが限界かなと思います。録画開始前と、録画開始1時間後のメモリの状態を以下に示します。
$ free -m
total used free shared buff/cache available
Mem: 3794 264 1175 44 2354 3410
Swap: 99 99 0
$ free -m
total used free shared buff/cache available
Mem: 3794 3177 74 44 541 496
Swap: 99 84 15
1時間経過するとほとんどメモリが80%ほど消費されています。あとしばらくすると、ffmpegがエラーをはいて終了します。
最後に、作ってみて、実際に使ってみての感想をいくつか挙げておきたいと思います。
良い点
- 手作り感が楽しい
- 手持ちのWebカメラとラズパイで遊べる
残念な点
- 画質がイマイチなのは否めない
- 屋外で撮影すると背景が白飛びしがち
- オートホワイトバランスのため、意図しない色調になる場合がある
単純に機能面だけを捉えるならスマホの方がはるかに上です。これを作ってみると、スマホの偉大さがよくわかります。ただ、こちらにはこちらの良さがありますので、プログラミングが好きな方や、手作り感に良さを感じる方は、試しに作ってみると楽しいと思います。苦労も多いですが、発見も多いです。
それではまた。
- ファーストリリース