Vagrant、nginxでストリーミングサーバを作り、動画ファイルに保存、ダウンロードまでをググりながらやって見た

結構前の勉強会でコードのレベルはともかく、とりあえず動くものを作成しましたので、その時に行ったことを書かせていただきます。m(_ _)m

私自身初心者でググりながらやってますのであまり当てにしないでください。

---

参考

-  [nginxでストリーミングサーバを立ててライブ配信する。](https://qiita.com/hagane5563/items/842afe6d6e7100db3a28)
-  [Vagrant+nginxでストリーミングサーバーを作る方法](https://qiita.com/sssinsi/items/a8a2564af92fb7dd1e32)

とても参考になりましたm(_ _)m

---

バージョン

- macOS High Sierra
- Vagrant 2.2.2
- VirtualBox 5.2.22
- Google Chrome 71.0.3578.98
- OBS 22.0.3

---

# centos7を起動する

### boxを追加

```bash
$ vagrant box add centos/7
==> box: Loading metadata for box 'centos/7'
    box: URL: https://vagrantcloud.com/centos/7
This box can work with multiple providers! The providers that it
can work with are listed below. Please review the list and choose
the provider you will be working with.

1) hyperv
2) libvirt
3) virtualbox
4) vmware_desktop

Enter your choice: 3  # <- virtualboxを使うので3を選択
==> box: Adding box 'centos/7' (v1809.01) for provider: virtualbox
    box: Downloading: https://vagrantcloud.com/centos/boxes/7/versions/1809.01/providers/virtualbox.box
    box: Download redirected to host: cloud.centos.org
==> box: Successfully added box 'centos/7' (v1809.01) for 'virtualbox'!
```

box listで追加されていたらOK

```bash
$ vagrant box list
centos/7     (virtualbox, 1809.01)
```

### 仮想マシンでCentOSを立ち上げる

好きなディレクトリを作成し、そこで作業をする

```bash
$ vagrant init centos/7
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
```

VagrantFileができているので、35行目ぐらいにある`config.vm.network`のコメントを外しておく

```bash
$ vi Vagrantfile 
```

```diff
-  # config.vm.network "private_network", ip: "192.168.33.10"
+  config.vm.network "private_network", ip: "192.168.33.10"
```

仮想マシンを起動

```bash
$ vagrant up
```

起動されいることを確認

```bash
$ vagrant status
Current machine states:

default                   running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.
```

ssh接続し、`yum update`を実行しておく

```
$ vagrant ssh
[vagrant@localhost ~]$ sudo yum update
```

# nginxのインストール

```bash
$ sudo yum groupinstall "Development Tools"
$ sudo yum -y install pcre-devel zlib-devel openssl-devel wget

$ cd /usr/local/src/
$ sudo wget http://nginx.org/download/nginx-1.8.0.tar.gz
$ sudo tar -zxvf nginx-1.8.0.tar.gz
$ sudo wget https://github.com/arut/nginx-rtmp-module/archive/master.zip
$ sudo unzip master.zip

$ cd nginx-1.8.0
$ sudo ./configure --user=nginx --group=nginx --with-http_ssl_module --add-module=../nginx-rtmp-module-master
$ sudo make
$ sudo make install

$ sudo groupadd nginx
$ sudo useradd -g nginx nginx
$ sudo usermod -s /bin/false nginx
```

nginxの設定ファイルの一番下に以下の設定を追加

```bash
$ sudo vi /usr/local/nginx/conf/nginx.conf
```

```
rtmp {
        server {
            listen 1935;
            chunk_size 4096;

            application live {
                live on;
                record off;
                wait_video on;
            }
        }
    }
```

![Screen Shot 2018-12-11 at 23.46.43.png](https://qiita-image-store.s3.amazonaws.com/0/142910/53693d71-cf3f-c4ba-f91f-7857ddf71628.png)



ファイアーウォールを切る

```bash
$ sudo systemctl stop firewalld
$ sudo systemctl disable firewalld
```

nginx起動

```bash
$ sudo /usr/local/nginx/sbin/nginx # <- 起動
$ sudo /usr/local/nginx/sbin/nginx -s quit # <- 停止
```

Vagrantfileに設定してあるipアドレスにブラウザからアクセス

![Screen Shot 2018-12-11 at 23.50.27.png](https://qiita-image-store.s3.amazonaws.com/0/142910/76867658-c4e6-d65d-a6ce-646fda6679c3.png)


# 配信する方の設定

フリーソフトの「OBS」をインストールする

https://obsproject.com/ja

---

- 「Video Capture Device」を選択↓

<img width="1111" alt="Screen Shot 2018-12-12 at 23.23.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/142910/d163675c-0605-c71f-5693-1dd2d7af2714.png">

- 「OK」を押す↓

<img width="1191" alt="Screen Shot 2018-12-12 at 23.24.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/142910/212db398-b01c-3e52-ec3b-d078199c79ef.png">

- macの内蔵カメラを使うので「FaceTime HD Camera」を選択し「OK」を押す↓

<img width="832" alt="Screen Shot 2018-12-12 at 23.30.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/142910/438b8bf8-8809-e3f5-366d-dddaae94f6f8.png">

- 右下の「Settings」をクリック

<img width="1163" alt="Screen Shot 2018-12-12 at 23.35.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/142910/2f08177a-e735-26ea-b06c-9e220a5c9ebb.png">

- 「Stream」の設定
    - Strream Type -> Custom Streaming Server
    - URL -> rtmt://ipアドレス/アプリケーション名
    - Stream key -> 「test」と設定しました
   
<img width="1093" alt="Screen Shot 2018-12-12 at 23.44.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/142910/a75886bf-fdbf-4aba-1d54-8046b9faead9.png">

※URLのアプリケーション名はnginx.confに設定した名前、今回では「live」になる

<img width="605" alt="Screen Shot 2018-12-12 at 23.45.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/142910/a9f7e824-38c7-3115-0dff-090fe71cb30d.png">

- 「Output」の設定

設定を変えなくても配信に問題ありませんでした

<img width="1093" alt="Screen Shot 2018-12-12 at 23.50.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/142910/fbfda2d0-1664-2bf2-5e70-c8c550b62d8c.png">

- 右下の「Start Streaming」を押すと配信が開始される

<img width="1163" alt="Screen Shot 2018-12-12 at 23.56.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/142910/553283a2-5130-3629-34c3-8fcd581c083c.png">

# 受信する方の設定

- 受信用のhtmlを作成

```bash
$ sudo vi /usr/local/nginx/html/live.html
```

```live.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css" />
    <link href="https://vjs.zencdn.net/7.3.0/video-js.css" rel="stylesheet">
    <script src="https://vjs.zencdn.net/7.3.0/video.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/videojs-flash/2.1.2/videojs-flash.min.js"></script>
    <style>
        .disabled {
            cursor: not-allowed;
            opacity: .5;
        }
    </style>
  </head>
  <body>
    <div class="container">
        <video id="example_video_1" class="video-js vjs-default-skin u-full-width" height="500" controls preload="none" poster="" data-setup="{}">
            <source src="rtmp://192.168.33.10/live/test" type="rtmp/mp4">
            <p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that
                <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
            </p>
        </video>
        <hr>
        <div class="u-pull-right">
            <a id="record" class="button" href="#">保存開始</a>
            <a id="download" class="button button-primary disabled" href="" download>ダウンロード</a>
        </div>
    </div>
  </body>
</html>
```

- chrome://settings/content/flash を開き、「Ask frist」に設定しておく↓

![Screen Shot 2018-12-13 at 1.23.26.png](https://qiita-image-store.s3.amazonaws.com/0/142910/45ad03a8-200f-b59d-5a96-725b2e3f8b8b.png)


- http://192.168.33.10/live.html にアクセス

![Screen Shot 2018-12-13 at 1.26.27.png](https://qiita-image-store.s3.amazonaws.com/0/142910/b75d9d5d-a82b-65db-4a79-7d8d5a64da29.png)


- flashを許可すると、OBSで配信中の映像が見れる

![CZLvLwg5U9.gif](https://qiita-image-store.s3.amazonaws.com/0/142910/070d312f-388b-140c-28c3-860991c4106f.gif)

---

# ストリーミング配信をファイルに保存する

### ffmpegをインストール

```bash
$ sudo yum -y install epel-release
$ sudo rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
$ sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-1.el7.nux.noarch.rpm
$ sudo yum -y install ffmpeg ffmpeg-devel
$ ffmpeg -h # ヘルプが表示されたらOK
```

### phpのインストール

```bash
$ sudo yum -y install php
$ php --version
PHP 5.4.16 (cli) (built: Oct 30 2018 19:30:51) 
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
```

### php-frmのインストール

```bash
$ sudo yum -y install php-fpm
$ sudo vi /etc/php-fpm.d/www.conf
```

- apacheからnginxに変更

```diff:/etc/php-fpm.d/www.conf
  ; Unix user/group of processes
  ; Note: The user is mandatory. If the group is not set, the default user's group
  ;       will be used.
  ; RPM: apache Choosed to be able to access some dir as httpd
- user = apache
+ user = nginx
  ; RPM: Keep a group allowed to write in log dir.
- group = apache
+ group = nginx
```

![Screen Shot 2018-12-13 at 23.22.06.png](https://qiita-image-store.s3.amazonaws.com/0/142910/03f722bc-7dea-c1a1-12f3-1454b01fdfda.png)

- php-fpmを起動

```bash
$ sudo systemctl start php-fpm
$ sudo systemctl enable php-fpm
```

- nginxの設定変更


```bash
$ sudo vi /usr/local/nginx/conf/nginx.conf
```

```diff:/usr/local/nginx/conf/nginx.conf
   location / {
        root   html;
-       index  index.html index.htm;
+       index  index.php index.html index.htm;
   }

  (省略)

-  #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;
-  #}
+  location ~ \.php$ {
+       root           html;
+       fastcgi_pass   127.0.0.1:9000;
+       fastcgi_index  index.php;
+       fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
+       include        fastcgi_params;
+  }
```

![Screen Shot 2018-12-13 at 23.35.20.png](https://qiita-image-store.s3.amazonaws.com/0/142910/aa2c04d3-d084-b0e1-f6eb-d658f2d4058f.png)

- nginx再起動

```bash
$ sudo /usr/local/nginx/sbin/nginx -s reload
```

- index.phpを作成

```bash
$ sudo sh -c "echo '<?php phpinfo();' > /usr/local/nginx/html/index.php"
```

![Screen Shot 2018-12-13 at 23.44.59.png](https://qiita-image-store.s3.amazonaws.com/0/142910/ba3a584f-7585-488e-8ff0-0d65df503bd5.png)

### nginxにsudo権限を持たせる

phpからffmpegの起動プロセスをkillするなどでnginxにsudo権限がないとうまくいきませんでした。

```bash
$ sudo visudo
```

以下を一番下に追加して上書き保存
※ローカルの開発環境なのでALLでてきとうに全権限を与えました。

```
%nginx ALL=NOPASSWD: ALL
```

![Screen Shot 2018-12-15 at 2.44.54.png](https://qiita-image-store.s3.amazonaws.com/0/142910/1d34447c-86e1-3c27-7ecb-bccc0e5bee68.png)

### SELinuxを無効化しておく

```bash
$ sudo vi /etc/selinux/config
```

enforcingをdisabledに変える

```diff
- SELINUX=enforcing
+ SELINUX=disabled
```

![Screen Shot 2018-12-15 at 3.59.54.png](https://qiita-image-store.s3.amazonaws.com/0/142910/1a62ceac-7138-c763-469f-1689d376e79a.png)

```bash
$ sudo reboot # サーバ再起動
```

```bash
$ getenforce # 設定を確認
Disabled
$ sudo /usr/local/nginx/sbin/nginx # nginxの自動起動設定をしていないので起動する
```





### ffmpeg実行用のphp作成

```bash
$ sudo vi /usr/local/nginx/html/stream.php
```

```php:/usr/local/nginx/html/stream.php
<?php
ini_set('ignore_user_abort', 1);
header('Content-Type: text/event-stream; charset=utf-8');
header('Cache-Control: no-cache');
header('Content-Encoding: none');
header('X-Accel-Buffering: no');

echo sprintf("data: %s\n\n", json_encode(['file' => $time = time()]));
ob_end_flush();
flush();

// 保存を開始する -> ffmpegでストリーミング配信を非同期に保存する
shell_exec(sprintf('echo vagrant | sudo ffmpeg -i rtmp://127.0.0.1/live/test -c copy %s/%s.mp4 > /dev/null 2>&1 </dev/null &', __DIR__, $time));
while(connection_aborted()) {
    echo sprintf("data: %s\n\n", json_encode(['status' => '保存中']));
    ob_end_flush();
    flush();
    sleep(1);
}

// 保存を終了する -> ffmpegのプロセスをkill
$pid = shell_exec('ps --no-heading -C ffmpeg -o pid');
shell_exec(sprintf('echo vagrant | sudo kill %d', $pid));
```

live.htmlを変更して、ボタンを押して保存などできるようにする

```html:/usr/local/nginx/html/live.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css" />
    <link href="https://vjs.zencdn.net/7.3.0/video-js.css" rel="stylesheet">
    <script src="https://vjs.zencdn.net/7.3.0/video.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/videojs-flash/2.1.2/videojs-flash.min.js"></script>
    <style>
        .disabled {
            cursor: not-allowed;
            opacity: .5;
        }
    </style>
  </head>
  <body>
    <div class="container">
        <video id="example_video_1" class="video-js vjs-default-skin u-full-width" height="500" controls preload="none" poster="" data-setup="{}">
            <source src="rtmp://192.168.33.10/live/test" type="rtmp/mp4">
            <p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that
                <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
            </p>
        </video>
        <hr>
        <div class="u-pull-right">
            <a id="record" class="button" href="#">保存開始</a>
            <a id="download" class="button button-primary disabled" href="" download="">ダウンロード</a>
        </div>
    </div>
    <script>
    (function(){
        'use strict'
        document.addEventListener("DOMContentLoaded", ()=>{
            let es
            // 保存ボタン
            record.addEventListener("click", e => {
                e.preventDefault()
                if(es == null) {
                    es = new EventSource("./stream.php")
                    es.addEventListener("message", e => {
                        if(JSON.parse(e.data).file) download.href = `${JSON.parse(e.data).file}.mp4`
                        console.log(JSON.parse(e.data))
                    })
                    es.addEventListener("error", e => {
                        alert("error")
                        e.target.close()
                        es = null
                        record.textContent = "保存開始"
                    })

                    e.target.textContent = "保存終了"
                    download.classList.add("disabled")
                } else {
                    es.close()
                    es = null
                    record.textContent = "保存開始"
                    download.classList.remove("disabled")
                }
            })

            // ダウンロードボタン
            download.addEventListener("click", e => {
                if (e.target.classList.contains("disabled")) {
                    e.preventDefault()
                    return
                }
                e.target.classList.add("disabled");
            })
        })
    })()
    </script>
  </body>
</html>
```

エラー処理をほとんど書いてないので不安定な部分も多いですが、とりあえず動くところまでできました。

![Untitled2.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/142910/4082c2d8-10ab-37bc-cd58-3f995c22e2e8.gif)

最後まで見ていただいてありがとうございましたm(_ _)m

---

参考

- [yum groupinstall "Development tools" で入るパッケージ一覧（CentOS） - Qiita](https://qiita.com/hirapi/items/6f9da09b9af795c11b71)

- [Getting Started with Video.js - Video.js: The Player Framework](https://videojs.com/getting-started/)

- [In chrome to play rtmp/flv ?? · Issue #58 · videojs/videojs-flash](https://github.com/videojs/videojs-flash/issues/58)

- [CentOS7にffmpegをyumインストール - Qiita](https://qiita.com/snoguchi/items/d12f0407075d7978925c)

- [CentOS7 + Nginx + PHP-FPM でPHPを実行する環境を整える - Qiita](https://qiita.com/noraworld/items/fd491a77af9d4e1ccffa)

- [Linux: Restart Nginx WebServer - nixCraft](https://www.cyberciti.biz/faq/nginx-linux-restart/)

- [Server-Sent Events の利用 | MDN](https://developer.mozilla.org/ja/docs/Server-sent_events/Using_server-sent_events)

- [HTML5 Server Sent events in 5 minutes
](https://www.youtube.com/watch?v=YfD5HO-iyIk)

- [php - Server sent events - text/event-stream ocasionally misenterpreted as text/html by chrome and firefox - Stack Overflow](https://stackoverflow.com/questions/31430839/server-sent-events-text-event-stream-ocasionally-misenterpreted-as-text-html-b)

- [PHP Flush that works... even in Nginx - Stack Overflow](https://stackoverflow.com/questions/4870697/php-flush-that-works-even-in-nginx)

- [PHP: connection_aborted - Manual](http://php.net/manual/ja/function.connection-aborted.php)

- [How To Remove a Class Name](https://www.w3schools.com/howto/howto_js_remove_class.asp)

- [dom - What is the &quot;hasClass&quot; function with plain JavaScript? - Stack Overflow](https://stackoverflow.com/questions/5085567/what-is-the-hasclass-function-with-plain-javascript)

- [十三章第二回　Server-Sent Events — JavaScript初級者から中級者になろう — uhyohyo.net](https://uhyohyo.net/javascript/13_2.html)

- [visudoでsudoを追加、からの終わりまで - Qiita](https://qiita.com/dekosuke-menti/items/11d5f1066d58245fb8b0)
- [シェルスクリプト　バックグラウンド実行が失敗する際の対応策｜ prophet STAFF BLOG](http://blog.prophet.jp/1232)
- [Linuxコマンド(Bash)でバックグラウンド実行する方法のまとめメモ - Qiita](https://qiita.com/inosy22/items/341cfc589494b8211844)
- [SELinuxの無効化 - Qiita](https://qiita.com/hanaita0102/items/5d3675e4dc1530b255ba)
- [Handle connection reset by the client? · Issue #1 · hhxsv5/php-sse](https://github.com/hhxsv5/php-sse/issues/1)