1対1音声通話
1対1音声通話を実装するサンプルコードです。
前提条件
開始する前に、次の作業が必要です。
- PlanetKitを初期化してください。
- 適切なアクセストークンを取得してください。
- 1対1通話フローにて、APIを使用するための全般的なプロセスを確認してください。
1対1音声通話実装時の考慮事項
受信者側で通話の通知を受けるには、通知用システムを実装するか、APNs(Apple Push Notification service)またはFCM(Firebase Cloud Messaging)といった外部プッシュ通知システムを連携する必要があります。
また、受信者にどのような情報を渡すべきかを知っておく必要があります。アプリサーバーの役割にて、アプリケーションが渡すべきデータであるcc_paramについて確認できます。
makeCall()またはverifyCall()を呼び出した後は、返されたPlanetKitMakeCallResultまたはPlanetKitVerifyCallResultのreasonのプロパティを確認する必要があります。
reasonがPlanetKitStartFailReason.NONEの場合、成功を意味します。- それ以外は失敗を意味し、
reasonに応じて適切に処理する必要があります。
デバイス権限のリクエスト
Dart permission_handlerパッケージを使って、電話およびマイクの権限をリクエストします。
import 'package:permission_handler/permission_handler.dart';
final status = await [Permission.microphone, Permission.phone, Permission.bluetoothConnect].request();
変数準備
主要プロパティとイベントハンドラーに対する変数を用意します。
PlanetKitCallEventHandlerは、1対1通話のステータス変更イベントを処理するのに使用されます。詳しくは、1対1通話フローを参照してください。PlanetKitMyMediaStatusHandlerは、ローカルユーザーのメディアステータス変更イベントを処理するために使用されます。マイクがミュートもしくはミュート解除された場合、またはオーディオの説明が更新された場合など、イベントに基づいてローカルユーザーのUIを更新できます。
final String _myUserId = "test user id";
final String _serviceId = "test service id";
PlanetKitCall? _call;
final _eventHandler = PlanetKitCallEventHandler(
onWaitConnected: (call) => print("wait connected"),
onVerified: (call, peerUseResponderPreparation) => print("verified"),
onConnected: (call, isInResponderPreparation, shouldFinishPreparation) => print("connected"),
onDisconnected: (call, reason, source, byRemote) => print("disconnected $reason"));
final _myMediaStatusHandler = PlanetKitMyMediaStatusHandler(
onMicMute: (status) => print("mic muted"),
onMicUnmute: (status) => print("mic unmuted"),
onAudioDescriptionUpdate: (status, averageVolumeLevel) => print("audio description updated"));
通話作成(発信側)
通話を作成するには、PlanetKitMakeCallParamBuilder()を使ってPlanetKitMakeCallParamを作成し、これを引数としてPlanetKitManager.instance.makeCall()を呼び出します。
Future<bool> makeCall(String peerId, String accessToken) async {
final builder = PlanetKitMakeCallParamBuilder()
.setMyUserId(_myUserId)
.setMyServiceId(_serviceId)
.setPeerUserId(peerId)
.setPeerServiceId(_serviceId)
.setAccessToken(accessToken);
PlanetKitMakeCallParam? param;
try {
param = builder.build();
} catch (error) {
print("failed to build make call param $error");
return false;
}
final result = await PlanetKitManager.instance.makeCall(param, _eventHandler);
if (result.reason != PlanetKitStartFailReason.none) {
print("make call failed. reason: $result.reason");
return false;
}
// Store call instance and set MyMediaStatusHandler.
_call = result.call;
_call?.myMediaStatus.setHandler(_myMediaStatusHandler);
return true;
}
通話の受信(受信側)
アプリサーバーからプッシュメッセージを受け取ったら、メッセージからcc_paramをパーシングしてccParamを作成します。このデータは、通話を受信する際に渡す必須引数です。
通話を受信するには、適切なPlanetKitVerifyCallParamと共にPlanetKitManager.instance.verifyCall()を呼び出します。PlanetKitVerifyCallParamを作成するには、PlanetKitVerifyCallParamBuilder()を使用してください。
Future<bool> verifyCall(String ccParamString) async {
final ccParam = await PlanetKitCcParam.createCcParam(ccParamString);
if (ccParam == null) {
print("failed to create ccparam string $ccParamString");
return false;
}
final builder = PlanetKitVerifyCallParamBuilder()
.setMyUserId(_myUserId)
.setMyServiceId(_serviceId)
.setCcParam(ccParam);
PlanetKitVerifyCallParam? param;
try {
param = builder.build();
} catch (error) {
print("failed to build verify call param $error");
return false;
}
final result =
await PlanetKitManager.instance.verifyCall(param, _eventHandler);
if (result.reason != PlanetKitStartFailReason.none) {
print("make call result $result.reason");
return false;
}
// Store call instance and set MyMediaStatusHandler.
_call = result.call;
_call?.myMediaStatus.setHandler(_myMediaStatusHandler);
return true;
}
バックグラウンド・アイソレートでプッシュ通知を処理する
バックグラウンド・アイソレート(例:AndroidのFCMバックグラウンドメッセージ)でプッシュ通知を処理する場合、各Flutterアイソレートは独自のヒープとイベントループを持つため、PlanetKitBackgroundCallインスタンスを直接共有できない点に注意してください。代わりに、バックグラウンド・アイソレートでPlanetKitManager.verifyBackgroundCall()を使ってPlanetKitBackgroundCallを作成し、作成されたbackgroundCallIdをSendPort/ReceivePortペアのようなアイソレート間チャネルを通じてメイン・アイソレートに引き渡します。メイン・アイソレートでは、backgroundCallIdを引数としてPlanetKitManager.adoptBackgroundCall()を呼び出して通話を管理します。
Androidでは、プロセスを維持し、長時間実行される通話ロジックを処理するために、ネイティブフォアグラウンドサービスを使ってこのフローをサポートできます。フォアグラウンドサービスはプラットフォームチャネルを通じてFlutterと通信でき、Dart側ではアイソレート間メッセージングを使用してbackgroundCallIdのような軽量データをバックグラウンド・アイソレートとメイン・アイソレートの間で送信できます。
// Background isolate
Future<void> verifyBackgroundCall(PlanetKitVerifyCallParam param, PlanetKitBackgroundCallEventHandler eventHandler) async {
final result = await PlanetKitManager.instance.verifyBackgroundCall(param, eventHandler);
if (result.reason != PlanetKitStartFailReason.none) {
print("Verify background call failed. ${result.reason}");
return;
}
YourForegroundServicePlugin.startService(
result.call!.backgroundCallId,
notificationTitle: 'Call in Progress');
}
// Adopt background call on main isolate
Future<void> adoptBackgroundCall(PlanetKitCallEventHandler eventHandler) async {
final backgroundCallId = YourForegroundServicePlugin.getCurrentServiceId();
final call = await PlanetKitManager.instance.adoptBackgroundCall(backgroundCallId, eventHandler);
if (call == null) {
print("adopt background call failed.");
return;
}
}
通話の応答(受信側)
一般的に通話を受信した後、通話を受信するかどうかを決める時間が必要です。通話に応答するには、PlanetKitCall.acceptCall()を呼び出します。
_call変数は、verifyCall()で検証されたPlanetKitCallのインスタンスです。
Future<void> acceptCall() async {
return await _call?.acceptCall() ?? false;
}
通話の切断
通話を切断するには、endCall()を呼び出します。
Future<bool> endCall() async {
return await _call?.endCall() ?? false;
}
CallKit連携のための追加実装(任意)
この手順は、iOSアプリケーションにのみ適用されます。
CallKitフレームワークを使用する場合、PlanetKitMakeCallParamおよびPlanetKitVerifyCallParamでcallKitTypeをPlanetKitCallKitType.userに設定する必要があります。
その後、CXProviderDelegate.provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession)でPlanetKitCall.notifyCallKitAudioActivation()を呼び出し、PlanetKitでオーディオを有効にする必要があります。