TypescriptによるTencent Real-Time Communication SDKの使い方

# はじめに
これまで、Amazon Chime SDK, Twillio, Zoom SDK, Janus Gatewayなどを用いてビデオ会議アプリを開発してきました。より幅を広げようと思い、今回初めてTRTC(Tencent Real-Time Communication)を勉強しています。

本稿ではTypescript+ReactでTRTCのビデオ会議アプリを作りはじめるまで(Hello World!的なところまで)に行ったことと、感想をご紹介してきたいと思います。

# 前提 その１
TRTCを使用したウェブアプリを作成するには、TencentCloudのアカウントを作成する必要があります。[こちら](https://qiita.com/advent-calendar/2022/tencentcloud03)にリンクが張られている「アカウント解説手順」に従い、アカウントを作成しておいてください。
フリートライアルでテストで使用する場合は、クレジットカードの登録や免許証の登録はせずに使えるようです。

# 前提 その２
TRTCのアプリケーションIDとシークレットを生成しておいてください。
下記は生成までの流れですが、この辺のインターフェイスはちょくちょく変わるようなので、現時点(2022/12/23)のものということでご了承ください。

製品のところから、"trtc"と入力すると候補でTencent Real-Time Communicationが表示されるのでこれを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/a22d0969-cdf9-5386-9a6e-564a62f948d6.png)

無料で始めるをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/d3a8d1f7-d93a-aa35-11d3-f98e2de3b6d1.png)

メニューからApplication Managementを選択するとCreate applicationボタンが表示されるのでそれをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/6102f96b-2ad6-aea0-5c57-07ab82d731f2.png)

下記のようなワークフローが表示されるので情報を入力していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/811b2fcf-6c51-c489-2eef-59c1332fcc2e.png)

新しくアプリを作る場合は、Application TypeはNewでよさそうです。Application Nameは適当なものを入力します。

Download Source Codeでサンプルコードを入手できるようですが、今回はTypescriptで一から作成するつもりなので、そのままNextを押してしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/0de4f52a-e461-13be-8e14-86a56a6bbcb4.png)

すると、下記のようなコンフィギュレーションが表示されます。赤枠のところにこのアプリケーションのSDKAPPIDとSecretKeyが表示されます。これを記憶しておいてください。（管理には十分お気を付けください。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/b20a0838-0725-b079-d61d-ef4a3cb7681c.png)

ステップ４まで行けば完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/6cf4aaa4-fc00-119a-d5a6-9e9748130cd1.png)

