.. include:: /index.rst
:start-after: start_hello_message
:end-before: end_hello_message
.. _py_video:
3.2 ビデオモジュール
=====================
**はじめに**
Raspberry Piカメラモジュールは、写真撮影だけでなく高品質な動画の録画にも対応しています。このプロジェクトでは、カメラモジュールを設定し、解像度、フレームレート、ビットレートを調整しながら動画を録画し、Raspberry Piに直接保存する方法を紹介します。
----------------------------------------------
**必要なもの**
このプロジェクトに必要なコンポーネントは以下のとおりです。
.. list-table::
:widths: 30 20
:header-rows: 1
* - COMPONENT INTRODUCTION
- PURCHASE LINK
* - :ref:`cpn_camera_module`
- |link_camera_buy|
* - :ref:`cpn_fusion_hat`
- \-
* - Raspberry Pi
- \-
----------------------------------------------
**実験手順**
#. カメラモジュールを便利に使用するために、:ref:`assemble_fusion_hat_pan_tilt` を推奨します。
.. note::
パンチルトを組み立てると一部のピンが隠れてしまう場合があるため、カメラを使用するときだけ組み立てるか、組み立て後に外側へ配置することをおすすめします。
.. image:: ../quick_start/img/gimbal_assemble.png
#. Raspberry Piのデスクトップにアクセスします。
* :ref:`remote_desktop`: **VNC** を使用してフルデスクトップ環境にアクセスします。
* |link_rpi_connect|: **Raspberry Pi Connect** を使用して、任意のブラウザから安全にPiへアクセスします。
#. ターミナルを開き、プロジェクトフォルダへ移動します。
.. raw:: html
.. code-block:: shell
cd ~/ai-lab-kit/python
#. 動画録画スクリプトを実行します。
.. raw:: html
.. code-block:: shell
sudo python3 3.2_Video.py
#. スクリプトの起動後、 **Fusion HAT+ のUSRボタン** を押して録画を操作します。
.. image:: img/3.1_user_button.png
:width: 50%
* 1回目の押下 → **ライブプレビュー付きで録画開始**
* 2回目の押下 → **録画停止**
* 動画は ``~/Videos`` ディレクトリに、次のような名前で保存されます。
``video_20250101_153045.mp4``
* プログラムを終了するには、ターミナルで ``Ctrl+C`` を押します。
.. image:: img/3.2_record_video.png
:width: 70%
.. note::
QTプレビューを使用するにはデスクトップ環境が必要です。
たとえばSSH経由などでプレビューを開始できない場合でも、
カメラは通常どおり動画を記録して保存できます。
----------------------------------------------
**コード**
以下は、このプロジェクトで使用するPythonコードです。
.. raw:: html
.. code-block:: python
import os
import time
import pwd
from picamera2 import Picamera2, Preview
from picamera2.encoders import H264Encoder
from picamera2.outputs import FfmpegOutput
from fusion_hat.user_button import UserButton
# ----- Paths (works correctly even when using sudo) -----
sudo_user = os.getenv("SUDO_USER")
home = pwd.getpwnam(sudo_user).pw_dir if sudo_user else os.path.expanduser("~")
videos_dir = os.path.join(home, "Videos")
os.makedirs(videos_dir, exist_ok=True)
# ----- Camera / encoder -----
camera = Picamera2()
camera.configure(camera.create_video_configuration(main={"size": (800, 600)}, controls={"FrameRate": 30}))
encoder = H264Encoder(bitrate=10_000_000)
is_recording = False
output = None
preview_started = False
def start_recording():
"""Start recording to a timestamped MP4 file."""
global is_recording, output
ts = time.strftime("%Y%m%d_%H%M%S")
path = os.path.join(videos_dir, f"video_{ts}.mp4")
output = FfmpegOutput(path) # keep a reference while recording
camera.start_recording(encoder, output)
is_recording = True
print(f"\nStart recording: {path}")
def stop_recording():
"""Stop recording safely."""
global is_recording, output
camera.stop_recording()
is_recording = False
output = None
print("\nStop recording.")
def toggle():
"""USR button callback: toggle start/stop."""
if not is_recording:
start_recording()
else:
stop_recording()
# ----- Button -----
UserButton().set_on_click(toggle)
# ----- Preview (only if a GUI display is available) -----
if os.getenv("DISPLAY"):
try:
camera.start_preview(Preview.QT)
preview_started = True
except Exception:
preview_started = False # continue without preview
# ----- Run -----
camera.start()
print("Press USR to START/STOP recording. Ctrl+C to exit.")
try:
while True:
time.sleep(0.1)
except KeyboardInterrupt:
pass
finally:
# Stop recording if still active
if is_recording:
try:
camera.stop_recording()
except Exception:
pass
# Stop preview only if it was started
if preview_started:
try:
camera.stop_preview()
except Exception:
pass
try:
camera.stop()
except Exception:
pass
try:
camera.close()
except Exception:
pass
----------------------------------------------
**コードの解説**
**コードの解説**
1. **インポートと役割**
.. code-block:: python
import os
import time
import pwd
from picamera2 import Picamera2, Preview
from picamera2.encoders import H264Encoder
from picamera2.outputs import FfmpegOutput
from fusion_hat.user_button import UserButton
これらのモジュールは次の用途で使用されます。
- ``os``: 環境変数( ``DISPLAY`` や ``SUDO_USER`` など)の取得やファイルパスの生成に使用します。
- ``time``: タイムスタンプ付きのファイル名を生成し、ループ内でプログラムを継続動作させるために使用します。
- ``pwd``: スクリプトを ``sudo`` で実行した場合に、実際のユーザーのホームディレクトリを取得するために使用します。
- ``Picamera2`` / ``Preview``: カメラパイプラインとプレビュー機能を制御するために使用します。
- ``H264Encoder``: 動画フレームをH.264形式でエンコードするために使用します。
- ``FfmpegOutput``: エンコードされた映像をFFmpeg経由で ``.mp4`` ファイルとして保存するために使用します。
- ``UserButton``: Fusion HAT+ のUSRボタンを読み取り、クリック時のコールバック関数を関連付けます。
2. **正しい保存ディレクトリの選択(sudo対応)**
.. code-block:: python
sudo_user = os.getenv("SUDO_USER")
home = pwd.getpwnam(sudo_user).pw_dir if sudo_user else os.path.expanduser("~")
videos_dir = os.path.join(home, "Videos")
os.makedirs(videos_dir, exist_ok=True)
この処理により、動画は実際のユーザーの ``~/Videos`` フォルダに保存されます。
- ``sudo`` で実行した場合、 ``SUDO_USER`` が元のユーザー名を示します。
- ``pwd.getpwnam(...).pw_dir`` で、そのユーザーのホームディレクトリを取得します。
- ``sudo`` を使用していない場合は、現在のユーザーのホームディレクトリ( ``~`` )を使います。
- ``~/Videos`` が存在しない場合は自動的に作成します。
3. **カメラと動画エンコーダーの初期化**
.. code-block:: python
camera = Picamera2()
camera.configure(camera.create_video_configuration(main={"size": (800, 600)}, controls={"FrameRate": 30}))
encoder = H264Encoder(bitrate=10_000_000)
- ``create_video_configuration(...)`` は、動画録画用のカメラ設定を行います。
- ``size=(800, 600)`` は録画解像度を指定します。
- ``FrameRate=30`` は30 FPSで録画する設定です。
- ``H264Encoder`` は映像を約10 MbpsのH.264形式に圧縮します。
4. **録画状態を管理する変数**
.. code-block:: python
is_recording = False
output = None
preview_started = False
- ``is_recording`` は、現在録画中かどうかを管理します。
- ``output`` は、録画中に ``FfmpegOutput`` オブジェクトを保持するために使用します。
- ``preview_started`` は、プレビューウィンドウが正常に開始されたかを記録し、必要な場合のみ停止処理を行うために使用します。
5. **録画開始処理**
.. code-block:: python
def start_recording():
"""Start recording to a timestamped MP4 file."""
global is_recording, output
ts = time.strftime("%Y%m%d_%H%M%S")
path = os.path.join(videos_dir, f"video_{ts}.mp4")
output = FfmpegOutput(path) # keep a reference while recording
camera.start_recording(encoder, output)
is_recording = True
print(f"\nStart recording: {path}")
録画を開始すると、次の処理が行われます。
- タイムスタンプ付きのファイル名を生成します(例: ``video_20260203_154500.mp4``)。
- そのファイルに対応する ``FfmpegOutput`` を作成します。
- ``camera.start_recording(...)`` により、動画のエンコードと保存を開始します。
6. **録画停止処理**
.. code-block:: python
def stop_recording():
"""Stop recording safely."""
global is_recording, output
camera.stop_recording()
is_recording = False
output = None
print("\nStop recording.")
録画を停止すると、次の処理が行われます。
- ``camera.stop_recording()`` により、出力ファイルを正しく終了します。
- ``output`` の参照を解除し、オブジェクトを解放できるようにします。
7. **USRボタンによる録画切り替え**
.. code-block:: python
def toggle():
if not is_recording:
start_recording()
else:
stop_recording()
UserButton().set_on_click(toggle)
Fusion HAT+ のUSRボタンを押すたびに録画状態が切り替わります。
- 1回目の押下 → 録画開始
- 2回目の押下 → 録画停止
8. **プレビュー処理(画面あり/なし両対応)**
.. code-block:: python
if os.getenv("DISPLAY"):
try:
camera.start_preview(Preview.QT)
preview_started = True
except Exception:
preview_started = False
- デスクトップGUIが利用可能な場合( ``DISPLAY`` が設定されている場合)、QTプレビューを開始しようとします。
- 画面やGUIが利用できない場合(X11なしのSSHなど)は、プレビューを開始せず、安全にそのまま録画のみを続行します。
9. **メインループと安全な終了処理(Ctrl+C)**
.. code-block:: python
camera.start()
try:
while True:
time.sleep(0.1)
except KeyboardInterrupt:
pass
finally:
if is_recording:
camera.stop_recording()
if preview_started:
camera.stop_preview()
camera.stop()
camera.close()
- ``camera.start()`` でカメラパイプラインを開始します。
- ループによってスクリプトを継続動作させ、ボタン入力を待ちます。
- ``Ctrl+C`` が押されると、 ``finally`` ブロックが実行されます。
- 録画中であれば録画を停止します(破損した動画ファイルを防ぎます)。
- プレビューが開始されていた場合のみ、プレビューを停止します。
- カメラを停止して閉じ、ハードウェアリソースを安全に解放します。
-----------
**トラブルシューティング**
1. **プレビューウィンドウが表示されない**
- **原因:** デスクトップ環境なしで実行している、またはPicamera2のGUIサポートが不足している可能性があります。
- **対処方法:** Raspberry Pi Desktop/VNCを使用し、Picamera2をインストールしてください。
.. raw:: html
.. code-block:: shell
sudo apt install -y python3-picamera2
2. **USRボタンが動作しない**
- **原因:** Fusion HAT+ が正しく接続されていない、または権限が不足している可能性があります。
- **対処方法:** HATを差し直し、 ``sudo`` でスクリプトを実行し、必要に応じて ``toggle_recording()`` にデバッグ出力を追加してください。
3. **動画が保存されない**
- **原因:** ``sudo`` 使用時にホームディレクトリが正しく解決されていない、または ``~/Videos`` フォルダが存在しない可能性があります。
- **対処方法:** ``REAL_USER`` と ``VIDEOS_DIR`` の値を確認してください。 ``~/Videos`` が存在し、書き込み可能であることを確認してください。
4. **録画が意図せず開始/停止する**
- **原因:** 他のプロセスがカメラを使用している、またはシステム負荷が高い可能性があります。
- **対処方法:** 他のカメラアプリを終了し、Raspberry Piを再起動し、 ``ffmpeg`` がインストールされていることを確認してください。
5. **ファイルが正常に確定されない**
- **原因:** 録画停止前にスクリプトが強制終了された可能性があります。
- **対処方法:** 必ず ``Ctrl+C`` で終了し、スクリプトが録画を正常に停止できるようにしてください。
------
**まとめ**
このスクリプトでは、Fusion HAT+ のUSRボタンを使って動画の録画開始・停止を行いながら、ライブプレビューを表示できます。録画された動画は、自動的にタイムスタンプ付きファイル名で ``~/Videos`` に保存されます。
ボタン操作による動画撮影のシンプルな基盤として活用でき、DIYカメラ、授業でのデモ、センサー連動型の録画プロジェクトなどへ発展させることができます。