Macからカメラ映像をAmazon Kinesis Video Streamに送信しHLSで動画再生をする

この記事ではKinesis Video Streamを使ってMacに搭載されているカメラから撮影した動画をAWSに配信し、その後戻り値にHLSストリーミングセッションURLを返すlambda関数を作成し、簡単なフロントエンドからlambdaを呼び出しLIVE動画を視聴する方法を書いていきます。

# 環境
MacBook Air (13-inch, Early 2015)
OS: macOS Mojave バージョン 10.14.5

# プロデューサーライブラリ
Kinesis Video Streamにデータを送信するにはプロデューサーライブラリをMacにビルドする必要があります。Amazon Kinesisのダッシュボードに行くとプロデューサーSDKの項目があり、基本的にはそれに従って設定していくことになると思いますが、参考までに私が行なったことを書いていきます。

### ビルド時に必要なライブラリをインストール
- cmake 3.7/3.8
- xCode (Mac OS) / clang / gcc (xcode-select version 2347)
- autoconf 2.69 (License GPLv3+/Autoconf: GNU GPL version 3 or later)
- bison 2.4
- pkgconfig
- Java JDK (optional) - if Java JNI compilation is required

brew installが使えるのであれば楽にインストールできるかと思います。


### プロデューサーライブラリのコードをダウンロード

```console
$ git clone https://github.com/awslabs/amazon-kinesis-video-streams-producer-sdk-cpp 
```
### ビルド
クローンしたディレクトリの中のkinesis-video-native-buildに移動し、install-scriptを実行するとビルドが始まリます。30分くらいで完了しました。エラーが発生する場合は、ライブラリのバージョンがあっているのかの確認を行ってください。私は、bisonのバージョンが古かったため若干はまりました。ここまでできればMac側の設定は完了です。

```console
$ cd amazon-kinesis-video-streams-producer-sdk-cpp/kinesis-video-native-build
$ sudo ./install-script
```

# AWSでの設定
### IAMユーザーの作成
AWSにストリームデータを送信するためのIAMユーザーを作成しました。今回はお試しで実行するため付与した権限をAmazonKinesisVideoStreamsFullAccessにしましたが、本番環境などで使う際は検討が必要だと思います。作成した際のアクセスキーとシークレットアクセスキーは後々使うのでメモっておいてください。
![スクリーンショット 2019-08-15 13.02.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306038/243724f0-cd21-ac02-8a28-0d1c6b890a8e.png)

### Kinesisビデオストリームの作成
コンソールからKinesis Video Streamsに移動し、適当なストリーム名を入力して下さい。今回はMyKinesisVideoStreamという名前にしました。これだけでストリームを作成することができます。
![screencapture-us-west-2-console-aws-amazon-kinesisvideo-streams-create-2019-08-15-13_14_58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306038/49640598-d9cd-7ee3-907a-e305186dc5d2.png)

# 実行してみる
では実際にMacからAWSにカメラ映像を送信してみましょう。先ほどビルドをしたことでkinesis-video-native-buildの中にフォルダがいくつか作成されていると思います。その中のdownloads>local>binに移動して以下のコマンドを入力してください。毎回移動するのが面倒くさいという方はパスを通してしまうのがいいと思います。

```console
$ ./gst-launch-1.0 autovideosrc ! videoconvert ! video/x-raw,format=I420,width=1280,height=720 ! vtenc_h264_hw allow-frame-reordering=FALSE realtime=TRUE max-keyframe-interval=45 bitrate=512 ! h264parse ! video/x-h264,stream-format=avc,alignment=au,profile=baseline ! kvssink stream-name='YourStreamName' storage-size=512 access-key='YourAccessKey' secret-key='YourSecretKey' aws-region='YourAWSRegion'
```
これを実行するとエラーがなければこのような感じでデータを送信している様子が出力されます。

```
2019-08-15 13:34:41 [0x70000d419000] DEBUG - Kinesis Video client and stream metrics
	>> Overall storage byte size: 536870912
	>> Available storage byte size: 536756426
	>> Allocated storage byte size: 114486
	>> Total view allocation byte size: 144072
	>> Total streams frame rate (fps): 33
	>> Total streams transfer rate (bps): 28801584 (28126 Kbps)
	>> Current view duration (ms): 134
	>> Overall view duration (ms): 1501
	>> Current view byte size: 39560
	>> Overall view byte size: 112232
	>> Current frame rate (fps): 33.6071
	>> Current transfer rate (bps): 28801584 (28126 Kbps)
2019-08-15 13:34:42 [0x70000d107000] DEBUG - postReadCallback(): Wrote 16372 bytes to Kinesis Video. Upload stream handle: 0
2019-08-15 13:34:42 [0x70000d419000] DEBUG - postReadCallback(): Wrote 16372 bytes to Kinesis Video. Upload stream handle: 0
2019-08-15 13:34:42 [0x70000d419000] DEBUG - postReadCallback(): Wrote 16372 bytes to Kinesis Video. Upload stream handle: 0
2019-08-15 13:34:42 [0x70000d419000] DEBUG - postReadCallback(): Wrote 16372 bytes to Kinesis Video. Upload stream handle: 0
2019-08-15 13:34:42 [0x70000d419000] DEBUG - postReadCallback(): Wrote 1136 bytes to Kinesis Video. Upload stream handle: 0
2019-08-15 13:34:42 [0x70000d419000] DEBUG - postReadCallback(): Pausing CURL read for upload handle: 0
2019-08-15 13:34:42 [0x70000d419000] DEBUG - postWriteCallback(): Curl post body write function for stream with handle: MyKinesisVideoStream and upload handle: 0 returned: {"EventType":"RECEIVED","FragmentTimecode":1469,"FragmentNumber":"91343852333181447252914294203941992195168502076"}
```

