Amazon Interactive Video Service (IVS) を使って特定ドメインでのみ再生可能なライブ配信を行う

# はじめに

[Amazon Interactive Video Service(IVS)](https://aws.amazon.com/jp/ivs/) を利用することで、非常に簡単にライブ配信の仕組みを作ることができる。
しかし、個人でライブ配信を行いたいのであれば、YouTube やニコニコ動画、Twitch など、個人が無料で利用可能な様々なサービスがある。 そのため、IVS のような仕組みを利用したいのは、商用、あるいは利用規約的に既存のライブ配信システムが使えないため、独自でライブ配信システムを作成する場合になるだろう。
商用のライブ配信システムを作成する場合、要件として「会員でないと動画を見ることができない」ことは必須事項となる。 例えば動画配信システムとして Vimeo を利用し、ウェブサイトで組み込みプレイヤーを利用する場合、動画・ライブイベント単位で「どのドメインであれば組み込みプレイヤーでの再生を許可する」という設定を行うことができるので、これで動画のURLが外に漏れても、その動画を再生できないようにしている。

Amazon IVS には「トークンによる認証機能」が実装されている。 しかし、この字面だけを見ても「ドメイン単位の動画再生の制御」を実施できるかどうかは分からなかった。 **結論を先に述べると、ドメイン単位での動画再生の制御はトークンによる認証で実現可能** であるが、本記事ではそのやり方・内容について説明する。


# Amazon IVS を利用したライブ配信の手順
## 利用想定

配信者は [OBS Studio](https://obsproject.com/ja/) と Amazon IVS を利用して動画配信を行う前提で説明を行う。

## 動画配信用チャネルの作成

AWS Managemnt Console でオレゴン (us-west-2) リージョンに配信用チャネルを作成する。 **(日本から距離的に一番近いから。 本当は遅延の少ない ap-northeast-1 に立てたいが、残念ながら IVS がまだ提供されていない)**。 今回はテスト配信でよいので、料金が低くなるような設定でチャネルを作成した。

![SnapCrab_NoName_2021-5-19_22-16-20_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502582/8e4eb475-4fe0-606d-df51-c3328593b05a.png)

これだけで、ライブ配信用のストリーム設定・再生設定が作成される。

![SnapCrab_NoName_2021-5-19_22-18-43_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502582/a206e46f-e1fa-a874-d1ae-15cf5bcaf9e0.png)

## 配信ツール (OBS Studio) の設定

OBS Studio の設定 > 配信を開き、サービスを「カスタム」にした後、サーバーに「取り込みサーバー」の値を、ストリームキーに「ストリームキー」の値を設定する。

![SnapCrab_NoName_2021-5-19_22-21-45_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502582/0e678707-5ed8-a57b-5bd0-47b4fe47547a.png)

これでどのウインドウを配信するかを設定して配信開始すればすぐに配信が開始できる……と思ったが、 **自分の環境でこれをやると「１度接続はできたが、すぐに接続が切れて、再び再接続して…」といった状況が繰り返された**。 これは Amazon IVS 以外を使って OBS Studio で配信をしている人にも見られるトラブルで、「想定されているより大きなビットレートの映像がライブ配信のエンドポイントに送られている」ことが問題である。 実際、 **チャネル設定はベーシックにしているので、設定 > 出力からビットレートを低く ~~(400～700kbps程度)~~ しておき、必要に応じて配信動画の解像度を下げておく必要がある (追記参照)**。

なお、高ビットレートの動画配信を行いたい場合は、IVSのチャネルを作成するときの設定で「チャネルタイプ」を「標準」とすること。

あとはこの状態で OBS Stduio で配信を開始すると、それだけでライブ配信が開始される。 Management Console のライブストリームで確認ができる。 以下は、Windows の電卓の画面を配信のテストとしたもの。

![SnapCrab_NoName_2021-5-19_22-37-59_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502582/de7fe33b-5554-c7e4-a0ac-fcf4383ca3f5.png)

### 2021/07/21 追記: Standard と Basic で許容されるビットレートと解像度

最初は単に「うまくいかない場合はビットレートを下げればよい」とだけ記載したが、当記事のコメントで @clom さんに補足頂いた通り、ビットレートを低くするだけはうまくいかないケースがある。 

https://docs.aws.amazon.com/ivs/latest/userguide/service-quotas.html#quotas-contribution-playback

Amazon IVS のサービスクォータによれば、正常に配信可能であるのは以下の２つの条件を満たすときである。 条件外のデータを送信すると、Amazon IVS は（即座に）接続を切断する。

| タイプ | ビットレート | 許容解像度 |
|-------+--------------+--------|
| Basic | 1.5M bps 以下  | 合計 415K pixel 以下、かつ、動画の辺が 855 pixel 以下 |
| Standard | 8.5M bps 以下 | 合計 2.1M pixel 以下、かつ、動画の辺が 1920 pixel 以下 |

そのため、Basic での配信を行うときに切断されてしまうときの対処としては　**「ビットレートを 1.5M bps以下となるように下げ、なおかつ、動画の解像度をSD (480p) 以下にする」** とするのが正しい。


## Amazon IVS 配信用のプレイヤー設定

[公式のサンプル](https://docs.aws.amazon.com/ivs/latest/userguide/player-web.html) を元に、以下の html を作ってウェブサーバーにアップする。

```html
<html>
  <head>
    <title>IVS Example</title>
  </head>
  <body>
    <video id="video-player" playsinline controls></video>
    <script src="https://player.live-video.net/1.3.1/amazon-ivs-player.min.js"></script>
    <script>
      // チャネルの再生URL
      var url = 'https://************.us-west-2.playback.live-video.net/api/video/v1/us-west-2.************.channel.************.m3u8';
      if (IVSPlayer.isPlayerSupported) {
          const player = IVSPlayer.create();
          player.attachHTMLVideoElement(document.getElementById('video-player'));
          player.load(url);
          player.play();
      }
    </script>
  </body>
</html>
```

これをウェブサーバーで表示して、ライブ配信が表示できればOK。


# Amazon IVS の認証機能を利用したライブ配信

## 注意

IVS の認証機能を使う場合、認証用トークンを作成する必要があるので、サービス利用の難易度が跳ね上がる。 これまでは AWS Management Console の上で操作するだけでライブ配信ができたが、認証機能を利用する場合はプログラムによって JWT トークンを作成して、これをライブ動画再生時に渡してやる必要がある。

## Amazon IVS で利用する秘密鍵・公開鍵の設定

Amazon IVS の画面から「再生キー」を開き、この画面を使って認証に利用するトークン用の鍵を作成する。 ここでは、Management Console の「再生キーを作成」から鍵を生成する。

![SnapCrab_NoName_2021-5-19_23-1-58_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502582/61c9fcb6-d298-6d77-9a91-61f9a8dabe12.png)

名前を入力すると、秘密鍵をダウンロードできるので、後の手順でこれを使う。

## チャネルのプライベート設定

チャネルの設定で「再生承認」のトークン認証を有効にする。 これが有効になると、プレビューが利用できなくなるため注意。

![SnapCrab_NoName_2021-5-19_23-10-22_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502582/74588b88-6aff-b377-807f-4f98e64116eb.png)


## JWT Token の作成

本番のウェブサイトで JTW トークンを作成する場合はサーバーサイドで作る、Lambda で作るなど色々な考え方があるが、今回は動作検証なのでローカル環境でトークンを作る。 ここでは [こちらの記事](https://dev.classmethod.jp/articles/amazon-ivs-generate-private-channel-token-by-python/) を参考にして以下の手順でJWTトークンを生成する。

```bash
# 必要なライブラリをインストール
$ pipenv install
$ pipenv install cryptography pyjwt
$ vim create_token.py
```

以下の Python コードを記述。 変数部分やトークンの有効期限は利用環境に応じて置き換える。

```python:create_token.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Amazon IVS の JWT Token を生成します。
"""

from datetime import datetime, timedelta

import jwt


def main():
    videourl = '<再生URL>'
    arn = '<チャネルのARN>'
    cors = '<再生を許可するドメイン - 例: https://www.example.com >'
    now = datetime.utcnow()
    expires = now + timedelta(days=3)
    payload = {
        'aws:channel-arn': arn,
        'aws:access-control-allow-origin': cors,
        'exp': int(expires.timestamp())
    }
    with open('privkey.pem', 'r') as fh:
        secret = fh.read()
    encoded_jwt = jwt.encode(payload, secret, algorithm='ES384')
    print(f'{videourl}?token={encoded_jwt}')


if __name__ == '__main__':
    main()
```

ここに答えがあるが、**JWTトークンを作るときの `aws:access-control-allow-origin` で動画が再生可能なウェブサイトを指定できる** 。そのため、**ドメイン単位で適切なトークンを生成することで、特定ドメインでの再生を許可することが可能** ということが分かる。

あとは秘密鍵を `privkey.pem` という名前でコピーして来て、コマンドを実行し、トークン付きの再生URLを出力する。

```bash
$ cp [ダウンロードした再生キー] privkey.pem
$ pipenv run python create_token.py 
https://************.us-west-2.playback.live-video.net/api/video/v1/us-west-2.************.channel.************.m3u8?token=eyJXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY.ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
```

## ドメイン別で再生が制御されているかの確認

これを異なる２つのドメインで再生してみる。 HTML は Video URL の部分が変更した以外は、先に示したものと同じものを利用する。

```index.html
<html>
  <head>
    <title>IVS Example</title>
  </head>
  <body>
    <video id="video-player" playsinline controls></video>
    <script src="https://player.live-video.net/1.3.1/amazon-ivs-player.min.js"></script>
    <script>
      // チャネルの再生URL
      var url = 'https://************.us-west-2.playback.live-video.net/api/video/v1/us-west-2.************.channel.************.m3u8?token=eyJXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY.ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ';
      if (IVSPlayer.isPlayerSupported) {
          const player = IVSPlayer.create();
          player.attachHTMLVideoElement(document.getElementById('video-player'));
          player.load(url);
          player.play();
      }
    </script>
  </body>
</html>
```

## 確認

ここでは、２つ Web Hosting をする S3 バケットを作成して、ここに同じ index.html を配置した。

トークン生成時に指定したウェブサイトでは正常にライブ配信動画を再生することができた。

![SnapCrab_NoName_2021-5-19_23-25-7_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502582/d19f4711-497e-4c1b-95bd-4cdfae2cc0a1.png)

一方、それ以外のドメインを持つウェブサイトでは動画を開くタイミングで CORS のエラーが生じて、動画が再生できなかった。 

![SnapCrab_NoName_2021-5-19_23-26-42_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502582/1e7119c0-4dfd-180f-2ce3-6b758ddcc978.png)

# まとめ

Amazon IVS の簡単な使い方から、特定ドメインのサイトでのみ再生可能なライブ動画配信を行う実践的な方法までを説明した。
配信の総閲覧時間に対する重量課金制、ということが月額固定額の Vimeo とは異なるが、超小規模の商用ライブ配信を行いたいという場合、あるいは、Vimeo の Enterprise を使わずに自動化されたライブ動画配信システムを作りたい、といった場合はかなり実用性があるように感じた。


# 参考

https://dev.classmethod.jp/articles/amazon-ivs-generate-private-channel-token-by-python/