Amazon Kinesis Video Streams を使ってみた

## 概要

[Amazon Kinesis Video Streams](https://aws.amazon.com/jp/kinesis/video-streams/) は、AWS によるストリーミング動画分析・再生のためのサービスです。

Kinesis Video Streams に送信した映像は、分析や再生に使用することができます。いわゆる大規模同時配信をおこなうためのメディアサービスではなく、映像を保存・分析する用途に適しています。配信のためのプロビジョニングは不要でスケーラブル。動画の保存期間も 10 年まで設定できます。

![概要](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2022/08/14/86796ce2-1801-483b-9f61-c6b05f22fed3.png)

- 映像の送信 (Produce) は RTMP 等の標準的なプロトコルではなく独自 API のみ。SDK (C++/Java) を使ってフレームデータを送信するか、GStreamer でプラグインを使ってストリーミング配信する。
- 動画のコーデックは H.264/H.265 のみ、トランスコーディング機能はなし。

ここでは、GStreamer を使った動画配信と、HLS/MPEG-DASH による動画再生を試してみます。

## 料金

参考) [料金](https://aws.amazon.com/jp/kinesis/video-streams/pricing/)

東京リージョンの場合は以下のような感じです。

|項目|料金|
|:--|:--|
|配信 (Produce)|0.01097 USD/GB|
|閲覧 (Consume)|0.01097 USD/GB ※HLS の場合は 0.01536 USD/GB|
|ストレージ|0.025 USD/GB•月|

このほかにインターネットへのデータ転送料金(0.084~0.114 USD/GB)がかかります。インターネットからの受信、及び同一リージョン内のデータ転送料金はかかりません。

1 GB の目安として、2 Mbps の動画なら 1 時間ぶん。ストレージ料金も S3 と同程度なので、常時撮りためておいて任意の時刻の動画を再生する用途にも使えそうです。保存期間は 0 から設定可能なので、ライブ配信だけならストレージ料金も不要になります。(その場合、ストリームバッファは 5 分または 200 MB のいずれかが上限となるようです。)

## 準備) 認証情報の作成

配信用と閲覧用にそれぞれ必要な権限を与えた IAM ユーザーを作成し、アクセスキーを発行しておきます。

![ユーザー構成](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/11/30/b566d15b-da92-47f5-8b11-80e305675a90.png)

### 配信用 (kinesis-video-producer-test)

Kinesis Video Streams のストリームへ配信 (Produce) する権限を持つユーザーです。

```shell
$ aws iam create-user --user-name kinesis-video-producer-test
$ aws iam attach-user-policy --user-name kinesis-video-producer-test \
    --policy-arn arn:aws:iam::aws:policy/AmazonKinesisVideoStreamsFullAccess
$ aws iam create-access-key --user-name kinesis-video-producer-test
```

最小権限にする場合は以下のようにします。

```shell
$ aws iam create-user --user-name kinesis-video-producer-test
$ ACCOUNT_ID=$(aws sts get-caller-identity --output text --query "Account")
$ aws iam put-user-policy --user-name kinesis-video-producer-test \
    --policy-name ProduceToStream \
    --policy-document "{
        \"Version\": \"2012-10-17\",
        \"Statement\": [
            {
                \"Effect\": \"Allow\",
                \"Action\": [
                    \"kinesisvideo:CreateStream\",
                    \"kinesisvideo:DescribeStream\",
                    \"kinesisvideo:GetDataEndpoint\",
                    \"kinesisvideo:GetClip\"
                ],
                \"Resource\": \"arn:aws:kinesisvideo:ap-northeast-1:${ACCOUNT_ID}:stream/stream-test/*\"
            }
        ]
    }"
$ aws iam create-access-key --user-name kinesis-video-producer-test
```

### 閲覧用 (kinesis-video-consumer-test)

Kinesis Video Streams のストリームを閲覧 (Consume) する権限を持つユーザーです。

```shell
$ aws iam create-user --user-name kinesis-video-consumer-test
$ aws iam attach-user-policy --user-name kinesis-video-consumer-test \
    --policy-arn arn:aws:iam::aws:policy/AmazonKinesisVideoStreamsReadOnlyAccess
$ aws iam create-access-key --user-name kinesis-video-consumer-test
```

## ストリームの作成

Kinesis Video Streams に、ストリームを作成します。必要なパラメーターはストリーム名と、保存期間(0〜87600時間)のみです。

```shell
$ aws kinesisvideo create-stream --stream-name stream-test --data-retention-in-hours 24
{
    "StreamARN": "arn:aws:kinesisvideo:ap-northeast-1:(AccountNumber):stream/stream-test/XXXXXXXXXXXXX"
}
```

## 動画の配信 (Producer)

動画を Kinesis Video Streams に配信(Producde)するには、[Kinesis Video Streams Producer Library](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/producer-sdk.html) という C++/Java の SDK を使ってクライアントを開発するか、GStreamer で [Kinesis Video Streams プラグイン](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/examples-gstreamer-plugin.html) を使って配信することができます。

### GStreamer を使った動画配信

GStreamer を使った方法をいくつか試してみました。

#### GStreamer on Docker で静的な動画ファイルを配信する

![GStreamer on Docker で静的な動画ファイルを配信](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/03/04/7fe62fca-b7a8-4af5-83f2-e7661e50e6a9.png)

参考) [開発者ガイド - はじめに - Kinesis ビデオストリームにデータを送信する](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/gs-send-data.html)

