4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ラズパイに繋いだWEBカメラの映像を HLS 形式で配信してブラウザで再生する

Posted at

ラズパイに繋いだ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_flagsdelete_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 ライブラリになります。
※ソースは、こちらのサイトから拝借して手を加えました。

index.html
<!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 の公式サイトを撮影しているところです。

image.png

30秒程度の遅延が発生していることも確認できたため改善の余地は沢山ありそうですが、
ひとまず、カメラ映像をブラウザで再生させるところまでは実現することができました。

注意点

SDカード内に大量の動画ファイルが作成されては消されていくため、やりすぎるとSDカードの寿命が縮まり壊れる可能性があります。
対策として「ラズパイ本体のメモリー領域であるRAMに保存する」方法がありますので、興味のある方は試してみて下さい。

ハマったところ

ffmpeg のオプションで -c:v libx264 としている箇所ですが、当初、-c:v copy として「入力フォーマット(今回だとYUYV422)でそのまま出力する」ようにしていました。

この場合、きちんと .ts ファイルも作られて一見すると問題無さそうに見えたのですが、ブラウザからアクセスした際、いつまで経っても再生が始まらないという挙動になっていました。

そこで、HLS について調査を行ったところ「H.264 にエンコードする必要がある」ことが分かり、最終的に-c:v libx264 に辿り着きました。
ただし、エンコードを行っている分 .ts ファイルの作成にその分の時間がかかってしまっています。。

参考ページ

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?