Raspberry Pi 4B で CSI カメラ映像を配信する

やりたいこと

なるべく低遅延でカメラの映像を RTSP や WebRTC で配信したい。
画質は低画質でも良い。
閲覧には ID / Password で制限をかけたい。

購入したもの

今回、画質は全く求めていないので、とにかく安く!
と探したんだけれど、まさか 999 円(税込:2026/01/25)で売られているとは思わなかった…安すぎんだろ!w

Amazon.co.jp: KEYESTUDIO 5MP カメラ モジュール ウェブカメラ for Raspberry Pi ラズベリーパイ2 3 4 Model B+ 電子工作 電子部品 : パソコン・周辺機器
Amazon.co.jp: KEYESTUDIO 5MP カメラ モジュール ウェブカメラ for Raspberry Pi ラズベリーパイ2 3 4 Model B+ 電子工作 電子部品 : パソコン・周辺機器

尚、このカメラ、RaspberryPi は 2/3/4 だけでなく、Zeroにも対応し、さらに 5 用のケーブルまで付属しているので、1000円という価格が本当に信じられない…

さすがに CMOS が OV5647 なので、暗いところは超絶苦手だけど…
(普段、最低被写体照度 0.005 lx とか見てるので余計に映らない印象は出てしまうけど、まぁ、999 円だしw)

使用するハード/ソフト

Raspberry Pi 4B / 4GB
Ubuntu Server 24.04.3 LTS
CSI 接続カメラ … 今回購入品
V4L2(Video for Linux 2)… 確認用
MediaMTX 1.15.6 … 配信用
ffmpeg … いろいろ加工用

お決まりの処理を含めてv4l2インストール

MediaMTX だけ含まれていないけど、それでOK。MediaMTX は Git から頂いてくるので。

$ sudo apt update
$ sudo apt install v4l-utils ffmpeg

V4l をインストール出来たらカメラが認識されているか確認。
以下のコマンドを叩くと、ずらずらと表示されるはずで、その中で「video0」があればOK
※ただし、USB カメラも同時に繋げていたりすると、末尾の番号がズレるので注意。

$ ls /dev/video*

もう少し正確に videoX の番号を確認するなら以下。

$ ls -l /dev/v4l/by-path/
total 0
lrwxrwxrwx 1 root root 13 Aug  6 02:00 platform-bcm2835-codec-video-index0 -> ../../video18
lrwxrwxrwx 1 root root 13 Aug  6 02:00 platform-bcm2835-isp-video-index0 -> ../../video20
lrwxrwxrwx 1 root root 13 Aug  6 02:00 platform-bcm2835-isp-video-index1 -> ../../video14
lrwxrwxrwx 1 root root 13 Aug  6 02:00 platform-bcm2835-isp-video-index2 -> ../../video15
lrwxrwxrwx 1 root root 13 Aug  6 02:00 platform-bcm2835-isp-video-index3 -> ../../video23
lrwxrwxrwx 1 root root 12 Jan 26 19:09 platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.1:1.0-video-index0 -> ../../video1
lrwxrwxrwx 1 root root 12 Jan 26 19:09 platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.1:1.0-video-index1 -> ../../video2
lrwxrwxrwx 1 root root 12 Jan 26 19:09 platform-fd500000.pcie-pci-0000:01:00.0-usbv2-0:1.1:1.0-video-index0 -> ../../video1
lrwxrwxrwx 1 root root 12 Jan 26 19:09 platform-fd500000.pcie-pci-0000:01:00.0-usbv2-0:1.1:1.0-video-index1 -> ../../video2
lrwxrwxrwx 1 root root 12 Aug  6 02:00 platform-fe801000.csi-video-index0 -> ../../video0
lrwxrwxrwx 1 root root 13 Aug  6 02:00 platform-feb10000.codec-video-index0 -> ../../video19