SDK がインストールされたコンテナイメージが用意されているので、それに入っている GStreamer を使ってまずは動画ファイルを配信してみます。

```shell
$ aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 546150905175.dkr.ecr.us-west-2.amazonaws.com
$ docker pull 546150905175.dkr.ecr.us-west-2.amazonaws.com/kinesis-video-producer-sdk-cpp-amazon-linux:latest
$ docker run -it -v $(pwd):/work --network="host" \
    546150905175.dkr.ecr.us-west-2.amazonaws.com/kinesis-video-producer-sdk-cpp-amazon-linux \
    /bin/bash
```

動画ファイルをループさせて配信するため、以下のようなスクリプトを書きます。動画ファイルは [NHK クリエイティブライブラリー](https://www.nhk.or.jp/archives/creative/)等から適当にダウンロードしました。

```bash
// play.sh
#!/bin/sh

export LD_LIBRARY_PATH=/opt/awssdk/amazon-kinesis-video-streams-producer-sdk-cpp/build/downloads/local/lib:$LD_LIBRARY_PATH
export PATH=/opt/awssdk/amazon-kinesis-video-streams-producer-sdk-cpp/build/downloads/local/bin:$PATH
export GST_PLUGIN_PATH=/opt/awssdk/amazon-kinesis-video-streams-producer-sdk-cpp/build:$GST_PLUGIN_PATH

# Proxy サーバーを経由する場合
#export HTTPS_PROXY=http://192.168.1.1:8080

# kinesis-video-producer-test のアクセスキー
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export AWS_DEFAULT_REGION=ap-northeast-1
STREAM_NAME=stream-test
MOVIE_FILE=/work/sample.mp4

while [ 1 ]
do
    gst-launch-1.0 \
        filesrc location="${MOVIE_FILE}" \
        ! qtdemux \
        ! queue \
        ! h264parse \
        ! video/x-h264,stream-format=avc,alignment=au \
        ! kvssink stream-name="${STREAM_NAME}" storage-size=512
done
```

gst-launch-1.0 は「!」でつないでパイプラインを構成し、順番に各プラグインを処理していきます。

![GStreamer Pipeline (ファイルから)](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/12/07/5409f8a7-cb8c-4c3f-8a8c-dd8dd739db83.png)

- [coreelements: filesrc](https://gstreamer.freedesktop.org/documentation/coreelements/filesrc.html) : ファイルから読み込む
- [isomp4: qtdemux](https://gstreamer.freedesktop.org/documentation/isomp4/qtdemux.html) : メディアデータを取り出す
- [coreelements: queue](https://gstreamer.freedesktop.org/documentation/coreelements/queue.html) : データをある程度バッファに一旦貯めてから次へ渡す
- [videoparsersbad: h264parse](https://gstreamer.freedesktop.org/documentation/videoparsersbad/h264parse.html) : H.264 ストリームを解析
- [kvssink](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/examples-gstreamer-plugin-parameters.html) : Kinesis Video Streams に送信
  - storage-size にはバッファサイズ(MB)を指定する。

正しく配信されているかどうかは、マネジメントコンソールから動画を再生することで確認できます。

- Kinesis Video Streams > Vidoe streams > stream-test > Media playback

#### GStreamer on Raspberry Pi でカメラモジュールの動画を配信する

![GStreamer on Raspberry Pi でカメラモジュールの動画を配信](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/03/04/96ba63cf-820b-4c4d-8c8d-51004fe8d709.png)

参考) [Kinesis Video Streams Producer SDK GStreamer Plugin](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/examples-gstreamer-plugin.html)

GStreamer をパッケージでインストールします。

```shell
$ sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base-apps
$ sudo apt-get install -y gstreamer1.0-plugins-bad gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly gstreamer1.0-tools
$ sudo apt-get install -y gstreamer1.0-omx
```

Kinesis Video Streams Producer SDK GStreamer プラグインを SDK ドキュメントの手順に従ってビルドします。

```shell
$ sudo apt-get install -y cmake git libcurl4-openssl-dev liblog4cplus-dev libssl-dev m4 pkg-config
$ sudo mkdir -p /opt/awssdk && sudo chown $(whoami) /opt/awssdk && cd /opt/awssdk
$ git clone https://github.com/awslabs/amazon-kinesis-video-streams-producer-sdk-cpp
$ cd amazon-kinesis-video-streams-producer-sdk-cpp
$ mkdir build && cd build
$ cmake .. -DBUILD_GSTREAMER_PLUGIN=TRUE
$ make
```

Raspberry Pi のカメラモジュールを動画で配信します。

```bash
export LD_LIBRARY_PATH=/opt/awssdk/amazon-kinesis-video-streams-producer-sdk-cpp/build/downloads/local/lib:$LD_LIBRARY_PATH
export PATH=/opt/awssdk/amazon-kinesis-video-streams-producer-sdk-cpp/build/downloads/local/bin:$PATH
export GST_PLUGIN_PATH=/opt/awssdk/amazon-kinesis-video-streams-producer-sdk-cpp/build:$GST_PLUGIN_PATH

# Proxy サーバーを経由する場合
#export HTTPS_PROXY=http://192.168.1.1:8080

# kinesis-video-producer-test のアクセスキー
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export AWS_DEFAULT_REGION=ap-northeast-1
STREAM_NAME=stream-test

BPS=2048000
FPS=5
gst-launch-1.0 \
    v4l2src device=/dev/video0 \
    ! videoconvert \
    ! video/x-raw,format=I420,width=640,height=480,framerate=${FPS}/1 \
    ! omxh264enc control-rate=2 target-bitrate=${BPS} periodicity-idr=45 inline-header=FALSE \
    ! h264parse \
    ! video/x-h264,stream-format=avc,alignment=au,profile=baseline \
    ! kvssink stream-name="${STREAM_NAME}" storage-size=512
```

![GStreamer Pipeline (Raspberry Pi カメラから)](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/12/07/8dd1fa83-4413-4905-9a8f-53f2e86c03c7.png)

- [videoconvert: videoconvert](https://gstreamer.freedesktop.org/documentation/videoconvert/index.html) : フレームフォーマット変換
- [omxh264enc](https://developer.qualcomm.com/qualcomm-robotics-rb5-kit/software-reference-manual/application-semantics/gstreamer-plugins/omxh264enc) : H.264 ハードウェアエンコード
- [videoparsersbad: h264parse](https://gstreamer.freedesktop.org/documentation/videoparsersbad/h264parse.html) : H.264 ストリームを解析
- [kvssink](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/examples-gstreamer-plugin-parameters.html) : Kinesis Video Streams に送信

#### GStreamer on Raspberry Pi でネットワークカメラの動画を配信する

![GStreamer on Raspberry Pi でネットワークカメラの動画を配信](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/03/04/d76de311-4622-4488-9437-eeab73b81fed.png)

RTSP(MP4V) で取得できるネットワークカメラから配信する場合。

```bash
RTSP_URL=rtsp://...
BPS=2048000
gst-launch-1.0 \
    rtspsrc location="${RTSP_URL}" protocols=tcp \
    ! rtpmp4vdepay \
    ! v4l2mpeg4dec \
    ! videoconvert \
    ! video/x-raw,format=I420,width=640,height=480 \
    ! omxh264enc control-rate=2 target-bitrate=${BPS} periodicity-idr=15 inline-header=FALSE \
    ! h264parse \
    ! video/x-h264,stream-format=avc,alignment=au,profile=baseline \
    ! kvssink stream-name="${STREAM_NAME}" storage-size=512
```

![GStreamer Pipeline (Raspberry Pi カメラから)](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/12/07/aa715b9c-a701-4fdb-98d6-4f134199e2c8.png)

- [rtp: rtpmp4vdepay](https://gstreamer.freedesktop.org/documentation/rtp/rtpmp4vdepay.html) : RTP パケットからペイロードを取得
- v4l2mpeg4dec : MPEG4 デコード
- [videoconvert: videoconvert](https://gstreamer.freedesktop.org/documentation/videoconvert/index.html) : フレームフォーマット変換
- [omxh264enc](https://developer.qualcomm.com/qualcomm-robotics-rb5-kit/software-reference-manual/application-semantics/gstreamer-plugins/omxh264enc) : H.264 ハードウェアエンコード
- [videoparsersbad: h264parse](https://gstreamer.freedesktop.org/documentation/videoparsersbad/h264parse.html) : H.264 ストリームを解析
- [kvssink](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/examples-gstreamer-plugin-parameters.html) : Kinesis Video Streams に送信

#### GStreamer on Mac で内蔵カメラの動画を配信する

![GStreamer on Mac で内蔵カメラの動画を配信](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/03/05/c58ba036-7b20-4074-b68f-05e66c7b370d.png)

GStreamer/Docker による動画ファイルの配信と同様に Docker 環境を使いますが、Docker 環境内からは直接内蔵カメラを扱うことができないため、ホストで内蔵カメラをストリーム -> Docker 内ストリーム -> Kiniesis Video Streams という流れにします。

##### Mac 環境

```shell
# 初回のみ: gstreamer をインストール
brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad
brew install gst-plugins-ugly

# ローカルで、内蔵カメラのストリームサーバを立てる
gst-launch-1.0 \
    autovideosrc \
    ! videoconvert \
    ! video/x-raw,width=1280,height=720 \
    ! vtenc_h264_hw allow-frame-reordering=FALSE realtime=TRUE max-keyframe-interval=45 bitrate=512 \
    ! gdppay ! tcpserversink host=127.0.0.1
```

##### Docker 環境

```shell
$ aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 546150905175.dkr.ecr.us-west-2.amazonaws.com
$ docker pull 546150905175.dkr.ecr.us-west-2.amazonaws.com/kinesis-video-producer-sdk-cpp-amazon-linux:latest
$ docker run -it -v $(pwd):/work --network="host" \
    546150905175.dkr.ecr.us-west-2.amazonaws.com/kinesis-video-producer-sdk-cpp-amazon-linux \
    /bin/bash
```

```inside-docker.shell
export LD_LIBRARY_PATH=/opt/awssdk/amazon-kinesis-video-streams-producer-sdk-cpp/build/downloads/local/lib:$LD_LIBRARY_PATH
export PATH=/opt/awssdk/amazon-kinesis-video-streams-producer-sdk-cpp/build/downloads/local/bin:$PATH
export GST_PLUGIN_PATH=/opt/awssdk/amazon-kinesis-video-streams-producer-sdk-cpp/build:$GST_PLUGIN_PATH

# kinesis-video-producer-test のアクセスキー
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export AWS_DEFAULT_REGION=ap-northeast-1
STREAM_NAME=stream-test

# ホストで立てたストリームを読んで、Kinesis Video Streams に流す
gst-launch-1.0 \
    tcpclientsrc host=host.docker.internal \
    ! gdpdepay \
    ! h264parse \
    ! video/x-h264,stream-format=avc,alignment=au \
    ! kvssink stream-name="${STREAM_NAME}" storage-size=512
```

参考) [\[Kinesis Video Streams\] DockerイメージのプロデューサーライブラリでMacのカメラを使用してみました \| Developers\.IO](https://dev.classmethod.jp/articles/kinesis-video-streams-docker-with-mac-cam/)

## 動画の閲覧 (Consumer)

参考) [ビデオストリーム再生](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/how-playback.html)

Kinesis Video Streams に配信(Produce)された動画を再生(Consume)するには、[Kinesis Video Streams Parser Library](https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/parser-library.html) という C++/Java の SDK を使ってプレイヤーを開発するか、HLS または MPEG-DASH プレイヤーを使って再生することができます。[HLS (HTTP Live Streaming)](https://developer.apple.com/streaming/) は Apple が開発したもの、[MPEG-DASH (Dynamic Adaptive Streaming over HTTP)](https://mpeg.chiariglione.org/standards/mpeg-dash) は後発の国際規格で、いずれも標準的なものです。

### HLS で閲覧する

![HLS で閲覧](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/03/04/efda4624-c084-41e4-a9d1-362279696e68.png)

ブラウザ上では [Video.js](https://github.com/videojs/video.js/) または [Google Shaka Player](https://github.com/google/shaka-player) といったライブラリを使って再生できます。[hls.js](https://github.com/video-dev/hls.js/) もたぶん大丈夫。Safari, Edge はブラウザ自体がネイティブ対応しているので URL 直接入力でも再生できるようです。

再生用のセッション URL は以下の手順で取得します。セッション URL は有効期限を持ったセッショントークンが埋め込まれた一時的なものになります。

![](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2022/08/14/29228064-765d-4b18-8eb1-9b9a17ae427a.png)

 1. [GetDataEndpoint](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_GetDataEndpoint.html) API で、ストリーム用の API エンドポイントを取得する。
 2. 取得した API エンドポイントに対して [GetHLSStreamingSessionURL](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_reader_GetHLSStreamingSessionURL.html) API を呼び、セッション URL を取得する。
    例) `https://b-87178fb5.kinesisvideo.ap-northeast-1.amazonaws.com/hls/v1/getHLSMasterPlaylist.m3u8?SessionToken=XXXXXXX`

CLI でセッション URL を取得する例。取得したセッション URL をプレイヤーに指定すれば再生できます。

```shell
ENDPOINT_URL=$(aws kinesisvideo get-data-endpoint \
    --stream-name ${STREAM_NAME} \
    --api-name GET_HLS_STREAMING_SESSION_URL \
    --output text)
aws kinesis-video-archived-media get-hls-streaming-session-url \
    --endpoint ${ENDPOINT_URL} \
    --stream-name ${STREAM_NAME}
```

JavaScript でセッション URL を取得し、Video.js に再生させる場合の例。ここではとりあえず HTML にアクセスキーを埋め込んでいますが、ユーザーに丸見えになるので、通常はバックエンド側で URL を発行します。[GetHLSStreamingSessionURL](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_reader_GetHLSStreamingSessionURL.html) のオプションで、再生モード、再生する動画の日時、動画のフォーマット(MPEG-TS or Fragmented MP4)や有効期限などを指定します。

```html
<!DOCTYPE html>
<html>

<head>
    <link href="https://vjs.zencdn.net/7.7.5/video-js.css" rel="stylesheet" />
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.642.0.min.js"></script>
    <title>HLS Player</title>
</head>

<body>
    <video id="videojs" class="player video-js vjs-default-skin" controls autoplay></video>

    <script src="https://vjs.zencdn.net/7.7.5/video.min.js"></script>
    <script>
        async function getURL() {
            // kinesis-video-consumer-test のアクセスキー
            const accessKeyId = 'XXXXXXXXXXXXXXXXXXXX';
            const secretAccessKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
            const region = 'ap-northeast-1';
            const streamName = 'stream-test';

            const options = {
                accessKeyId: accessKeyId,
                secretAccessKey: secretAccessKey,
                region: region,
            }
            const kinesisVideoClient = new AWS.KinesisVideo(options);
            const kinesisVideoArchivedMediaClient = new AWS.KinesisVideoArchivedMedia(options);

            // GetDataEndpoint
            // https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_GetDataEndpoint.html 
            const e = await kinesisVideoClient.getDataEndpoint({
                APIName: 'GET_HLS_STREAMING_SESSION_URL',
                StreamName: streamName
            }).promise();
            kinesisVideoArchivedMediaClient.endpoint = new AWS.Endpoint(e.DataEndpoint);

            // GetHLSStreamingSessionURL API
            // https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_reader_GetHLSStreamingSessionURL.html
            const d = await kinesisVideoArchivedMediaClient.getHLSStreamingSessionURL({
                //ContainerFormat: 'FRAGMENTED_MP4', // or 'MPEG_TS'
                //DiscontinuityMode: 'ALWAYS', // or 'NEVER' or 'ON_DISCONTINUITY'
                //DisplayFragmentTimestamp: 'NEVER', // or 'ALWAYS'
                //Expires: 300,
                //PlaybackMode: 'LIVE', // or 'LIVE_REPLAY' or 'ON_DEMAND'
                StreamName: streamName
            }).promise();
            return d.HLSStreamingSessionURL;
        }

        document.addEventListener('DOMContentLoaded', async () => {
            const url = await getURL();
            const player = videojs('videojs');
            player.src({
                src: url,
                type: 'application/x-mpegURL'
            });
        });
    </script>
</body>

</html>
```

指定した日時から追っかけ再生する場合。開始日時を指定する。

```js
    const d = await kinesisVideoArchivedMediaClient.getHLSStreamingSessionURL({
        HLSFragmentSelector: {
            FragmentSelectorType: 'PRODUCER_TIMESTAMP',
            TimestampRange: {
                StartTimestamp: 1588912753 // 再生開始タイムスタンプ
            }
        },
        PlaybackMode: 'LIVE_REPLAY',
        StreamName: streamName
    }).promise();
```

過去の動画を再生する場合の例。プレイヤー側で動画内の任意の日時にシークできる。

```js
    const d = await kinesisVideoArchivedMediaClient.getHLSStreamingSessionURL({
        PlaybackMode: 'ON_DEMAND',
        HLSFragmentSelector: {
            FragmentSelectorType: 'PRODUCER_TIMESTAMP',
            TimestampRange: {
                StartTimestamp: 1588912753, // 再生開始タイムスタンプ
                EndTimestamp: 1588912813, // 再生終了タイムスタンプ
            }
        },
        StreamName: streamName
    }).promise();
```

### MPEG-DASH で閲覧する

![MPEG-DASH で閲覧](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/03/04/a5d3aae6-28ee-442e-b190-3ed4c476065a.png)

[Dash.js](https://github.com/Dash-Industry-Forum/dash.js/wiki) または [Google Shaka Player](https://github.com/google/shaka-player) といったライブラリで再生できます。[Video.js](https://github.com/videojs/video.js/) 7 (includes [videojs-http-streaming](https://github.com/videojs/http-streaming)) で再生できました。

再生用のセッション URL は HLS と同様の方法で取得できます。取得した URL は有効期限を持ったセッショントークンが埋め込まれた一時的なものになります。MPEG-DASH の場合、動画のフォーマットは Fragmented MP4 固定です。

![](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2022/08/14/65b73664-3e8c-4fd4-ae34-a43d6b48bd4b.png)

 1. [GetDataEndpoint](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_GetDataEndpoint.html) API で、ストリーム用の API エンドポイントを取得する。
 2. 取得した API エンドポイントに対して [GetDASHStreamingSessionURL](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_reader_GetDASHStreamingSessionURL.html) API を呼び、セッション URL を取得する。

CLI でセッション URL を取得する例。取得したセッション URL をプレイヤーに指定すれば再生できます。

```shell
ENDPOINT_URL=$(aws kinesisvideo get-data-endpoint \
    --stream-name ${STREAM_NAME} \
    --api-name GET_DASH_STREAMING_SESSION_URL \
    --output text)
aws kinesis-video-archived-media get-dash-streaming-session-url \
    --endpoint ${ENDPOINT_URL} \
    --stream-name ${STREAM_NAME}
```

JavaScript でセッション URL を取得し、Video.js に再生させる場合の例。ここではとりあえず HTML にアクセスキーを埋め込んでいますが、ユーザーに丸見えになるので、通常はバックエンド側で URL を発行します。[GetDASHStreamingSessionURL](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_reader_GetDASHStreamingSessionURL.html) のオプションで、再生モード、再生する動画の日時やセッションの有効期限などを指定します。

```html
<!DOCTYPE html>
<html>

<head>
    <link href="https://vjs.zencdn.net/7.7.5/video-js.css" rel="stylesheet" />
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.642.0.min.js"></script>
    <title>MPEG-DASH Player</title>
</head>

<body>
    <video id="videojs" class="player video-js vjs-default-skin" controls autoplay></video>

    <script src="https://vjs.zencdn.net/7.7.5/video.min.js"></script>
    <script>
        async function getURL() {
            // kinesis-video-consumer-test のアクセスキー
            const accessKeyId = 'XXXXXXXXXXXXXXXXXXXX';
            const secretAccessKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
            const region = 'ap-northeast-1';
            const streamName = 'stream-test';

            const options = {
                accessKeyId: accessKeyId,
                secretAccessKey: secretAccessKey,
                region: region,
            }
            const kinesisVideoClient = new AWS.KinesisVideo(options);
            const kinesisVideoArchivedMediaClient = new AWS.KinesisVideoArchivedMedia(options);

            // GetDataEndpoint
            // https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_GetDataEndpoint.html 
            const e = await kinesisVideoClient.getDataEndpoint({
                APIName: 'GET_DASH_STREAMING_SESSION_URL',
                StreamName: streamName
            }).promise();
            kinesisVideoArchivedMediaClient.endpoint = new AWS.Endpoint(e.DataEndpoint);

            // GetDASHStreamingSessionURL API
            // https://docs.aws.amazon.com/ja_jp/kinesisvideostreams/latest/dg/API_reader_GetDASHStreamingSessionURL.html
            const d = await kinesisVideoArchivedMediaClient.getDASHStreamingSessionURL({
                //DisplayFragmentTimestamp: 'NEVER', // or 'ALWAYS'
                //Expires: 300,
                //PlaybackMode: 'LIVE', // or 'LIVE_REPLAY' or 'ON_DEMAND'
                StreamName: streamName
            }).promise();
            return d.DASHStreamingSessionURL;
        }

        document.addEventListener('DOMContentLoaded', async () => {
            const url = await getURL();
            const player = videojs('videojs');
            player.src({
                src: url,
                type: 'application/dash+xml'
            });
        });
    </script>
</body>

</html>
```

指定した日時から追っかけ再生する場合。開始日時を指定する。

```js
    const d = await kinesisVideoArchivedMediaClient.getDASHStreamingSessionURL({
        DASHFragmentSelector: {
            FragmentSelectorType: 'PRODUCER_TIMESTAMP',
            TimestampRange: {
                StartTimestamp: 1588912753 // 再生開始タイムスタンプ
            }
        },
        PlaybackMode: 'LIVE_REPLAY',
        StreamName: streamName
    }).promise();
```

過去の動画を再生する場合の例。プレイヤー側で動画内の任意の日時にシークできる。

```js
    const d = await kinesisVideoArchivedMediaClient.getDASHStreamingSessionURL({
        DASHFragmentSelector: {
            FragmentSelectorType: 'PRODUCER_TIMESTAMP',
            TimestampRange: {
                StartTimestamp: 1588912753, // 再生開始タイムスタンプ
                EndTimestamp: 1588912813, // 再生終了タイムスタンプ
            }
        },
        PlaybackMode: 'ON_DEMAND',
        StreamName: streamName
    }).promise();
```

### その他 API

保存された映像を取得する API としては、以下のものがあります。

- [GetMedia](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_dataplane_GetMedia.html) : メディアデータを取得する
  - CLI: [aws kinesis-video-media get-media](https://docs.aws.amazon.com/cli/latest/reference/kinesis-video-media/get-media.html)
- [ListFragments](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_reader_ListFragments.html) : ストリームの指定した位置からフラグメントのリストを取得する
  - CLI: [aws kinesis-video-archived-media list-fragments](https://docs.aws.amazon.com/cli/latest/reference/kinesis-video-archived-media/list-fragments.html)
- [GetMediaFromFragmentList](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_reader_GetMediaForFragmentList.html) : 指定したフラグメントのメディアデータを取得する
  - CLI: [aws kinesis-video-archived-media get-media-for-fragment-list](https://docs.aws.amazon.com/cli/latest/reference/kinesis-video-archived-media/get-media-for-fragment-list.html)
- [GetClip](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_reader_GetClip.html) : ストリームから指定した範囲の mp4 ファイルを取得する
  - CLI: [aws kinesis-video-archived-media get-clip](https://docs.aws.amazon.com/cli/latest/reference/kinesis-video-archived-media/get-clip.html)

#### GetMedia: メディアデータを取得する

この API は単純なリクエスト・レスポンスではなくメディアデータを連続的に受信するため、停止するまでファイルに追記します。

現在以降から受信する場合

```shell
aws --endpoint="$(aws kinesisvideo get-data-endpoint --stream-name "${STREAM_NAME}" --api-name GET_MEDIA --output text --query "DataEndpoint")" \
    kinesis-video-media get-media \
    --stream-name "${STREAM_NAME}" \
    --start-selector "{
        \"StartSelectorType\": \"NOW\"
    }" \
    -- video.webm
```

指定日時から受信する場合

```shell
START_DATETIME=2022-01-13T11:00:00+09:00
aws --endpoint="$(aws kinesisvideo get-data-endpoint --stream-name "${STREAM_NAME}" --api-name GET_MEDIA --output text --query "DataEndpoint")" \
    kinesis-video-media get-media \
    --stream-name "${STREAM_NAME}" \
    --start-selector "{
        \"StartSelectorType\": \"PRODUCER_TIMESTAMP\",
        \"StartTimestamp\": \"${START_DATETIME}\"
    }" \
    -- video.webm
```

#### ListFragments: ストリームの指定した位置からフラグメントのリストを取得する

```shell
START_DATETIME=2022-01-13T13:00:00+09:00
END_DATETIME=2022-01-13T14:00:00+09:00
aws --endpoint="$(aws kinesisvideo get-data-endpoint --stream-name "${STREAM_NAME}" --api-name LIST_FRAGMENTS --output text --query "DataEndpoint")" \
    kinesis-video-archived-media list-fragments \
    --stream-name "${STREAM_NAME}" \
    --fragment-selector "{
        \"FragmentSelectorType\": \"PRODUCER_TIMESTAMP\",
        \"TimestampRange\": {
            \"StartTimestamp\": \"${START_DATETIME}\",
            \"EndTimestamp\": \"${END_DATETIME}\"
        }
    }"
```

フラグメントごとの、以下のメタデータが取得できます。

- FragmentNumber : フラグメント番号
- FragmentSizeInBytes : フラグメントデータサイズ (Bytes)
- ProducerTimestamp : 送信タイムスタンプ (UNIX EPOCH, ms まで)
- ServerTimestamp : 受信タイムスタンプ (UNIX EPOCH, ms まで)
- FragmentLengthInMilliseconds : フラグメントの長さ (ms)

#### GetMediaFromFragmentList: 指定したフラグメントのメディアデータを取得する

ListFragments API で取得した一覧から、FragmentNumber を指定します。

```shell
FRAGMENT_NUMBER=91343852333181969173386617097861910005048270499
aws --endpoint="$(aws kinesisvideo get-data-endpoint --stream-name "${STREAM_NAME}" --api-name GET_MEDIA_FOR_FRAGMENT_LIST --output text --query "DataEndpoint")" \
    kinesis-video-archived-media get-media-for-fragment-list \
    --stream-name "${STREAM_NAME}" \
    --fragments "${FRAGMENT_NUMBER}" \
    -- video.webm
```

#### GetClip: ストリームから指定した範囲の mp4 ファイルを取得する

最大で 100 MB または 200 フラグメントまでとなっており、一度に長時間の動画を取得することはできません。

```shell
START_DATETIME=2022-01-13T13:45:00+09:00
END_DATETIME=2022-01-13T13:50:00+09:00
aws --endpoint="$(aws kinesisvideo get-data-endpoint --stream-name "${STREAM_NAME}" --api-name GET_CLIP --output text --query "DataEndpoint")" \
    kinesis-video-archived-media get-clip \
    --stream-name "${STREAM_NAME}" \
    --clip-fragment-selector "{
        \"FragmentSelectorType\": \"PRODUCER_TIMESTAMP\",
        \"TimestampRange\": {
            \"StartTimestamp\": \"${START_DATETIME}\",
            \"EndTimestamp\": \"${END_DATETIME}\"
        }
    }" \
    -- video.mp4
```