複数台Android端末のカメラ映像をFFmpegでリアルタイム合成してYouTubeLiveから配信

#はじめに
---
岩手県立大学2017年度アドベントカレンダー7日目の記事です．
FFmpegで複数のネットワークストリームをリアルタイムエンコードしてる記事が中々見つからなかったので備忘録も兼ねて記しておきます．

#目指すところ
---
![ihan_tousatsu_smartphone.jpg](https://qiita-image-store.s3.amazonaws.com/0/151972/45bab48e-2be8-813a-ec1d-f221470cfac0.jpeg)


1) 複数台のAndroid端末のカメラ映像をメディアサーバにそれぞれの端末から送信(今回はRTMPを用います)．
2) メディアサーバ内でRTMPストリームを受け取り，FFmpegで入力映像ストリームにリアルタイムエンコード掛けて合成します．
3) 合成した映像ストリームをFFmpegを用いてRTMPでYouTubeLiveに送信．
4) 視聴ページで複数ストリームを同時に視聴．
このような流れです．

#使うもの
---
- Android端末(複数)
  - 各種RTMP Streamer(カメラ映像送信)
 
- メディアサーバ(Ubuntu)
  - Nginx with RTMP module(Android端末から送信されるRTMPストリームの受信)
  - FFmpeg(受信したRTMPストリームのリアルタイムエンコード & YouTubeLiveに送信)

- YouTubeLive
    - YouTubeアカウント
    - YouTubeLiveのストリームキー
    
- 視聴者端末(YouTubeLiveにアクセスできるもの)
    
# 準備
---
使うものを一つずつ準備していきます．

#① Android端末