下から2つ目に platform-fe801000.csi-video-index0 とある。
まぁ、CSI と書いてあるし、これだけでもわかるけど、fe801000 という表記も重要ね。
Raspberry Pi4 系だと CSI のベースアドレスにあたるので、この表記も併せて確認するとバッチリ。
で、それが video0 になっているので、video0 が目的のものとなる。

尚、ここでは USB 接続の Web カメラも繋げて試した。
fd500000 という表記になっている部分に usb なんちゃらが出てるので、これが Web カメラ。

ということで、videoX の情報もチョロっと見ておくといいかも。(今はそんなに重要じゃない)

$ v4l2-ctl -d /dev/video0 --all

MediaMTX のインストール

以下 Git からダウンロード。
mediamtx_v1.15.6_linux_arm64.tar.gz が該当。
 ※mediaMTX は更新が早いので、バージョン番号は変わってるかも。

Releases · bluenviron/mediamtx
Ready-to-use SRT / WebRTC / RTSP / RTMP / LL-HLS / MPEG-TS / RTP media server and media proxy that allows to read, publi...

とりあえずテストなので任意のディレクトリに解凍する。
今回はホームディレクトリ直下に mediaMtx というのを作ってその中に保存した。

$ tar zxfv mediamtx_v1.15.6_linux_arm64.tar.gz

って、ファイルが3つ解凍されるだけ。シンプル。

続けて設定ファイルを編集する。ファイル名は mediamtx.yml
とりあえず最後にある paths セクションを以下のようにして見た。

paths:
  camera:
    source: rpiCamera
    sourceProtocol: automatic

ちゃんと配信されるかどうかを確かめるだけならこれだけで良いので、早速実行してみる。

$ ./mediamtx

別端末からVLC 等で RTSP による映像受信ができればOK
VLC には下記のように指定する。

rtsp://[ラズパイのIPアドレス]:8554/camera

尚、この段階で WebRTC での配信もできるようになっているので、ブラウザからは下記のようにすると、カメラの映像が見られる。

http://[ラズパイのIPアドレス]:8889/camera/

しかもかなり低遅延でいい感じ。
すごいね、MediaMTX…

サービス化してラズパイ起動時に自動起動

本当なら、閲覧するには ID / Password の設定を、と先にやりたいところだけど、忘れがちなサービス化を先にする。

以下のように必要なファイルを作成する。

sudo vi /etc/systemd/system/mediamMtx.service
[Unit]
Description=MediaMTX Server
Wants=network.target
 
[Service]
ExecStart=/[path]/mediaMtx/mediamtx /[path]/mediaMtx/mediamtx.yml
Restart=always
RestartSec=5
 
[Install]
WantedBy=multi-user.target

ExecStart の [path] の部分は環境や使い方によって適宜変更してくださいな。

で、設定のリロードと起動、ラズパイ起動と同時にサービス開始。

$ sudo systemctl start mediaMtx.service
$ sudo systemctl status mediaMtx.service
$ sudo systemctl enable mediaMtx.service

こんな感じになっていればOK

$ sudo systemctl status mediaMtx.service
● mediaMtx.service - MediaMTX Server
     Loaded: loaded (/etc/systemd/system/mediaMtx.service; disabled; preset: enabled)
     Active: active (running) since Mon 2026-01-26 13:06:30 JST; 891ms ago
   Main PID: 3316 (mediamtx)
      Tasks: 9 (limit: 3853)
     Memory: 12.0M (peak: 12.4M)
        CPU: 1.679s
     CGroup: /system.slice/mediaMtx.service
             mq3316 /[path]/mediaMtx/mediamtx /[path]/mediaMtx/mediamtx.yml

