🎞️

Rapberry PI Zero からWebRTCでリアルタイム動画配信

に公開

はじめに

raspberry pi zeroは、小型のボードコンピュータですが、ハードウェアエンコーダーを持っています。
これを使うことで、カメラモジュールで取り込んだ動画をハードウェアエンコーダーでH.264へエンコードし、RTP(リアルタイム転送プロトコル)を使って配信することができます。

RTPで配信される動画を見るためには、別途VideoLAN等の動画プレーヤーを用意することになりますが、今回はjanusを使いwebブラウザで参照できるようにすることをゴールとします。

また、今回は画質よりも、なるべく低遅延で配信できるようなパラメタ設定とします。

以下のようなシステムを構築します。

動作テスト( libcamera + ffmpeg )

まずは、raspberry piに接続した、カメラモジュールが正しく動作しているか、動作確認の意味も含めてwebRTCを使わない構成で動作確認を行います。

Raspberry PIのセットアップ

Raspberry PIは、SDカードにRaspberry Pi Imagerで適当なOSイメージを書き込みます。
Raspberry Pi ZeroにはGUIは重たく実用性が低いのと、512MBしかない貴重なメモリを少しでも節約したいので、ここではRaspberry Pi OS Lite(64-bit)を選択しています。

ビュワーのセットアップ

ビュワーとしてはVLCを使います。
https://images.videolan.org/vlc/index.ja.html

カメラモジュールのテスト

カメラモジュールの操作は、libcamera-vidで行います。

Raspberry pi側:

以下のコマンドで、クライアントからの接続を待ちます。

$ libcamera-vid -t 0 --inline --listen -o tcp://0.0.0.0:8888

VLC側:

メニューの「メディア」⇒「ネットワークストリームを開く」から「ネットワークプロトコル」に以下のようなアドレスを指定すると、カメラの画像が表示されます。

tcp/h264://192.168.1.10:8888   # IPアドレスにはRaspberry Piのアドレスを指定

ffmpegのテスト

次に、ffmpegのテストとして、ffmpegに組み込まれているテスト動画をRTPで送信して、VLCで受信してみます。

ffmpegのテスト動画

$ ffmpeg -f lavfi -readrate 1 -i testsrc2=size=768x432:rate=30 \
  -c:v h264_v4l2m2m -b:v 1M \
  -f rtp rtp://192.168.1.10:8004  # IPアドレスはPCのIPアドレスにします。

VLC側

以下のファイルをメモ帳で作成し、拡張子を.sdpとして保存します。
(IPアドレスはPCのIPアドレスに変更してください)

v=0
o=- 0 0 IN IP4 192.168.1.10
s=FFmpeg RTP Stream
c=IN IP4 192.168.1.10
t=0 0
m=video 8004 RTP/AVP 96
a=rtpmap:96 H264/90000

作成した.sdpファイルをvlcから開くと、以下のようなテスト動画が再生されます。

カメラ画像の配信

libcamera-vidで取り込んだカメラ動画を、パイプ(|)でffmpegに流し込むことでカメラ動画をRTP送信することができるようになります。

Raspberry PI側

$ libcamera-vid --hdr -n -t 0 --inline -o - --level 4.2 \
        --width 640 --height 480 \
        --denoise cdn_off \
        -b 1000K \
        --framerate 30 --flush 1  \
        | \                                    # パイプで動画ストリームをffmpegに流す
        ffmpeg  -i - -c:v copy -b:v 1000K \    # ffmpegでは何もしない
        -preset ultrafast -tune zerolatency \
        -bufsize 2000K -fps_mode vfr  \
        -f rtp rtp://192.168.1.10:8004         # IPアドレスはPCのIPアドレスにします。

※libcamera-vidでH.264へのエンコードが行われているため、今回はffmpegでは何もせずそのままrtpストリームに流しています。(-c:v copyの部分)

VLC側

先ほどと同じsdpファイルで受信できます。

v=0
o=- 0 0 IN IP4 192.168.1.10
s=FFmpeg RTP Stream
c=IN IP4 192.168.1.10
t=0 0
m=video 8004 RTP/AVP 96
a=rtpmap:96 H264/90000

.sdpファイルをvlcから開くと、今度はカメラ動画が再生されます。

配信サーバの構築

配信サーバは、webRTCサーバになるjanus-gatewayと、nginxを使います。
それぞれdockerコンテナ上で構築します。

dockerのセットアップ

Docker Desktopをインストールしてください。
https://www.docker.com/ja-jp/products/docker-desktop/

