ラズパイに繋いだWEBカメラの映像を HLS 形式で配信してブラウザで再生する検証を行ったので、その際の手順などを残しておきたいと思います。
構成
- Raspberry Pi 3B+
- WEBカメラ(USBカメラ)
- ラズパイに接続します
- 中古ショップで300円くらいで購入したものです
- Windows 10 Pro
- クライアント機(配信された映像の再生用)
OS のディストリビューションとバージョン
$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description: Raspbian GNU/Linux 11 (bullseye)
Release: 11
Codename: bullseye
カーネル
$ uname -a
Linux raspberrypi 5.15.84-v7+ #1613 SMP Thu Jan 5 11:59:48 GMT 2023 armv7l GNU/Linux
カメラの対応フォーマットと解像度
解像度がとても低く、対応フォーマットも非常に少ないですね。
v4l2-ctl -d /dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'MJPG' (Motion-JPEG, compressed)
Size: Discrete 160x120
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 176x144
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 320x240
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 352x288
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
[1]: 'YUYV' (YUYV 4:2:2)
Size: Discrete 160x120
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 176x144
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 320x240
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 352x288
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Interval: Discrete 0.040s (25.000 fps)
Interval: Discrete 0.050s (20.000 fps)
Interval: Discrete 0.067s (15.000 fps)
Interval: Discrete 0.100s (10.000 fps)
Interval: Discrete 0.200s (5.000 fps)
検証
1. ffmpeg のインストール
ラズパイで以下のコマンドを実行して ffmpeg をインストールします。
$ sudo apt -y install ffmpeg
今回使用したバージョンは以下の通りです。
$ ffmpeg -version
ffmpeg version 4.3.5-0+deb11u1+rpt3 Copyright (c) 2000-2022 the FFmpeg developers
built with gcc 10 (Raspbian 10.2.1-6+rpi1)
configuration: --prefix=/usr --extra-version=0+deb11u1+rpt3 --toolchain=hardened --incdir=/usr/include/arm-linux-gnueabihf --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-mmal --enable-neon --enable-rpi --enable-v4l2-request --enable-libudev --enable-epoxy --enable-pocketsphinx --enable-libdc1394 --enable-libdrm --enable-vout-drm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared --libdir=/usr/lib/arm-linux-gnueabihf --cpu=arm1176jzf-s --arch=arm
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
libpostproc 55. 7.100 / 55. 7.100
2. ffmpeg 使って HLS ファイルを作成する
HLS 配信を行う際は、H.264 エンコード済みの動画を HLS 用の .ts
ファイルに分割し、連番を付与しつつ .m3u8
プレイリストを作成する必要があります。
今回は stream
というディレクトリを作成し、そのディレクトリ内に .m3u8
と .ts
ファイルを作成するようにしました。
$ mkdir stream
$ ffmpeg -input_format yuyv422 -video_size 352x288 -framerate 30 -i /dev/video0 -f hls -c:v libx264 -hls_time 1 -hls_list_size 10 -hls_flags delete_segments stream/out.m3u8
以下、ffmpeg 実行時のオプション値の説明になります。
今回のように、事前にカメラの対応フォーマットや解像度を確認しておくと便利だと思います。
-
-input_format
:入力フォーマット ※事前に確認したYUYV
を指定 -
-video_size
:入力動画の解像度 -
-framerate
:入力動画のフレームレート -
-i
:入力デバイス -
-f
:出力フォーマット -
-c:v
:出力ビデオのエンコード ※H.264 を指定 -
-hls_time
:.ts
ファイルのセグメントの長さ ※今回は 1秒ごとに分割 -
-hls_list_size
:.ts
ファイルをプレイリスト上に保持する個数 -
-hls_flags
:delete_segments
を指定して、古い.ts
ファイルを削除 -
stream/out.m3u8
:出力されるプレイリストのパス(名称)
コマンド実行すると、stream
ディレクトリ内に以下のようなファイルが作成されます。
※余談ですが、ffmpeg の実行を停止する場合は、キーボードの「q」キーで停止できます。
$ ls -lh stream/
合計 2.3M
-rw-r--r-- 1 pi pi 352 1月 26 22:58 out.m3u8
-rw-r--r-- 1 pi pi 208K 1月 26 22:57 out10.ts
-rw-r--r-- 1 pi pi 203K 1月 26 22:57 out11.ts
-rw-r--r-- 1 pi pi 209K 1月 26 22:58 out12.ts
-rw-r--r-- 1 pi pi 194K 1月 26 22:58 out13.ts
-rw-r--r-- 1 pi pi 206K 1月 26 22:56 out3.ts
-rw-r--r-- 1 pi pi 211K 1月 26 22:56 out4.ts
-rw-r--r-- 1 pi pi 213K 1月 26 22:57 out5.ts
-rw-r--r-- 1 pi pi 211K 1月 26 22:57 out6.ts
-rw-r--r-- 1 pi pi 212K 1月 26 22:57 out7.ts
-rw-r--r-- 1 pi pi 211K 1月 26 22:57 out8.ts
-rw-r--r-- 1 pi pi 211K 1月 26 22:57 out9.ts
out.m3u8
の中身は以下のようになっていました。
$ cat stream/out.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:4
#EXTINF:8.333333,
out4.ts
#EXTINF:8.333333,
out5.ts
#EXTINF:8.333333,
out6.ts
#EXTINF:8.333333,
out7.ts
#EXTINF:8.333333,
out8.ts
#EXTINF:8.333333,
out9.ts
#EXTINF:8.333333,
out10.ts
#EXTINF:8.333333,
out11.ts
#EXTINF:8.333333,
out12.ts
#EXTINF:7.833333,
out13.ts
#EXT-X-ENDLIST
3. カメラ映像確認用の HTML ページを作成する
クライアント機のブラウザからアクセスする用の HTML ページを作成し、WEB サーバを立てます。
ラズパイで、以下のHTMLファイルを作成し stream
ディレクトリと同階層に配置します。
HTMLファイル内に登場する hls.js は、HLSクライアントの実装となる JavaScript ライブラリになります。
※ソースは、こちらのサイトから拝借して手を加えました。
<!DOCTYPE html>
<html>
<head>
<title>カメラ映像の配信</title>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>
<body>
<h1>HLSのサンプル</h1>
<video id="sampleVideo" controls></video>
<script>
var video = document.getElementById('sampleVideo');
var videoSrc = '/hoge/out.m3u8';
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
}
</script>
</body>
</html>
ファイル・ディレクトリの位置関係を確認しておきます。
$ tree .
.
├── index.html
└── stream
├── out.m3u8
├── out10.ts
├── out11.ts
├── out12.ts
├── out13.ts
├── out3.ts
├── out4.ts
├── out5.ts
├── out6.ts
├── out7.ts
├── out8.ts
└── out9.ts
WEB サーバはどのような方法で立てても良いのですが、Python の http.server
を使って WEB サーバを立てました。
$ python -m http.server 8000 --bind 0.0.0.0 --directory .
4. クライアント機のブラウザから HTML ページを表示する
ラズパイで WEB サーバを立てたら、クライアント機のブラウザからアクセスしてみます。
http://<ラズパイのIPアドレス>:8000/
でアクセスできます。
静止画なので非常に分かりづらいと思いますが、きちんとカメラ映像を再生できていることを確認できました。
※カメラで ffmpeg の公式サイトを撮影しているところです。
30秒程度の遅延が発生していることも確認できたため改善の余地は沢山ありそうですが、
ひとまず、カメラ映像をブラウザで再生させるところまでは実現することができました。
注意点
SDカード内に大量の動画ファイルが作成されては消されていくため、やりすぎるとSDカードの寿命が縮まり壊れる可能性があります。
対策として「ラズパイ本体のメモリー領域であるRAMに保存する」方法がありますので、興味のある方は試してみて下さい。
ハマったところ
ffmpeg のオプションで -c:v libx264
としている箇所ですが、当初、-c:v copy
として「入力フォーマット(今回だとYUYV422)でそのまま出力する」ようにしていました。
この場合、きちんと .ts
ファイルも作られて一見すると問題無さそうに見えたのですが、ブラウザからアクセスした際、いつまで経っても再生が始まらないという挙動になっていました。
そこで、HLS について調査を行ったところ「H.264 にエンコードする必要がある」ことが分かり、最終的に-c:v libx264
に辿り着きました。
ただし、エンコードを行っている分 .ts
ファイルの作成にその分の時間がかかってしまっています。。
参考ページ
-
ubuntu 21.10(arm64) + Raspberry Pi 4環境における動画ストリーミング配信(HLS編)
- 今回の検証はこちらを参考にして行いました。とても参考になりました。