Jan 26 13:06:30 rbp-cam systemd[1]: Started mediaMtx.service - MediaMTX Server.
Jan 26 13:06:30 rbp-cam mediamtx[3316]: 2026/01/26 13:06:30 INF MediaMTX v1.15.6
Jan 26 13:06:30 rbp-cam mediamtx[3316]: 2026/01/26 13:06:30 INF configuration loaded from /[path]/mediaMtx/mediamtx.yml
Jan 26 13:06:30 rbp-cam mediamtx[3316]: 2026/01/26 13:06:30 INF [path camera] [RPI Camera source] started
Jan 26 13:06:30 rbp-cam mediamtx[3316]: 2026/01/26 13:06:30 INF [RTSP] listener opened on :8554 (TCP), :8000 (UDP/RTP), :8001 (UDP/RTCP)
Jan 26 13:06:30 rbp-cam mediamtx[3316]: 2026/01/26 13:06:30 INF [RTMP] listener opened on :1935
Jan 26 13:06:30 rbp-cam mediamtx[3316]: 2026/01/26 13:06:30 INF [HLS] listener opened on :8888
Jan 26 13:06:30 rbp-cam mediamtx[3316]: 2026/01/26 13:06:30 INF [WebRTC] listener opened on :8889 (HTTP), :8189 (ICE/UDP)
Jan 26 13:06:30 rbp-cam mediamtx[3316]: 2026/01/26 13:06:30 INF [SRT] listener opened on :8890 (UDP)

ここまでで配信に必要な超最低限の設定は完了

完全ローカルで自分だけが閲覧するという環境なら、このままでもまぁあり。
あとは好きなように設定変えてあげればいいんじゃないかな。

とはいえ、やっぱり閲覧等は ID / Password で制限しておきたい

AI さんに聞くと最初に出てくるのはちょっと古いやり方だったw
それをいくら試しても No ID / No Pass で映像が見れてしまう…

で、普通に Web 検索で以下が出てきたので、試したらちゃんとできた…

編集するのは設定ファイルの Global settings -> Authentication セクション

authInternalUsers:
- user: [username]
  pass: [password]
  ips:                   # アクセスを許可するIPアドレスを指定できる
  - 127.0.0.1            # ローカルループバック:サーバ自身からのアクセスを居K
  - 192.168.50.0/24      # ローカルネットワークからのアクセスを許可
  - 192.168.60.0/24      # 同上
  permissions:
  - action: publish      # MediaMTXへ向けた動画のアップロードについて
    path: camera         # path:camera に対するアップロードを許可
  - action: read         # MediaMTXからの映像閲覧について
    path: camera         # path:camera に対する映像の閲覧を許可
  - action: playback     # これは read に変わってるので削除して良い
    path: camera         # 同上

こんな感じにしておくと、設定ファイルの最後にある paths セクションでかいた「camera」という path に対して制限がかけられる。

他のRTSP対応カメラもまとめて管理したい

最初は単純に「ラズパイの CSI 接続カメラ映像を配信できればいいや」と思っていたけど、MediaMTX 優秀すぎ…
他にも RTSP 対応カメラがあるのなら、本来の配信サーバとして活躍させられる。

編集するのは設定ファイルの Path settings -> Paths セクション

paths:
  camera:
    source: rpiCamera
    sourceProtocol: automatic

  parking:
    source: rtsp://[id]:[pw]@[ip]

  entrance:
    source: publisher
    runOnInit: |
      ffmpeg -i "rtsp://[id]:[pw]@[ip]/" -c:v copy -an \
             -f rtsp -rtsp_transport tcp "rtsp://[id]:[pw]@127.0.0.1:8554/entrance"
    runOnInitRestart: yes

  room:
    source: rtsp://[id]:[pw]@[ip]

  usbcam:
    source: publisher
    runOnInit: |
      bash -c 'sleep 3; ffmpeg -f v4l2 -video_size 640x480 -i /dev/video1 \
        -vf "format=yuv420p" -c:v h264_v4l2m2m -b:v 1M -an \
        -f rtsp "rtsp://[id]:[pw]@127.0.0.1:8554/usbcam"
    runOnInitRestart: yes

こんな感じで「parking」(駐車場)、「entrance」(玄関)、「room」(部屋:リビングとか)、「usbcam」と自宅や会社の中にありそうなカメラを集約してみた。

RTSP で映像を取得できなかったり、ちょっと癖ツヨカメラなんかもあったので、一旦 ffmpeg で映像を受け取って、ほにゃららして MediaMTX に投げるということもできる。

