Clubhouseのような音声SNSアプリをWebRTCで作ってみる

今年に入ってClubhouseという音声SNSが大ブームになっています。
技術のポイントとして、スピーカ同士が遅延なく通話が可能で、その通話を大勢のオーディエンスがリスニングできる点になります。

方法としてはスピーカ間はWebRTC、オーディエンスはHLSに変換されたデータを取得するという構成が考えられます。iOS/Androidアプリであれば独自プロトコルでの実装という事も考えられます。

今回はagora.ioの技術を用いてWeb版Clubhouseのようなアプリを作ってみようと思います。
WebアプリなのでベースはWebRTCとなります。又、agora.ioの技術を利用すれば大勢のオーディエンスもWebRTCで接続可能です。

サンプルは[こちら](https://github.com/fu-jimoto/AgoraIO-WebRTC-VoiceLive)に公開してます。

##画面イメージ
![スクリーンショット 0003-02-03 20.43.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/b6d5894a-a3fd-8af0-d264-db9d020f08c6.png)
以下の機能が実装されています。
・ルームの作成
・他の人が作成されたルームの表示
・他の人のルームに入室
・退室

ルーム表示についてはサーバー側で管理する方法が一般的かもしれませんが、今回はAgoraRTMSDKを利用してリアルタイムメッセージで管理してみます。
AppIDはagora.ioにサインアップして取得する必要があります。

##実装の詳細
JoinボタンをクリックしたタイミングでAgoraRTMにログインします。
全員共通の"main"というチャンネルに接続し、新規でルームが発生した際、入室ボタンを表示する実装になっています。

```index.js
async function joinRtm() {
  rtmClient = AgoraRTM.createInstance(options.appid);
  rtmClient.login({ token: '', uid: options.accountName}).then(() => {
    console.log('AgoraRTM client login success');

    //rtm channel
    rtmChannel = rtmClient.createChannel(mainRtmChannel);
    rtmChannel.join().then(()=>{
        console.log("RTM Join Channel Success");

        rtmChannel.on('ChannelMessage', function (message, memberId) {
            console.log("Get channel message:"+memberId);
            console.dir(message);
            const information = JSON.parse(message.text);

            if(information.type == "new"){

              const room = $(
                <div id="player-wrapper-${memberId}" >
                  <button id="" type="submit" class="btn btn-primary btn-sm" onclick="join('${information.channelName}','','host')">Join Speaker(Host:${memberId})</button>
                  <button id="" type="submit" class="btn btn-primary btn-sm" onclick="join('${information.channelName}','','audience')">Join Audience(Host:${memberId})</button>
                  <button id="" type="submit" class="btn btn-primary btn-sm" onclick="leave()">Leave</button>
                </div>
                <br />
              );
              $("#remote-playerlist").append(room);
            }
        });
    });
  }, function (err) {
    console.log("AgoraRTC client init failed", err);
  });
}
```

次にRoomに入室処理する処理です。
・新規Room作成か既存Roomに入室か
・スピーカーかオーディエンスか
で処理を分岐しています。

新規Room作成の場合は、他のユーザにRoom作成の通知をRTMSDKで実施しています。
スピーカー入室の場合は、マイクを取得してサーバーへPublishする実装をします。

```index.js
async function join(channelName,type,role) {
  console.log("channelName:" + channelName);
  // add event listener to play remote tracks when remote user publishs.
  client.on("user-published", handleUserPublished);
  client.on("user-unpublished", handleUserUnpublished);
  client.setClientRole(role);
  const uid = await client.join(options.appid, channelName, null, null);

  if(role == "host"){
    localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
    // Publish the local audio track to the channel.
    await client.publish([localAudioTrack]);
  }

  if(type == "new"){
    //新規ルームの通知
    var info = JSON.stringify({uid: options.accountName, channelName: channelName, type: type});
    rtmChannel.sendMessage({text: info});
    console.log("send new channel info");
  }
}
```

最後に他拠点からの音声取得部分になります。
こちらはスピーカーもオーディエンスも同様の処理になります。

```index.js
async function subscribe(user, mediaType) {
  const uid = user.uid;
  // subscribe to a remote user
  await client.subscribe(user, mediaType);
  console.log("subscribe success");

  if (mediaType === 'audio') {
    user.audioTrack.play();
  }
}
```

agora.ioを利用すればClubhouseのようなリアルタイム音声SNSの構築が簡単にできます。
現時点ではミニマムなサンプルですが、段階的にClubhouseに近づけるサンプルを公開していきたいと思います。

#参考リンク
agora.ioではiOS/Androidアプリ用のSDKも公開されています。より高性能な音声技術をアプリに組み込む事も可能です。
・[ボイスチャット/カラオケアプリにおける音声配信(VoIP)技術について](https://qiita.com/v-cube/items/38e70004248fcd3c3dc4)
・[Raw Audio Data を使って自分の音声を可視化してみる](https://qiita.com/v-cube/items/c550154ea83427f7adde)


# 最後に
[**agora.ioに関するお問い合わせはこちらから**](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=65e4688dcd4e9e9ea7e9)
[<img width="912" alt="スクリーンショット 0001-08-15 13.41.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300377/5d815376-9b89-08fa-9434-6fa7e8e67bcf.png">](https://jp.vcube.com/lp/agora?utm_campaign=Agora&utm_source=Qiita&arc_id=65e4688dcd4e9e9ea7e9)