janus-gatewayの構築

githubにある手順に従って、コンテナ上に各種ライブラリのインストールしたのち、janus-gatewayのビルドを行います。

https://github.com/meetecho/janus-gateway

以下にDockerfileと、docker-compose.yamlの例を以下に示します。

以下のようなフォルダ構成になります

/
├─ docker-compose.yaml
├─ container/
     ├─ janus/
          ├─ Dockerfile
		  ├─ janus-gateway/
		  │    ├─  :
          ├─ etc/
              ├─ janus
                   ├─ janus.jcfg 
                   ├─ janus.plugin.streaming.jcfg
                   ├─ janus.transport.http.jcfg
                   ├─ janus.transport.websockets.jcfg

docker関連ファイル

Dockerfile

FROM ubuntu:24.04

RUN apt-get update \
    && apt-get install -y --no-install-recommends  tzdata\
    && apt-get install -y git cmake \
    && apt clean
    # && apt-get install -y janus \

RUN apt install -y libmicrohttpd-dev libjansson-dev \
	libssl-dev libsofia-sip-ua-dev libglib2.0-dev \
	libopus-dev libogg-dev libcurl4-openssl-dev liblua5.3-dev \
	libconfig-dev pkg-config libtool automake \
    meson sudo wget

WORKDIR /work

RUN  git clone https://gitlab.freedesktop.org/libnice/libnice \
    && cd libnice \
    && meson --prefix=/usr build && ninja -C build && sudo ninja -C build install \
    && cd ..

RUN wget https://github.com/cisco/libsrtp/archive/v2.2.0.tar.gz \
    && tar xfv v2.2.0.tar.gz \
    && cd libsrtp-2.2.0 \
    && ./configure --prefix=/usr --enable-openssl \
    && make shared_library && sudo make install \
    && cd ..

RUN git clone https://libwebsockets.org/repo/libwebsockets \
    && cd libwebsockets \
    && mkdir build \
    && cd build \
    && cmake -DLWS_MAX_SMP=1 -DLWS_WITHOUT_EXTENSIONS=0 -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_C_FLAGS="-fpic" .. \
    && make && sudo make install \
    && cd ../..

COPY containers/janus/janus-gateway /work/janus-gateway
RUN cd janus-gateway \
    && sh autogen.sh \
    && ./configure --prefix=/opt/janus \
    && make && make install && make configs

docker-compose.yaml

services:
  janus:
    container_name: janus
    build: 
      context: .
      dockerfile: containers/janus/Dockerfile
    volumes:
      - ./containers/janus/etc/janus:/opt/janus/etc/janus
    tty: true
    command: /opt/janus/bin/janus
    ports:
      - 8004:8004/udp  # ストリーミングの受付PORT
      - 8088:8088      # http用
      - 8188:8188      # websocket用

janusの設定ファイル

以下の設定ファイルを、containers/janus/etc/janus以下に配置します。
ここでは最低限の設定だけ行っています。
これ以外のオプションについてはドキュメントや、janus-gateway/conf以下にあるサンプルファイルを参照してください。

janus.jcfg

general: {
	configs_folder = "/opt/janus/etc/janus"
	plugins_folder = "/opt/janus/lib/janus/plugins"
	transports_folder = "/opt/janus/lib/janus/transports"
	events_folder = "/opt/janus/lib/janus/events"
	loggers_folder = "/opt/janus/lib/janus/loggers"
}

janus.plugin.streaming.jcfg

h264-ch10: {
	type = "rtp"
	id = 10
	description = "H.264 live stream ch10"
	audio = false
	video = true
	videoport = 8004
	videopt = 126
	videocodec = "h264"
        videortpmap = "H264/9000"
	videofmtp = "profile-level-id=42e01f;packetization-mode=1"
	secret = "adminpwd"
}

janus.transport.http.jcfg

general: {
	json = "indented"
	base_path = "/janus"
	http = true
	port = 8088
	https = false
}

janus.transport.websockets.jcfg

general: {
	json = "indented"
	ws = true
	ws_port = 8188
	wss = false
}

構築

最初に、container/janusフォルダに移動して、janus-gatewayをcloneしておきます。
(デモ用のhtmlファイルなどを使いたいので)

$ cd container/janus
$ git clone https://github.com/meetecho/janus-gateway.git

次に、docker-compose.yamlの置かれているディレクトリに移動したのち
以下のコマンドでコンテナのビルドを行います

$ cd ../..
$ docker compose up --build -d
      :
$ docker container ls      # コンテナが起動していることを確認
      :
