Twilio Voice JavaScript SDK v2を活用したブラウザベース通話機能の実装

Twilioでブラウザ電話を作るに際して、AIのコーディング支援を受けて実装すると、多くの場合に古いSDKでの実装が提示されていました。特にHTML＋JSで構築しようと思うと、ほとんどがv1のコーデイングです。最初は自分で修正をしていたのですが、ネットの世界に落ちてる Voice JavaScript SDKのサンプルコードはほとんどがv1での実装だったので、困っている人がいるのではないかと思い、HTML + JSの組み合わせでのブラウザ電話の実装を記載します。

Voice JavaScript SDK v2を使用すると、開発者はWebアプリケーションに音声通話機能を組み込むことができます。このブログでは、最新のTwilio Voice SDK v2 を使用したブラウザベースの電話アプリケーションの実装例を紹介します。

## Voice JavaScript SDK v2の主な特徴

- **Promiseベースのアーキテクチャ**: v1と異なり、SDK v2はPromiseとasync/awaitパターンを使用
- **強化されたエラー処理**: `enableImprovedSignalingErrorPrecision`オプションによる詳細なシグナリングエラー情報
- **エッジロケーションのサポート**: レイテンシを改善するための優先エッジロケーション指定
- **コーデック設定**: 優先オーディオコーデックの構成が可能
- **トークン管理**: 簡素化されたトークン更新メカニズム
- **包括的なイベントシステム**: 通話のライフサイクルを詳細に追跡できるイベント
- CDN経由でのSDKの提供が廃止: NPMまたは、Githubからjsファイルをダウンロードして利用


## 実装
### サンプルコード
[GitHubリポジトリ](https://github.com/MitsuharuNakamura/js-sdk2-browser-phone)
TwilioのFuntions&Assetsで動作する最小限のコードだけをまとめたブラウザ電話の実装です。今回はこちらのコードから抜粋してv2の特徴を紹介します。

## デバイスのセットアップ

Twilioデバイスの初期化は以下のように行います:

```javascript
device = new Twilio.Device(data.token, {
  allowIncomingWhileBusy: false, // 通話中の着信を許可するかどうか
  closeProtection: true, // タブを閉じる際の警告表示
  enableImprovedSignalingErrorPrecision: true, // 詳細なエラー情報の有効化
  edge: ['tokyo','singapore'], // 優先エッジロケーション
  logLevel: 1, // ログレベル
  codecPreferences: ['opus', 'pcmu'], // 優先コーデック
});
```

注目すべき設定オプション:
- `closeProtection`: タブを閉じる際の誤切断を防止
- `edge`: 接続処理のための地域設定
- `codecPreferences`: 高音質のための優先オーディオコーデック

## 発信処理の実装

Voice SDK v2では、connect メソッドで await を使用する必要があります:

```javascript
const call = await device.connect({ params: { To: number } });
```

callオブジェクトは通話の全ライフサイクルのイベントを提供します:
- `ringing`: 呼び出し開始時
- `accept`: 通話が応答された時
- `disconnect`: 通話終了時
- `error`: 問題発生時
``` javascript
call.on('ringing', hasEarlyMedia => {
	updateStatus('呼び出しを開始');
	if (!hasEarlyMedia) {
		//音声をカスタマイズする関数を追加する
	}
});

//発信用のTWIMLのDialパラメーターに、answerOnBridge: trueを設定することで
//相手が出た時にイベントが発生する
call.on('accept', call => { 
	updateStatus('通話開始');
});

call.on('cancel', () => {
	updateStatus('通話がキャンセルされました');
});

call.on('reject', () => {
	updateStatus('通話が拒否されました。');
});

call.on('disconnect', () => {
	updateStatus('通話が切断されました');
});

call.on('error', (error) => {
	console.log('An error has occurred: ', error);
	updateStatus('エラーが発生しました。');
});
```
## 着信処理の実装

着信を受けるには、まずデバイスを登録する必要があります:

```javascript
device.register();
```

そして着信イベントを処理します:

```javascript
device.on("incoming", (call) => {
  // 着信処理のロジック
  call.on("accept", () => {
    // 通話受諾時の処理
  });
  call.on("disconnect", () => {
    // 通話終了時の処理
  });
});
```

## トークン管理

トークン管理は接続維持のために重要です。SDK V2では`updateToken`メソッドでこれを簡素化しています:

```javascript
async function refreshToken() {
  const response = await fetch(token_url);
  const data = await response.json();
  
  if (device) {
    await device.updateToken(data.token);
  }
}
```

## バックエンド連携

アプリケーションをサポートする2つの主要なバックエンドコンポーネント:

1. **トークン生成** (`/token`エンドポイント):
   - VoiceGrantを含むJWTトークンを作成
   - 発信アプリケーションと着信許可を設定

2. **TwiMLレスポンス** (`/voice`エンドポイント):
   - 通話ルーティング用のTwiMLを生成
   - `answerOnBridge: true`で通話品質を向上
   - 発信者IDを設定

## 実装時の実用的な考慮事項

Voice SDK V2を本番環境で実装する際の注意点:

1. **エラー処理**: リトライロジックを含む包括的なエラー処理の実装
2. **トークンライフサイクル**: 有効期限前の自動トークン更新の設定
3. **UI連携**: 直感的な通話コントロールとステータス表示
4. **通話品質メトリクス**: `call.on('warning')`および`call.on('warning-cleared')`イベントのモニタリング
5. **デバイステスト**: 異なるブラウザやネットワーク条件でのテスト

## まとめ

Twilio Voice SDK v2は、Webアプリケーションに音声機能を組み込むための強力な基盤を提供します。Promiseベースのアーキテクチャ、包括的なイベントシステム、簡素化されたトークン管理により、信頼性の高い音声体験の作成がこれまで以上に容易になりました。

このデモアプリケーションのパターンに従うことで、独自のWebプロジェクトに音声通話機能を迅速に実装できます。


## リンクまとめ
[GitHubリポジトリ](https://github.com/MitsuharuNakamura/js-sdk2-browser-phone)
[Twilio Progarmmable Voice Javascript SDK v2 移行ガイド](https://www.twilio.com/docs/voice/sdks/javascript/migrating-to-js-voice-sdk-20)