## RTMP streamer
Androidカメラの映像をRTMPで送信するものを用意します．作成しても既存のアプリを用いても問題ありません．
今回はGooglePlayにて配信されている [OS broadcaster](https://play.google.com/store/apps/details?id=air.OS.Broadcaster&hl=j)を使用させていただきます．

私の手順に順当に従う場合はGoogle Play からOS broadcasterをインストールして下さい．


#② メディアサーバ
## Nginx with RTMP module
Nginxに映像通信用のRTMPモジュールを導入したものを用意します．
Ubuntu 16.04の端末にセットアップしていきます．

##### makeに必要なものを取得

`$ sudo apt update`
`$ sudo apt upgrade` 
`$ sudo apt-get install aptitude`
`$ sudo aptitude install  libpcre3 libpcre3-dev libssl-dev build-essential unzip`

##### NginxとRTMPモジュールを入手
`$ wget http://nginx.org/download/nginx-1.12.2.tar.gz`
`$ wget https://github.com/arut/nginx-rtmp-module/archive/master.zip`
##### 解凍
`$ tar xvzf nginx-1.12.2.tar.gz`
`$ unzip master.zip`
##### ビルド
`$ cd nginx-1.12.2/`
`$ ./configure --add-module=../nginx-rtmp-module-master`
`$ make`
`$ sudo make install`
##### 起動
`$ sudo /usr/local/nginx/sbin/nginx`

#####RTMPの設定
ファイル末尾にRTMP設定を追記
ここでのアプリケーション名は"live"になります．
後ほど使用するので変更する場合は以降読み替えをお願いします． 
`$ sudo vim /usr/local/nginx/conf/nginx.conf`

```text:nginx.conf
}
rtmp {
 server {
  listen 1935;
  chunk_size 4096;

  application live {
   live on;
   record off;
  }
 }
}
```
nginxの再起動
`$ sudo /usr/local/nginx/sbin/nginx -s stop`
`$ sudo /usr/local/nginx/sbin/nginx`

## FFmpeg
Nginxで受信したRMTPストリームをFFmpegのインプットソースとして扱います．
入力ストリームに対してリアルタイムエンコードを掛け，複数ストリームを1つのストリームに合成してYouTubeLiveに送信します．


##### FFmpegのインストール
リポジトリを追加してインストールします
`$ sudo apt-get -y install software-properties-common
$ sudo add-apt-repository ppa:mc3man/trusty-media
$ sudo apt-get update
$ sudo apt-get -y install ffmpeg`

起動確認
` $ ffmpeg -help`

#####

#③ YouTube周り
アカウント作成は各自でお願いします．
## YouTubeLive ストリームキーの取得
FFmpegからYouTubeLive鯖に送信する際に必要になります．
取得の流れは以下の図の様になります．
![図3.png](https://qiita-image-store.s3.amazonaws.com/0/151972/33bb1e64-4eeb-d59d-4632-dd40056dbd08.png)



以上で準備が整いました．お疲れ様です．

#配信してみる
---
###①Android端末でストリームを送信
1. OS broadcasterを起動
2. アクセスポイントアイコンのボタンをクリック
3. RTMP入力欄にメディアサーバのIPアドレス/アプリケーション名を入力します

` rtmp://172.16.xxx.xx/live`
4. Stream欄に任意の文字列を入力します．
私は何代目の端末なのか，ということで1から順に整数を振りました．
FFmpegで入力ソースを引っ張ってくるときに使用するので被らない様にしましょう．

ex. 配信する端末数の数だけ1～4を繰り返します．
これでメディアサーバにRTMPを用いてカメラ映像が送信されました．

###②メディアサーバでストリーム受け取り&エンコード&送出
メディアサーバでFFmpegを実行します．コマンドのひな形を記しておきますが，shに書いて実行するのが精神衛生的に良いと思います．
各オプションについてここでの記述は控えさせていただきます．


今回はソースになるAndroid端末を2台としました(4台も確保できませんでした)



```
$ ffmpeg -i rtmp://[メディアサーバのip]/[アプリケーション名]/[Streamに入力した文字列] \
         -i rtmp://[メディアサーバのip]/[アプリケーション名]/[Streamに入力した文字列] \
         -filter_complex "nullsrc=size=1280x360[base];[0:v] scale= 640x360 [v1];[1:v] scale=640x360 [v2]; \
          [base][v1] overlay=0:0[tmp1]; [tmp1][v2] overlay= 640:0; " \
         -b:v 1000k -b:a 128k -preset fast -vcodec libx264 -f flv [YouTubeLiveの設定で確認したサーバURL]/[ストリームキー]
``` 

今回の作った私の環境の場合のコマンド例も記しておきます．自分の環境に書き換えてみてください．

```
$ ffmpeg -i rtmp://172.16.126.95/live/1 \
         -i rtmp://172.16.126.95/live/2  \
         -filter_complex "nullsrc=size=1280x360[base];[0:v] scale= 640x360 [v1];[1:v] scale=640x360 [v2]; \
          [base][v1] overlay=0:0[tmp1]; [tmp1][v2] overlay= 640:0" \
          -preset fast -c:v libx264 -c:a aac -strict -2 -f flv rtmp://a.rtmp.youtube.com/live2/xxxxxxxxxxxxxxxx
``` 

###③視聴
YouTubeLiveの配信ページにアクセスすると複数台Android端末の映像ストリーミングが合成されたものが配信されています．(スクショです)

![826b566cf6894dce421147639aca7d18.png](https://qiita-image-store.s3.amazonaws.com/0/151972/7ef8699b-b2fa-7584-7e8f-56f2bddcf7eb.png)

プレイヤー画面の左側半分が1代目の端末，右側半分が2台目の端末になります．

#課題
ずっと配信してると地獄のBufferqueueoverflow連発して接続断絶．心当たりのあるコマンド試してみても治らず．
RTSPとかUDPとかほかのプロトコルで入力してみたりFFmpegの設定値変えたりいろいろ試す必要がありそう．
何か心当たりのある方は教えて頂けると幸いです．
![c05156b27f3fb0f2b3b5879d14cec0d6.png](https://qiita-image-store.s3.amazonaws.com/0/151972/90666447-9c4e-5061-df8f-37847db16d39.png)

#参考にしたもの
[nginx-rtmp-moduleでお試しLive配信環境を作る](https://qiita.com/sparkgene/items/c3ac042f30cc5d0fe324)
[FFmpegの使い方](http://tech.ckme.co.jp/ffmpeg.shtml) 


---
次回は(@KentaKato)さんです．よろしくお願いします．