React と Video.js を使って Amazon IVS プレイヤーを実装する

2021 年より streampack チームに join した endo です。
Qiita に投稿するのは初めてですが、よろしくお願いします。

## はじめに

Amazon Interactive Video Service (IVS) のプレイヤーを実装する機会があったので書いていこうと思います。

## 前提

React を使用したプロジェクトにて Video.js が組み込まれている状態を想定しています。
一応ゼロからのセットアップ手順を記載していますが、詳細については各々の公式サイトをご参照ください。

## React のセットアップ

`create-react-app`を使って React の環境を構築します。

```shell:shell
npx create-react-app 【アプリ名】
```

[https://ja.reactjs.org/docs/create-a-new-react-app.html](https://ja.reactjs.org/docs/create-a-new-react-app.html)

## Video.js のインスール

Video.js が組み込まれていない場合 npm or yarn 経由で Video.js をインストールしてください。

```shell:shell
$ npm install --save-dev video.js
```

[https://videojs.com/getting-started/#install-via-npm](https://videojs.com/getting-started/#install-via-npm)

## プレイヤーの実装

[AWS のドキュメント](https://docs.aws.amazon.com/ivs/latest/userguide/player-videojs.html)を参考に実装していきます。

注意点として気をつけたいのが IVS プレイヤーを React で実装する場合 npm or yarn でインストールすると現時点ではエラーとなるということです。  
したがって、エラーを回避するために動的に JS を script タグで読み込みます。

### App.js の修正

AmazonIVSPlayer という名前でコンポーネントを作成し import します。

```js:App.js
import React from "react";
import AmazonIVSPlayer from "./components/AmazonIVSPlayer";

const App = () => {
  return <AmazonIVSPlayer />;
}

export default App;
```

### プレイヤー部分のコーディング

まずは`Video.js`を import します。

```js
import videojs from "video.js";
import "video.js/dist/video-js.css";
```

その後、動的に`amazon-ivs-videojs-tech.min.js`を読み込みます。

```js
const script = document.createElement("script");
script.src =
  "https://player.live-video.net/1.2.0/amazon-ivs-videojs-tech.min.js";
document.body.appendChild(script);
```

`registerIVSTech`関数を使って Video.js の登録をします。option がある時は第二引数に渡します。

```js
registerIVSTech(videojs, options);
```

Video.js を使ってプレイヤーの初期化をします。`techOrder`オプションで`AmazonIVS`を追加します。

```js
const player = videojs(
  "amazon-ivs-videojs",
  {
    techOrder: ["AmazonIVS"],
  },
  () => {
    // Play stream
    player.src(PLAYBACK_URL);
  }
);
```

全体的なコードは以下のようになります。

```js:AmazonIVSPlayer.js
/* global registerIVSTech  */
import React, { useEffect, useRef } from "react";
import videojs from "video.js";
import "video.js/dist/video-js.css";

const AmazonIVSPlayer = () => {
  const playerRef = useRef();

  useEffect(() => {
    const script = document.createElement("script");
    script.src =
      "https://player.live-video.net/1.2.0/amazon-ivs-videojs-tech.min.js";
    document.body.appendChild(script);

    script.addEventListener("load", () => {
      const PLAYBACK_URL =
        "【動画URL】";

      registerIVSTech(videojs);

      // Initialize player
      const player = videojs(
        "amazon-ivs-videojs",
        {
          techOrder: ["AmazonIVS"],
        },
        () => {
          console.log("Player is ready to use!");

          player.src(PLAYBACK_URL);
        }
      );

      playerRef.current = player;
    });

    return () => {
      playerRef.current.dispose();
      document.body.removeChild(script);
    };
  }, []);

  return (
    <div>
      <video
        id="amazon-ivs-videojs"
        className="video-js vjs-4-3 vjs-big-play-centered"
        controls
        autoPlay
        playsInline
        muted
      ></video>
    </div>
  );
};

export default AmazonIVSPlayer;
```

## 画質選択機能を付けてみる

画質選択機能をつけるにはプラグインを追加します。

`amazon-ivs-videojs-tech.min.js`に加えて、`amazon-ivs-quality-plugin.min.js`を読み込む必要があります。

動的に複数の JS を読み込むことになるので、全ての JS のロードを待つ必要があります。

```js
const loadScripts = [
  {
    src: "https://player.live-video.net/1.2.0/amazon-ivs-videojs-tech.min.js",
  },
  {
    src: "https://player.live-video.net/1.2.0/amazon-ivs-quality-plugin.min.js",
  },
];

// counterを用意して全てのJSの読み込みが終わったらプレイヤーを初期化する
let counter = 0;
const counterMax = loadScripts.length;

loadScripts.forEach((scr, i) => {
  const script = document.createElement("script");
  script.src = scr.src;
  document.body.appendChild(script);
  loadScripts[i].ref = script;

  script.addEventListener("load", () => {
    counter++;
    if (counter === counterMax) {
      // ここからプレイヤーの初期化処理
      // ...
    }
  });
});
```

`registerIVSQualityPlugin`関数を使って Video.js を登録し、

```js
registerIVSQualityPlugin(videojs);
```

プレイヤーの初期化時にプラグインの有効化をします。

```js
player.enableIVSQualityPlugin();
```

全体的には以下のようなコードになると思います。

```js:AmazonIVSPlayer.js
/* global registerIVSTech , registerIVSQualityPlugin */
import React, { useEffect, useRef } from "react";
import videojs from "video.js";
import "video.js/dist/video-js.css";

const AmazonIVSPlayer = () => {
  const playerRef = useRef();

  useEffect(() => {
    const loadScripts = [
      {
        src:
          "https://player.live-video.net/1.2.0/amazon-ivs-videojs-tech.min.js",
        ref: null,
      },
      {
        src:
          "https://player.live-video.net/1.2.0/amazon-ivs-quality-plugin.min.js",
        ref: null,
      },
    ];

    let counter = 0;
    const counterMax = loadScripts.length;

    loadScripts.forEach((scr, i) => {
      const script = document.createElement("script");
      script.src = scr.src;
      document.body.appendChild(script);
      loadScripts[i].ref = script;

      script.addEventListener("load", () => {
        counter++;
        if (counter === counterMax) {
          const PLAYBACK_URL =
             "【動画URL】";

          registerIVSTech(videojs);
          registerIVSQualityPlugin(videojs);

          // Initialize player
          const player = videojs(
            "amazon-ivs-videojs",
            {
              techOrder: ["AmazonIVS"],
            },
            () => {
              console.log("Player is ready to use!");

              // Play stream
              player.src(PLAYBACK_URL);

              player.enableIVSQualityPlugin();
            }
          );

          playerRef.current = player;
        }
      });
    });

    return () => {
      playerRef.current.dispose();
      loadScripts.forEach(scr => {
        document.body.removeChild(scr.ref);
      });
    };
  }, []);

  return (
    <div>
      <video
        id="amazon-ivs-videojs"
        className="video-js vjs-4-3 vjs-big-play-centered"
        controls
        autoPlay
        playsInline
        muted
      ></video>
    </div>
  );
};

export default AmazonIVSPlayer;
```

## まとめ

Reactを使ってのIVSプレイヤーの実装でした。
プレイヤーのUIを作るのは結構大変なので、 Video.js に対応しているのは非常にありがたいですね。

## 参考 URL

[https://docs.aws.amazon.com/ivs/latest/userguide/player.html](https://docs.aws.amazon.com/ivs/latest/userguide/player.html)
[https://aws.github.io/amazon-ivs-player-docs/1.2.0/web/](https://aws.github.io/amazon-ivs-player-docs/1.2.0/web/)
[https://docs.videojs.com/](https://docs.videojs.com/)
[https://dev.classmethod.jp/articles/workaround-react-ivs/)](https://dev.classmethod.jp/articles/workaround-react-ivs/)