$ docker logs janus -n 100 # エラーが出ていないことを確認
      :
---------------------------------------------------
  Starting Meetecho Janus (WebRTC Server) v1.3.3
---------------------------------------------------
      :
Loading plugin 'libjanus_streaming.so'...
JANUS Streaming plugin initialized!
      :
libwebsockets logging: 0
Websockets server started (port 8188)...
JANUS WebSockets transport plugin initialized!
Loading transport plugin 'libjanus_http.so'...
WebSockets thread started
HTTP transport timer started
HTTP webserver started (port 8088, /janus path listener)...
JANUS REST (HTTP/HTTPS) transport plugin initialized!

起動ログで、
Streaming plugin initialized!
Websockets server started (port 8188)
HHTTP webserver started (port 8088, /janus path listener)
が確認できれば問題なく設定できていると思います。

janusの動作確認は後の章にて行いますので、ここではコンテナが起動していることを確認できればOKです。

nginxのセットアップ

コンテナ用のファイルを以下のように配置します。

/
├─ docker-compose.yaml
├─ container/
     ├─ janus/
        :
     ├─ nginx/
          ├─ Dockerfile
          ├─ conf.d/
              ├─ default.conf

docker関連ファイル

Dockerfile

FROM nginx:latest

docker-compose.yaml
以下を追加します。

services:
  janus:
   :

  web:
    container_name: web
    build:
      context: .
      dockerfile: containers/nginx/Dockerfile
    volumes:
      - ./containers/janus/janus-gateway/html:/opt/janus/html
      - ./containers/nginx/conf.d:/etc/nginx/conf.d
    ports:
      - 80:80

nginx

nginxの設定ファイルdefault.confを、container/nginx/conf.dに置きます。

default.conf

server {
    listen 80;
    server_name 0.0.0.0;
    location /janus/ {
      alias /opt/janus/html/;
    }
}

構築

以下のコマンドでコンテナを起動します。

$ docker compose up web --build -d

コンテナが起動したら、
http://localhost/janus/
へアクセスして、以下のデモ画面が表示されることを確認してください。

動作確認

docker container lsコマンドで、jausとwebコンテナが起動していることを確認します。

$ docker container ls

CONTAINER ID   IMAGE                      COMMAND                  CREATED          STATUS                        PORTS                                                                                                                                   NAMES
e30df1b27de8   webrtc-janus               "/opt/janus/bin/janus"   27 minutes ago   Up 6 minutes                  0.0.0.0:8088->8088/tcp, [::]:8088->8088/tcp, 0.0.0.0:8004->8004/udp, [::]:8004->8004/udp, 0.0.0.0:8188->8188/tcp, [::]:8188->8188/tcp   janus
6ce3a18c2336   webrtc-web                 "/docker-entrypoint.…"   45 minutes ago   Up 31 minutes                 0.0.0.0:80->80/tcp, [::]:80->80/tcp                                                                                                     web

動作確認

  • 「Demos」⇒「Streaming」を選択して、streaming pluginのデモ画面に移動します。

  • 「Start」ボタンを押すと、Stream listに、janus.plugin.streaming.jcfgで登録した「H.264 live stream ch10(live)」が見えると思いますので、選択して「Watch」ボタンを押します。

  • Raspberry PIから以下のコマンドで動画のライブ配信を開始します。

$ ffmpeg -f lavfi -readrate 1 -i testsrc2=size=768x432:rate=30 \
  -c:v h264_v4l2m2m -b:v 1M \
  -f rtp rtp://192.168.1.10:8004   # IPアドレスはPCのIPアドレスを指定します。

うまくいけば、ウェブ画面上にて動画の再生が行われます。

Raspberry PIで実行するコマンドを以下のようにすれば、カメラ画像を配信できます。

$ libcamera-vid --hdr -n -t 0 --inline -o - --level 4.2 \
        --width 640 --height 480 \
        --denoise cdn_off \
        -b 1000K \
        --framerate 30 --flush 1  \
        | \
        ffmpeg  -i - -c:v copy -b:v 1000K \
        -preset ultrafast -tune zerolatency \
        -bufsize 2000K -fps_mode vfr  \
        -f rtp rtp://192.168.1.10:8004          # IPアドレスはPCのIPアドレスにします。

次回

次回は、Rapberry PIとjanusの間に画像認識サーバを入れて、yoloで物体認識をさせてみたいと思います。また、今回はjanusのデモサイトを使って動画を受信しましたが、任意のページに配置する方法についても紹介します。

Discussion