Genvid SDKで動画視聴者の操作が反映されるまでをカウントダウン


#はじめに: Genvid SDKについて
Genvidは、動画ストリーミングとブラウザを介したインタラクティブな体験を組み合わせた「大規模インタラクティブ・ライブ・イベント」（Massive Interactive Live Events）を実現するSDKです。
https://www.genvidtech.com/ja/mile%E3%81%A8%E3%81%AF%EF%BC%9F/

ゲーム技術をベースに、リアルタイムで進行する動画番組に対して、動画視聴者が能動的に参加できるシステムを提供します。
コンテンツ内のキャラクターが次に何をするかを投票で決めたり、ミニゲームをプレイしてポイントをため、特定のキャラクターを応援するなどの活動を経て、物語が変化していきます。
MILEは、Unityを使いFacebook上で配信されている「Rival Peak」と、Unreal Engine 4を使った「Project Raven」があります。

『RIVAL PEAK』が示す次世代の視聴者参加型デジタルエンタテインメント
https://news.yahoo.co.jp/byline/onokenji/20210326-00229353

Genvid SDKの導入については、Genvidディベロッパーサイトの日本語マニュアルからご確認ください。
https://www.genvidtech.com/for-developers/

# 視聴者の操作が反映されるまでカウントダウンする
ストリーミング動画コンテンツに視聴者インタラクションをつけようとした場合は、視聴者が何らかの入力をしてからコンテンツ側に反映され、それが動画で見えるまでにタイムラグが発生します。
「コメント連動」などで視聴者インタラクションを実装する場合、視聴者がコメントしてからその操作が反映されるまでにラグがあり、またコメントはどんどん流れてしまうので、「操作した感」「参加している感」がどうしても薄まってしまいます。

Genvidは動画コンテンツに視聴者が参加できる仕組みとして、動画の上に操作を表示する「オーバーレイ」を使用します。
加えて、「現在ブラウザー側ではどの程度の遅延が発生しているか」を数値として取得できますので、これを使えば「視聴者の操作がいつ反映されるか」を正確にカウントダウン表示できます。

以下はUnityを使った簡単なデモです。ゲームをキャプチャしたものではなく、ブラウザに配信されているストリーミング動画をキャプチャしています。
左上の緑色のボタンは動画の上からブラウザ上でHTMLのボタン要素を重ねて描画しています。
（分かりやすいように設定で６秒の遅延を加えています）

![dropbombgif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19010/818624f5-b38e-9f66-2201-ae300d0993cb.gif)

ボタンをクリックすると、そのクリック操作があと何秒で「ストリーミング動画上で」描写されるのかを正確にカウントダウンします。
これにより、動画視聴者に「自分の操作がちゃんと反映されたのか」という不安を解消できます。

# IGenvidClient.streamLatencyMS を活用する

具体的な実装方法を見ていきましょう。
ブラウザ側のGenvidのクライアントスクリプトから、フィールドstreamLatencyMSからストリーム遅延 (ミリ秒)が取得できます。

IGenvidClient.streamLatencyMS 
https://www.genvidtech.com/doc/SDK-1.32.0/reference/web_sdk/js/genvidClient.IGenvidClient.html

ボタンを押してGenvid Eventを投げる簡単なスクリプトで解説します。

# サンプルコード

```js
var genvidClient;
let delayToColorSwitchS = 0;

fetch("/api/public/channels/join", { method: "post" })
    .then(function (data) { return data.json() })
    .then(function (response) {
        genvidClient = genvid.createGenvidClient(response.info, response.uri, response.token, "video_player");
        genvidClient.start();
    })
    .catch(function (e) { console.log(e) })

dropBomb.onclick = function () {
    genvidClient.sendEvent([{
        "key": ["dropBomb"],
        "value": "1"
    }]);

    //delayToColorSwitchSに秒単位でボタン押した瞬間の遅延を保持
    delayToColorSwitchS = genvidClient.streamLatencyMS / 1000;
}

setInterval(function () {
    if (delayToColorSwitchS > 0)
    {
        dropBomb.textContent = Math.ceil(delayToColorSwitchS);
        delayToColorSwitchS -= 0.01;
    } else {
        dropBomb.textContent = "DropBomb";
    }
}
, 10);
```

仕組みは単純で、ボタンが押された瞬間に現在の遅延を保存し、setIntervalを使ってその値を減算＋画面表示に反映します。
（他フレームワークとの組み合わせでもっと簡潔に書けると思います）

各処理について説明します。

## Genvid Clientの初期化

まずはGenvid Clientの初期化です。これはサーバー側のコンテンツからデータを配信する「Genvid Stream」を含まない実装のため、最小限の手続きで初期化を行っているものです。

```js
var genvidClient;
let delayToColorSwitchS = 0;

fetch("/api/public/channels/join", { method: "post" })
    .then(function (data) { return data.json() })
    .then(function (response) {
        genvidClient = genvid.createGenvidClient(response.info, response.uri, response.token, "video_player");
        genvidClient.start();
    })
    .catch(function (e) { console.log(e) })
```
また、フィールドdelayToColorSwitchSを後の処理のために用意します。

## ボタン操作に合わせてsendEventを呼ぶ

次に、html側で定義しているボタン「dropBomb」のクリック操作に、GenvidClient.sendEventを紐づけます。
この時、ボタンをクリックしたタイミングでフィールドdelayToColorSwitchSにそのときの遅延秒数を保存します。

```js
dropBomb.onclick = function () {
    genvidClient.sendEvent([{
        "key": ["dropBomb"],
        "value": "1"
    }]);

    //delayToColorSwitchSに秒単位でボタン押した瞬間の遅延を保持
    delayToColorSwitchS = genvidClient.streamLatencyMS / 1000;
}
```
送信している配列は、あらかじめGenvidサーバー側でJSONスキーマを使って設定しておく必要があります。手順については以下のポストをご確認ください。

Genvid「Event」jsonスキーマ設定例
https://qiita.com/Takaaki_Ichijo/items/0eae7709741774722483


## フィールドに保存した遅延秒数を監視してカウントダウン

最後に定期実行であるsetIntervalを使って、秒数のカウントダウン処理を行います。

```js
setInterval(function () {
    if (delayToColorSwitchS > 0)
    {
        dropBomb.textContent = Math.ceil(delayToColorSwitchS);
        delayToColorSwitchS -= 0.01;
    } else {
        dropBomb.textContent = "DropBomb";
    }
}
, 10);
```


# 応用方法

今回は単純なボタン操作のカウントダウンに使用しました。
応用として、ボタンが押せるタイミングのインターバル調整に利用したり、カウントがゼロになったタイミングでオーバーレイ側にも何らかのアニメーションを表示して動画と同期した演出をするなどの活用もできます。

動画と同期した動きをブラウザ側でもレスポンスよく出すことで、動画視聴者が「コンテンツに参加」している感覚を強めることができます。
ぜひいろいろと試してみて下さい。