実際に送信されているのかコンソールに行って確認してみましょう。 このような形で動画が再生されていれば成功です。これでデバイス側の処理は終了しました。次はバックエンドの部分を作っていきます。
![screencapture-ap-northeast-1-console-aws-amazon-kinesisvideo-streams-streamName-MyKinesisVideoStream-2019-08-15-13_43_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306038/0eaa2ab5-f244-ad40-00d2-1068870ff3e0.png)

# セッションURL取得のためのバックエンドの作成
今回の最終的な目標である、ウェブサイトから動画をモニタリングするということを行うためにはまず、セッションURLというものを取得する必要があります。それをメディアプレーヤーまたはスタンドアロンアプリケーションで使用してストリームを表示できます。今回はそのセッションURLを取得するlambda関数を作成して、フロントエンドからAPI Gateway経由でlambdaにアクセスし、セッションURLを取得し再生、という構成にしたいと思います。lambdaやAPI Gatewayの設計に関しては他の方の記事で詳しく書いてあるものもあるのでザックリ行きたいと思います。

### lambda関数の作成
コンソールからlambdaに行き関数の作成を行ってください。実行ロールは今回も手抜きでAmazonKinesisVideoStreamsFullAccessを付与しています。lambdaのコードは以下のようになっています。

```python
import os
import json
import boto3

def lambda_handler(event, context):
    # Kinesis Video Streamのダッシュボードから取得
    streamarn = os.environ.get('STREAM_ARN')
    
    kvs = boto3.client('kinesisvideo',region_name='ap-northeast-1')
    endpoint = kvs.get_data_endpoint(
            StreamARN = streamarn,
            APIName = 'GET_HLS_STREAMING_SESSION_URL'
        )
    
    kvam = boto3.client('kinesis-video-archived-media',endpoint_url = endpoint.get('DataEndpoint'))
    url = kvam.get_hls_streaming_session_url(
        StreamARN=streamarn,![screencapture-ap-northeast-1-console-aws-amazon-apigateway-home-2019-08-15-14_24_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306038/a55318b2-8459-6658-797b-205757fb0e45.png)

        Expires=3600,
        PlaybackMode='LIVE'
    )

    headers = {'Content-Type' : 'application/json', 'Access-Control-Allow-Origin': '*'}
    res = {
        "statusCode" : 200,
        "body" : url.get('HLSStreamingSessionURL'),
        "headers" : headers
    }
    return res

```

テストを行ってセッションURLが取得できたらOKです。ただ、テストを行う際はストリーミングデータを流しておく必要があるので注意してください。

### API Gatewayの作成
lambdaにアクセスするためのAPIを作成しました。構成はシンプルで /sessionにGETメソッドを一つ追加しただけです。
![screencapture-ap-northeast-1-console-aws-amazon-apigateway-home-2019-08-15-14_24_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306038/61314d10-bc49-3a49-5ece-dcec8720d9e8.png)

これでAPI経由でセッションURLを取得することができるようになりました。次は作ったAPIにアクセスし、セッションURLを取得した後、Video.jsというビデオプレーヤーを使って再生してみましょう。

# フロントエンド部分
[こちら](https://dev.classmethod.jp/client-side/javascript/videojs-http-streaming-vhs-hls-streaming-minimum-setting/)のサイトを参考にさせていただいてコードを書いていきました。自分自身フロントエンドの知識や経験がほとんどないので指摘などがありましたらお願いします。script部分でAPIにアクセスして、レスポンスとして帰ってきたセッションURLをvideo-jsにセットしています。実際に、ストリーミングを開始している状態で表示してみて動画が再生できれば完了です。

```haml
<html>

<head>
    <title>Kinesis Video Stream Test</title>
    <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet">
</head>

<body>
    <video-js id=example-video width=1280 height=720 class="vjs-default-skin" controls />
    <script src="https://vjs.zencdn.net/7.4.1/video.js"></script>
    <script>
        var player = videojs('example-video');
        var request = new XMLHttpRequest();
        request.open('GET', '作成したAPIのURL', true);
        request.onload = function () {
            player.src({
                src: this.response,
                type: "application/x-mpegURL"
            });
        }
        request.send();
        player.play();
    </script>
</body>

</html>
```

## まとめ
特に難しいところはなかったので楽しく開発していくことができました。改善できるところとしてはせっかくAPI Gatewayを使っているのでCognitoなどで認証を行えるようにすることや、ブラウザで動画を見てみるとタイムラグが大きかったのでその辺だと思っています。

## 参考ページ
[AWS Kinesis Video Stream ドキュメント](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/what-is-kinesis-video.html)
[コンビニで買った育てるサラダをrasPiで撮ってKinesisに流してゴニョゴニョする](https://qiita.com/ikegam1/items/0785b1723f2ae6ea310b)
[Amazon Kinesis Video Streamsを使ってストリーム映像をAWS上に流してみる](https://dev.classmethod.jp/cloud/aws/mac-kinesis-video-stream-getting-started/)