SkyWayの公式のチュートリアルをやってビデオ通話を作ってみた

# はじめに

こんにちは、プログラミングを始めて約3年のエンジニアのkeitaMaxです。

今回は、SkyWayの公式に従ってチュートリアルをこなして、音声・ビデオ通話アプリを作成していこうと思います。

# SkyWayに登録する
下記サイトでまずはアカウント登録をします。無料でも使用できるので、今回は無料版に登録をしました。

https://console.skyway.ntt.com/

公式のガイドに従って登録、メール認証をしてプロジェクトとアプリを作成します。

アプリまで作成すると、『アプリケーションキー』と『シークレットキー』が発行されますのでメモっときます。

# 実際にローカル環境で通話をつなぐ
SkyWay開発ドキュメントのなかにクイックスタートという項目があったので、それに従ってブラウザのアプリケーションを作成します。

https://skyway.ntt.com/ja/docs/user-guide/javascript-sdk/quickstart/

まずは環境作成をします。公式に載っている通りに、以下の手順で開発環境を作成しました。

>3-1. npm init を実行し、対話画面が終了するまで Enter キーをクリックする
3-2. npm i @skyway-sdk/room を実行する
3-3. npm i -D parcel を実行する
3-5. package.json ファイルを開き、scripts の階層に "dev": "parcel ./index.html", を追記する
(引用元：https://skyway.ntt.com/ja/docs/user-guide/javascript-sdk/quickstart/)

次に公式ドキュメントにあるように、index.htmlとmain.jsを作成しました。念のため、今回作成したコードも書いておきます。

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>SkyWay Tutorial</title>
    <script type="text/javascript" src="./env.js"></script>
  </head>
  <body>
    <p>ID: <span id="my-id"></span></p>
    <div>
      room name: <input id="room-name" type="text" />
      <button id="join">join</button>
    </div>
    <video id="local-video" width="400px" muted playsinline></video>
    <div id="button-area"></div>
    <div id="remote-media-area"></div>
    <script type="module" src="main.js"></script>
  </body>
</html>
```
```js:main.js
import {
  nowInSec,
  SkyWayAuthToken,
  SkyWayContext,
  SkyWayRoom,
  SkyWayStreamFactory,
  uuidV4,
} from "@skyway-sdk/room";

const token = new SkyWayAuthToken({
  jti: uuidV4(),
  iat: nowInSec(),
  exp: nowInSec() + 60 * 60 * 24,
  scope: {
    app: {
      id: APP_ID,
      turn: true,
      actions: ["read"],
      channels: [
        {
          id: "*",
          name: "*",
          actions: ["write"],
          members: [
            {
              id: "*",
              name: "*",
              actions: ["write"],
              publication: {
                actions: ["write"],
              },
              subscription: {
                actions: ["write"],
              },
            },
          ],
          sfuBots: [
            {
              actions: ["write"],
              forwardings: [
                {
                  actions: ["write"],
                },
              ],
            },
          ],
        },
      ],
    },
  },
}).encode(SECRET_ID);

(async () => {
  const localVideo = document.getElementById("local-video");
  const buttonArea = document.getElementById("button-area");
  const remoteMediaArea = document.getElementById("remote-media-area");
  const roomNameInput = document.getElementById("room-name");

  const myId = document.getElementById("my-id");
  const joinButton = document.getElementById("join");

  const { audio, video } =
    await SkyWayStreamFactory.createMicrophoneAudioAndCameraStream();
  video.attach(localVideo);
  await localVideo.play();

  joinButton.onclick = async () => {
    if (roomNameInput.value === "") return;

    const context = await SkyWayContext.Create(token);
    const room = await SkyWayRoom.FindOrCreate(context, {
      type: "p2p",
      name: roomNameInput.value,
    });
    const me = await room.join();

    myId.textContent = me.id;

    await me.publish(audio);
    await me.publish(video);

    const subscribeAndAttach = (publication) => {
      if (publication.publisher.id === me.id) return;

      const subscribeButton = document.createElement("button");
      subscribeButton.textContent = `${publication.publisher.id}: ${publication.contentType}`;
      buttonArea.appendChild(subscribeButton);

      subscribeButton.onclick = async () => {
        const { stream } = await me.subscribe(publication.id);

        let newMedia;
        switch (stream.track.kind) {
          case "video":
            newMedia = document.createElement("video");
            newMedia.playsInline = true;
            newMedia.autoplay = true;
            break;
          case "audio":
            newMedia = document.createElement("audio");
            newMedia.controls = true;
            newMedia.autoplay = true;
            break;
          default:
            return;
        }
        stream.attach(newMedia);
        remoteMediaArea.appendChild(newMedia);
      };
    };

    room.publications.forEach(subscribeAndAttach);
    room.onStreamPublished.add((e) => subscribeAndAttach(e.publication));
  };
})();
```
```haml
<script type="text/javascript" src="./env.js"></script>
```
という行は公式にはありませんでしたが、私がアプリケーションキーとシークレットキーを別で管理したかったので、それ用に別にjsファイルを作成しました。

```js:env.js
var APP_ID = "＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊"; //アプリケーションキー
var SECRET_ID = "＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊";　//シークレットキー
```
アプリケーションキーとシークレットキーは見せられないので、`＊＊＊＊`に置き換えていいます。これをSkyWay登録時にメモったものを入れれば大丈夫です。

ここまでできたら以下コマンドをこのファイルがおいてあるところで実行します。
```
npm run dev
```

以下のように実行できれば成功です。
```
> skyway@1.0.0 dev
> parcel ./index.html

Server running at http://localhost:1234
✨ Built in 8ms
```

`http://localhost:1234`にブラウザでアクセスすると、音声・ビデオ通話ができるアプリケーションが立ち上がっています。

公式のドキュメントにあるように、ブラウザで２つのタブを開きます。

そしてそれぞれ`http://localhost:1234`にアクセスをします。

2つのタブで同じ`ROOM_NAME`を入力すると開いているブラウザでビデオ通話と音声通話ができるようになります。


###データを受け渡したいとき

データを受け渡す`data_conection`を行いたいときは下記URLを参考に開発すると良いです。（公式のGITのリンクがまとめてあります。）

https://skyway.ntt.com/ja/docs/sample-code/javascript-sdk/




# さいごに

この記事での質問や、間違っている、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。

最後まで読んでいただきありがとうございました！

# 参考文献

https://skyway.ntt.com/ja/docs/user-guide/javascript-sdk/quickstart/