# 公式のデモリポジトリ
[公式のデモリポジトリ](https://github.com/LiteAVSDK/TRTC_Web)のフォルダ構成は次のようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/8052278e-95da-6b5b-3087-0c43623914bd.png)

base-js, base-react-next, quick-demo-js, quick-demo-vue2-js, quick-demo-vew3-tsがサンプルになります。reactのサンプルも既にあります。しかし、ほかのサンプルも同じなのですが、たくさんのTRTCのSDKの機能を実装しているため、これらをそのまま流用していくのは難しそうです。各機能を使用する際に、リファレンスとしてみるのが良いのかなぁ、という感じです。

ということで、今回はこれらのサンプルには触れずに、会議室に入ってビデオ会議を始めるまでのベースライン(Hello World)を、一から組んでいこうと思います。

# ドキュメント
公式ドキュメントは[こちら](https://web.sdk.qcloud.com/trtc/webrtc/doc/en/TRTC.html)にあります。一部中国語の部分もありますが、大半は英語になっているので問題なさそうです。というか、かなり丁寧な説明でわかりやすいです。

# 準備 その１
SDKをnpmでインストールします。yarnでも行けると思います。
```
$ npm install trtc-js-sdk --save
```

# 準備 その２
実装を進めていくとわかるのですが、というかこれまでの説明でSDKKeyやらSecret Keyの話が出ているので予想はついていると思いますが、TRTCを使用するためにはアプリケーションの認証を行う必要があります([参考](https://www.tencentcloud.com/jp/document/product/647/35166))。認証のためのコード生成方法はいくつかあるようですが、テスト用にはリポジトリで[公開されているもの](https://github.com/LiteAVSDK/TRTC_Web/blob/main/base-js/js/debug/GenerateTestUserSig.js)を使用してよいようですので、今回はこれを使用させていただくこととします。

GenerateTestUserSig.jsから参照されている[このライブラリ](https://github.com/LiteAVSDK/TRTC_Web/blob/main/base-js/js/lib-generate-test-usersig.min.js)をダウンロードしておきます。

# 準備 その３
reactを開発できる環境を整えます。この辺は開発者によって流儀が異なると思いますので、説明は省略します。私の環境を参考にされたい方は、後述するリポジトリをcloneして`npm install`してください。

# 実装
それでは、実装を始めていきます。

## 認証用のライブラリの読み込み
上述した認証用のライブラリを使えるように、アプリのindex.htmlのスクリプトタグで読み込んでおきます。jsフォルダの下に配置している想定です。

```html
<!DOCTYPE html>
<html lang="ja" style="width: 100%; height: 100%; overflow: hidden">
    <head>
        <meta charset="utf-8" />
        <title>TRTC Simple Demo TS</title>
        <script src="./js/lib-generate-test-usersig.min.js"></script>　<!-- ★ ここ -->
    </head>
    <body style="width: 100%; height: 100%; margin: 0px">
        <div id="app" style="width: 100%; height: 100%"></div>
    </body>
</html>

```
## 認証用のライブラリの型定義（オプション）
認証用ライブラリには型定義がないので定義しておきます。TRTCの本質ではないので、省略可能です。

```ts
export interface LibGenerateTestUserSig {
    constructor(SDKAPPID: string, SECRETKEY: string, EXPIRETIME: number)
    genTestUserSig(userId: string)
}

declare global {
    interface Window {
        LibGenerateTestUserSig: new (SDKAPPID: number, SECRETKEY: string, EXPIRETIME: number) => LibGenerateTestUserSig
    }
}
```

## 本体
ビデオ会議のHello Worldとしては、こんな感じかなと思います。

HTML上のボタンにより、joinメソッドとleaveメソッドが呼び出されます。

joinメソッド(★１)は、HTML上のusernameとroom idを取得して、認証用のコードを作成します。これらの情報を使用して会議室に入ります。
認証用のコード生成には、SDKAPPKEY(コード内ではSDKAPPIDと読んでる)とSECRETKEYとEXPIRETIMEが必要となります。const.tsで別途定義していますので、上記の手順で入手した情報を使ってください。EXPIRETIMEは適切な値を設定してください。

leaveメソッド(★2)は会議室から退室して、クライアントを消去します。


```ts
import React, {  useRef } from "react";
import { createRoot } from "react-dom/client";
import TRTC, { Client } from "trtc-js-sdk"
import { EXPIRETIME, SDKAPPID, SECRETKEY } from "./const";
import "./index.css"

const App = () => {
    const clientRef = useRef<Client>()
    // ★１　joinメソッド
    const join = async () => {
        const usernameInput = document.getElementById("username") as HTMLInputElement
        const username = usernameInput.value
        const roomIdInput = document.getElementById("room-id") as HTMLInputElement
        const roomId = Number(roomIdInput.value)

        const signer = new window.LibGenerateTestUserSig(SDKAPPID, SECRETKEY, EXPIRETIME)
        const sign = signer.genTestUserSig(username)

        clientRef.current = TRTC.createClient({
            sdkAppId: SDKAPPID,
            userId: username,
            userSig: sign,
            mode: 'rtc'
        });

        clientRef.current.on('stream-added', event => {
            if (!clientRef.current) {
                alert("client is not initialized.")
                return
            }
            const remoteStream = event.stream;
            const container = document.getElementById("remote-video-container") as HTMLDivElement
            const divForRemote = document.createElement("div")
            divForRemote.id = `remote-video-${remoteStream.getId()}`
            container.appendChild(divForRemote)
            clientRef.current.subscribe(remoteStream);
        });
        clientRef.current.on('stream-subscribed', event => {
            const remoteStream = event.stream;
            console.log('Subscribed to remote stream successfully:' + remoteStream.getId());
            remoteStream.play(`remote-video-${remoteStream.getId()}`);
        });

        await clientRef.current.join({ roomId: roomId });
        const localStream = TRTC.createStream({ userId: username, audio: true, video: true });
        await localStream.initialize()
        localStream.play('local-video-container');

        await clientRef.current.publish(localStream);
    }

    // ★2　leaveメソッド
    const leave = async () => {
        if (!clientRef.current) {
            alert("client is not initialized.")
            return
        }
        await clientRef.current.leave();
        clientRef.current.destroy();
    }

    // HTMLコンポーネント部分
    return (
        <div className="root-div">
            <div className="header">
                <div className="header-item-container">
                    <div className="header-label">username</div>
                    <input type="text" id="username"></input>
                </div>
                <div className="header-item-container">
                    <div className="header-label">room id</div>
                    <input type="number" id="room-id"></input>
                </div>
                <div className="header-item-container">
                    <div className="header-button" onClick={join}>enter</div>
                </div>
                <div className="header-item-container">
                    <div className="header-button" onClick={leave}>leave</div>
                </div>
            </div>
            <div className="body">
                <div id="remote-video-container"></div>
                <div id="local-video-container"></div>
            </div>
        </div>
    )
}

const container = document.getElementById("app")!;
const root = createRoot(container);
root.render(
    <App></App>
);
```

以上が、Hello Worldの実装になります。ここまででざっと100行強程度です(cssは別)。

# デモ
このアプリはこんな感じで動きます。ちゃんと動いていますね。

https://user-images.githubusercontent.com/48346627/209261843-7185d504-df8e-4fde-9298-2128eac67f19.mp4

# リポジトリ
リポジトリは次のところです。

https://github.com/w-okada/trtc-simple-demo-ts


# 使い方
リポジトリをクローンする。
```
$ git clone https://github.com/w-okada/trtc-simple-demo-ts.git  -b hello-world
```

`src/const.ts`に情報を設定する。

そして、次のコマンドを実行してください。

```
$ npm install
$ npm run start
```
表示されたアドレスにブラウザでアクセスしてください。


# 感想
以上、Typescript+Reactを用いた、TRTCビデオ会議のHello Worldを作ってみました。

Hello Worldを作るまでのところであれば、他のビデオ会議のSDKと比べても、同等レベルかそれ以上に素直なつくりではないかと思います。めちゃくちゃ簡単に作れました。なんとなく、Janus Gatewayに近い感触かな、と思いました。

マニュアルがとてもわかりやすいので、公式のサンプルリポジトリを解析するより、一から作った方が楽だと思います。

TencentCloudを本格的に使うようになると、個人認証で免許証やパスポート情報を入力する必要があるので、そちらの方が心理的にネックな人も多いような気がします。

# 追記

コンソールでは使用状況・履歴が簡単に見えて楽しい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365991/3e6990c6-8a28-199f-288e-e0f0fc8e70d6.png)