ローカルのDockerとNginxを用いてGoproからRTMPで送信されたライブ配信映像を再生する
技術スタック DockerNginx

ローカルのDockerとNginxを用いてGoproからRTMPで送信されたライブ配信映像を再生する

2022.05.20

こんにちはjunです。今回の記事は一風変わって物理世界をちょっと扱います。いつもはPC内で行えることばかりやっていますが、今回はGoproをつかってタイトルの通りライブ映像を再生したいと思います。RTMPサーバーをローカルで作成し、そこに送信できるかのテストをしてみたかった感じです。

ローカルPCのDockerにNignxをインストールしてRTMPの設定をしてブラウザから見れるようにします。この記事ではまずなぜ、このようなことを始めたいのかという理由から述べます。実装内容をさっさと知りたい方は「RTMPサーバー実装」へ移動してください。

事の経緯

経緯としては私がyoutubeライブをやりたくなったからです笑。単純にyoutubeライブをやりたいだけならば、さっさとwebカメラからやればいいのですが、私はサイクリングが趣味でなのでその風景をライブしたいと思っていました。持っているアクションカメラがGoproであり、一応公式のスマホアプリを使用することでyoutubeライブをすることができるのですが以下の懸念がありました。

  • Gopro、スマホアプリなどカメラ側の不具合によるライブの中断
  • 柔軟なカメラ切り替えを行える環境の準備
  • スマホからのyoutubeライブ配信は登録者が50人必要(1番の障壁です笑)

カメラ側によるライブの中断

Goproは簡単にライブ配信ができるのですが、以下の様なカメラ・スマホ側の要因によってライブが中断される可能性があります。

  • Goproの熱暴走
  • トンネルなどでwi-fi・ネットワーク切断
  • カメラ、スマホの電池切れ

本当に中断されるかは 未検証 ですが可能性があるのでこれを避けるためには「Gopro側からライブの設定を制御するのでなく、Goproからは音声と映像を取得するのみで配信は別環境で行う」ことがベストな気がします。

柔軟なカメラ切り替えを行える環境の準備

ライブ配信では映しっぱなしでもいいのですが、撮影禁止の箇所だったり環境によって映像のON/OFFを切り替えたいと思っています。Goproアプリは確かにライブ配信は簡単なのですが、より細かい設定や制御は実装されていません。

登録者が50人必要

スマホ通じてライブ配信をする場合はどうやら50人が必要でしたが、RTMPやPCからであれば0人でもOKでした。

登録者的な問題だったり、より細かい制御を実装するために以下のような構成を考えた結果、まずは自前RTMPサーバーへの映像の送信と再生ができるかをテストしてみたかった次第です。最終的にはRTMPサーバーから映像を取得してyoutubeに流します。サーバーでは映像の表示制御やyoutubeのコメント読み上げなど配信に関係する機能を管理するものとします。

実装概要

この記事での目標は

  1. Goproからの映像をアプリを通じてコンテナ内のWebサーバーにRTMP通信で映像を送信。
  2. 映像をドキュメントルートへ配置。
  3. webブラウザからアクセスして映像を見れうようにする。

まずはRTMPサーバーの実装を行います。使用しているOSはmacOs Catalina 10.15.5でdockerは3.5.2です。実装の手順としては

  1. Dockerを用いてRTMPとwebが可能なNginxを構築する。
  2. RTMPの設定をする。
  3. web側でHLSにて映像をvideoタグを用いて再生する。

RTMPとは?

ここまで出ているRTMPとはReal Time Messaging ProtocolのことでTCP上のプロトコルで映像、音声、データを細かいフラグメントに分けてストリーミング送信ができます。今回のようなリアルタイムに映像を撮影してサーバーに送信する際にも利用されます。

なぜNginx?

NginxではRTMPモジュールというのがあり、インストールしてconfファイルに少し記述するだけで1935ポートにrtmp://example.com/liveのようなURLでサーバーに送信できます。

RTMPサーバー実装

docker-composeの作成

では早速やっていきましょう。rtmptestみたいな適当なディレクトリを作成して、Dockerfiledocker-compose.ymlファイルを作成します。また、webとconfファイルを格納してボリュームしておくディレクトリも作成します。

mkdir rtmptest
cd rtmptest

touch Dockerfile docker-compose.yml
mkdir html conf

Dockerfileは以下のようにしておきます。

FROM tiangolo/nginx-rtmp
VOLUME ["/usr/share/nginx/html","/etc/nginx"]

今回はrtmpの設定があるnginxのイメージを利用します。

docker-compose.ymlは以下の通りです。

