Note

Hello, welcome to the SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Community on Facebook! Dive deeper into Raspberry Pi, Arduino, and ESP32 with fellow enthusiasts.

Why Join?

  • Expert Support: Solve post-sale issues and technical challenges with help from our community and team.

  • Learn & Share: Exchange tips and tutorials to enhance your skills.

  • Exclusive Previews: Get early access to new product announcements and sneak peeks.

  • Special Discounts: Enjoy exclusive discounts on our newest products.

  • Festive Promotions and Giveaways: Take part in giveaways and holiday promotions.

👉 Ready to explore and create with us? Click [here] and join today!

3.2 Video Module

Introduction

Beyond taking photos, the Raspberry Pi Camera Module also allows you to record high-quality videos. This project demonstrates how to set up the camera module to record videos with configurable resolution, frame rate, and bitrate, saving them directly to your Raspberry Pi.


What You’ll Need

Below are the components required for this project:

COMPONENT INTRODUCTION

PURCHASE LINK

Camera Module

BUY

Fusion HAT+

-

Raspberry Pi

-


Experimental Procedures

  1. To use camera module conveniently, Assemble the Pan-tilt (For Camera) is recommended.

    Note

    Assembling the pan-tilt may obscure some pins, so it is recommended to assemble it only when using the camera, or place it on the outside after assembly.

    ../_images/gimbal_assemble.png
  2. Access the Raspberry Pi Desktop:

  3. Open a Terminal and navigate to the project folder:

    cd ~/ai-lab-kit/python
    
  4. Run the video recording script:

    sudo python3 3.2_Video.py
    
  5. After the script starts, press the Fusion HAT+ USR button to control recording.

    ../_images/3.1_user_button1.png
    • First press → Start recording with a live preview.

    • Second press → Stop recording.

    • Videos are saved in your ~/Videos directory with names like: video_20250101_153045.mp4.

    • Press Ctrl+C in the terminal to exit the program.

    ../_images/3.2_record_video.png

Note

QT preview requires a desktop environment. If the preview cannot be started (for example, over SSH), the camera can still capture and save photos normally.


Code

Below is the Python code used for this project:

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

Understanding the Code

Understanding the Code

  1. Imports and Purpose

    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
    

    These modules are used for:

    • os: reading environment variables (e.g., DISPLAY, SUDO_USER) and building file paths.

    • time: generating timestamped filenames and keeping the program running in a loop.

    • pwd: finding the real user’s home directory when the script is run with sudo.

    • Picamera2 / Preview: controlling the camera pipeline and preview modes.

    • H264Encoder: encoding video frames into H.264 format.

    • FfmpegOutput: saving the encoded stream into an .mp4 file using FFmpeg.

    • UserButton: reading the Fusion HAT+ USR button and binding a click callback.

  2. Selecting the Correct Save Directory (Works with 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)
    

    This ensures videos are saved in the real user’s ~/Videos folder:

    • When run with sudo, SUDO_USER points to the original user.

    • pwd.getpwnam(...).pw_dir returns that user’s home directory.

    • If not using sudo, it falls back to the current user’s home (~).

    • ~/Videos is created if it doesn’t exist.

  3. Camera and Video Encoder Initialization

    camera = Picamera2()
    camera.configure(camera.create_video_configuration(main={"size": (800, 600)}, controls={"FrameRate": 30}))
    encoder = H264Encoder(bitrate=10_000_000)
    
    • create_video_configuration(...) sets up the camera pipeline for recording video.

    • size=(800, 600) chooses the recording resolution.

    • FrameRate=30 records at 30 FPS.

    • H264Encoder compresses the stream to H.264 at ~10 Mbps.

  4. Recording State Variables

    is_recording = False
    output = None
    preview_started = False
    
    • is_recording tracks whether recording is currently active.

    • output stores the FfmpegOutput object to keep it alive while recording.

    • preview_started records whether a preview window was successfully started, so we only stop preview if it actually exists.

  5. Starting a Recording

    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}")
    

    When recording starts:

    • A timestamped filename is generated (e.g., video_20260203_154500.mp4).

    • A new FfmpegOutput is created for that file.

    • camera.start_recording(...) begins encoding and writing the video stream.

  6. Stopping a Recording

    def stop_recording():
       """Stop recording safely."""
       global is_recording, output
       camera.stop_recording()
       is_recording = False
       output = None
       print("\nStop recording.")
    

    When recording stops:

    • camera.stop_recording() finalizes the output file correctly.

    • The output reference is cleared so the object can be released.

  7. USR Button Toggle Control

    def toggle():
        if not is_recording:
            start_recording()
        else:
            stop_recording()
    
    UserButton().set_on_click(toggle)
    

    Each press of the Fusion HAT+ USR button toggles recording:

    • First press → start recording.

    • Second press → stop recording.

  8. Preview Logic (Works With or Without a Screen)

    if os.getenv("DISPLAY"):
        try:
            camera.start_preview(Preview.QT)
            preview_started = True
        except Exception:
            preview_started = False
    
    • If a desktop GUI is available (DISPLAY is set), the script attempts a QT preview.

    • If no screen/GUI is available (such as SSH without X11), it skips preview safely and recording still works normally.

  9. Main Loop and Clean Exit (Ctrl+C)

    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() starts the camera pipeline.

    • The loop keeps the script running so it can respond to button presses.

    • When Ctrl+C is pressed, the finally block runs: - Stops recording if it is active (prevents corrupted video files). - Stops preview only if it was started (prevents preview-related errors). - Stops and closes the camera to release hardware resources cleanly.


Troubleshooting

  1. No Preview Window

    • Cause: Running without a desktop environment or missing Picamera2 GUI support.

    • Fix: Use Raspberry Pi Desktop/VNC and install Picamera2:

    sudo apt install -y python3-picamera2
    
  2. USR Button Not Working

    • Cause: Fusion HAT+ not connected or insufficient permissions.

    • Fix: Reseat the HAT, run the script with sudo, and print debug logs in toggle_recording() if needed.

  3. Video Not Saved

    • Cause: Incorrect home directory when using sudo or missing ~/Videos folder.

    • Fix: Check REAL_USER and VIDEOS_DIR values. Ensure ~/Videos exists and is writable.

  4. Recording Starts/Stops Unexpectedly

    • Cause: Camera in use by another process or system under heavy load.

    • Fix: Close other camera apps, reboot the Pi, and ensure ffmpeg is installed.

  5. File Not Finalized

    • Cause: Script terminated abruptly before stopping recording.

    • Fix: Always exit with Ctrl+C so the script can stop recording properly.


Conclusion

This script lets you start and stop video recording using the Fusion HAT+ USR button while viewing a live preview. Videos are saved automatically to ~/Videos with timestamped filenames.

It provides a simple foundation for button-controlled video capture and can be extended for DIY cameras, classroom demos, or sensor-triggered recording projects.