ここまでやると、「NVR 入れろよ…」となりそうなもんだけど、まぁ、ラズパイでできるってなら安いし、勉強になるしでいいんじゃないかな。

で、USBカメラは古いカメラだとちょっと厄介

設定ファイルから抜粋する下記部分、たどり着くまでになかなか時間がかかった。

  usbcam:
    source: publisher
    runOnInit: |
      bash -c 'sleep 3; ffmpeg -f v4l2 -video_size 640x480 -i /dev/video1 \
        -vf "format=yuv420p" -c:v h264_v4l2m2m -b:v 1M -an \
        -f rtsp "rtsp://[id]:[pw]@127.0.0.1:8554/usbcam"'
    runOnInitRestart: yes

-vf “format=yuv420p” として、エンコーダ h264_v4l2m2 が YUV420 しか受け付けない為。
USB カメラは YUV422 で出そうとする。
なので不一致で動かなかった。

bash -c ‘sleep 3; (ffmpegのコマンド) ‘
この部分がなかなか厄介だった。

MediaMTX を止めて、ffmpeg 単体で実行すると問題ない。
しかし、同じコマンドを設定ファイル内に書いたとたん

Error opening input: Device or resource busy

というメッセージが繰り返し現れる。

USB カメラのを見ていると、数秒点灯、数秒消灯、というのをずっと繰り返している。
最初は 5.0V 弱なテキトーACアダプタが犯人で、Under-voltage 状態になって USB カメラが起動した瞬間に USB カメラが落ちる、みたいなことが起こっているのかと思った。

けれど

$ sudo dmesg | grep -i voltage

としても電圧低下は確認できないし、

$ sudo dmesg | grep -i usb

でも、USB カメラが切断(Disconnect)されたなんてログはなかった。

じゃぁ、本当に busy でなにかが掴んでいるのかも!?

$ sudo fuser -v /dev/video1

としてみたけど、誰もいない。
ということでさらにどんなタイミングで掴む奴が出てくるかを見張ってみると…

$ watch -n 1 'sudo fuser -v /dev/video1'

Every 1.0s: sudo fuser -v /dev/video1                                   Mon Jan 26 21:00:17 2026

                     USER        PID ACCESS COMMAND
/dev/video1:         root       2248 F...m ffmpeg

みたいな感じで、掴む奴が現れた!
が、ffmpeg がつかみに行ってた…
しかしどうも MediaMTX も同時に USB カメラを初期化するのかなにかで掴みに行ってるっぽい。
なので、その直後に実行される ffmpeg が失敗する。busy で。

ということで、ffmpeg が実行されるまでに少し間を持たせてみた。
それが

bash -c ‘sleep 3; (ffmpegのコマンド) ‘

という部分。

一応、3秒も必要なくて1秒くらいでも問題ない様だったけど、3秒でも違和感なかったのでとりあえず3秒でしばらく使ってみようかと。

ただ…

まぁ…

ラズパイ1台で4台のカメラを面倒見させると、大変ねw

最終構成はこうなった

1台目:CSIカメラ:1920 x 1080 ……… これが本来の目的だったのにw
2台目:RTSP対応カメラ:3840 x 2160 ……… 低画質でいいとか言っておきながら4Kカメラw
3台目:RTSP対応カメラ:1920 x 1080
4台目:USB Webカメラ:640 x 480

この4本のストリームを Raspberry Pi 4B / 4GB で CPU はほぼ100%使って回せる。ギリだけど。

…というか、メモリはそんなに使わんのね。

で、これを録画させようとするとどうなるんだ!?w
ちょっとやってみたい。が、さすがに SD カードに録画とかはしたくないしできるとも思ってないので…
USB3.0 な SSD か、USB3.0 な 外付けHDDでも買ってみるか。
いや、その前に、もうラズパイがいっぱいいっぱいなんじゃないかな…?
(I/Oはまだコキ使えるか!?w)

この記事にコメントしてみる