docke-compose.yml
version: '2'
services:
  app:
    build: .
    ports:
      - "8085:80"
      - "1935:1935"
    volumes:
      -  ./html:/usr/share/nginx/html
      -  ./conf/nginx.conf:/etc/nginx/nginx.conf
      -  ./conf/default.conf:/etc/nginx/conf.d/default.conf

confファイル調整

nginxのconfファイルを調整してRTMPとwebアクセスができるようにします。

RTMP

RTMPはnginx.confというapacheで言うhttp.confに以下のように記述します。

conf/nginx.conf
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

rtmp_auto_push on;

rtmp {
    server {
        listen 1935;
        listen [::]:1935 ipv6only=on;
        access_log /var/log/rtmp_access.log;
        chunk_size 4096;
        timeout 10s;

        application live {
            live on;

            # HLSの記述欄
            hls on;
            # ここに映像ファイルが配置される
            hls_path /usr/share/nginx/html/hls;
            hls_fragment 10s;
        }
    }
}



http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

このファイルはコンテナ内のnginx.confにボリュームします。tiangolo/nginx-rtmpのイメージで生成されるnginx.confはRTMPのために最低限の記述しかないので上記のようにします。

RTMPでは以下の設定です。

rtmp {
    server {
        listen 1935;
        listen [::]:1935 ipv6only=on;
        access_log /var/log/rtmp_access.log;
        chunk_size 4096;
        timeout 10s;

        application live {
            live on;

            # HLSの記述欄
            hls on;
            # ここに映像ファイルが配置される
            hls_path /usr/share/nginx/html/hls;
            hls_fragment 10s;
        }
    }
}

ここでは1935ポートでRTMPをリッスンし、またrtmp://example.com/liveというURLでアップロードできるようにしています。

web

webは以下のようにしておきます。ドキュメントルートは/usr/share/nginx/htmlです。

conf/nginx.conf
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

RTMPを含むNginxの設定はこれでOKです。

web側実装

html/test.htmlとして以下のように作成します。

test.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>MediaElement</title>
  <!-- MediaElement style -->
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/mediaelement/4.2.9/mediaelementplayer.css" />
</head>

<body>
  <!-- MediaElement -->
  <script src="//cdnjs.cloudflare.com/ajax/libs/mediaelement/4.2.9/mediaelement-and-player.js"></script>

  <video id="player" width="640" height="360">
</body>
<script type="text/javascript">

      var player = new MediaElementPlayer('player', {
        success: function(mediaElement, originalNode) {
          console.log("Player initialised");
        }
      });
        player.setSrc("hls/.m3u8");
</script>

</html>

hlsが再生できるようにjsの再生ライブラリを用意します。hls/.m3u8については後述します。

dokcer起動

docker-compose up -d

を用いてdockerイメージを起動しましょう。

配信テスト!

MacのIPを確認

今回のはMac上にRTMPサーバを立てたのでMacのIPを確認しておきます。「システム環境設定」から「ネットワーク」を選択し、Wi-FiからIPアドレスを確認します。このPCは「192.168.0.3」なので

RTMPはrtmp://192.168.0.3/live、webはhttp://192.168.0.3/index.htmlでアクセスできます。

gopro側の設定

Goproとアプリのテザリングなどは省略します。

公式アプリ(app store) 公式アプリ(android)

goproとアプリを接続して、「ライブの設定」に移動

その他、RTMPを選択

Macと同じwi-fiに接続してrtmp://192.168.0.3/liveをURLに入力

設定が完了したらライブ配信を開始します。

サーバーでは

ライブ配信を開始するとRTMPのURLへ動画がアップされていきます。サーバーの方ではドキュメントルートにこの映像のHLSが配置されるようにしたので、マウントの関係上以下のようなディレクトリが発生していると思います。

先程のHTMLにあった「hls/.m3u8」はサーバーにアップロードされた映像の元でもあるファイルを示しています。

ブラウザを見てみると..

ではhttp://localhost:8085/index.htmlでPCから映像を見てみましょう。再生ボタンを押すと以下のようになりました。

しっかりとGoproが写している映像が表示され、音声も流れました。画面を写していましたが、いろいろカメラを回すと確かに映像・音声も切り替わります。

とりあえずこれでライブ配信映像を自前で実装したサーバに送ることが確認できました。今度はサーバー内での映像をyoutubeのlivestreaming apiなどを通じて実際にライブ映像に流せるようにしてみます。

参考

今回参考にした資料です。非常に助かりました。

Copyright © 2021 jun. All rights reserved.