本文にスキップする
Version: 1.0

1対1ビデオ通話

1対1ビデオ通話を実装するサンプルコードです。

前提条件

開始する前に、次の作業が必要です。

1対1ビデオ通話実装時の考慮事項

受信者側で通話の通知を受けるには、通知用システムを実装するか、APNs(Apple Push Notification service)またはFCM(Firebase Cloud Messaging)といった外部プッシュ通知システムを連携する必要があります。

また、受信者にどのような情報を渡すべきかを知っておく必要があります。アプリサーバーの役割にて、アプリケーションが渡すべきデータであるcc_paramについて確認できます。

makeCall()またはverifyCall()を呼び出した後は、返されたPlanetKitMakeCallResultまたはPlanetKitVerifyCallResultreasonのプロパティを確認する必要があります。

  • reasonPlanetKitStartFailReason.NONEの場合、成功を意味します。
  • それ以外は失敗を意味し、reasonによって適切な措置を取る必要があります。

デバイス権限のリクエスト

Dart permission_handlerパッケージを使用してマイク、電話、Bluetooth接続、カメラの権限をリクエストします。

import 'package:permission_handler/permission_handler.dart';
final status = await [Permission.microphone, Permission.phone, Permission.bluetoothConnect, Permission.camera].request();

変数の用意

主要プロパティとイベントハンドラーに対する変数を用意します。

  • PlanetKitCallEventHandlerは、1対1通話のステータス変更イベントを処理するのに使用されます。詳しくは、1対1通話フローを参照してください。
  • PlanetKitMyMediaStatusHandlerは、ローカルユーザーのメディアステータス変更イベントを処理するのに使用されます。マイクがミュートもしくはミュート解除された場合やオーディオの説明が更新された場合、ビデオのステータスが変更された場合など、イベントに基づいてローカルユーザーのUIを更新できます。
final String _myUserId = "test user id";
final String _serviceId = "test service id";
PlanetKitCall? _call;

bool isMyAudioMuted = false;
bool isMyVideoPaused = false;
bool isPeerMicMuted = false;
bool isPeerVideoPaused = false;
bool isVideoEnabled = false;

int volume = 0;

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"),
onPeerMicMuted: (call) => isPeerMicMute = true,
onPeerMicUnmuted: (call) => isPeerMicMute = false,
onPeerVideoPaused: (call, reason) => isPeerVideoPaused = true,
onPeerVideoResumed: (call) => isPeerVideoPaused = false,
onVideoEnabledByPeer: (call) {
isVideoEnabled = true;
isPeerVideoPaused = false;
},
onVideoDisabledByPeer: (call, reason) => isVideoEnabled = false);

final _myMediaStatusHandler = PlanetKitMyMediaStatusHandler(
onAudioDescriptionUpdate: (status, averageVolumeLevel) => volume = averageVolumeLevel,
onMicMute: (status) => isMyAudioMuted = true,
onMicUnmute: (status) => isMyAudioMuted = false,
onVideoStatusUpdate: (status, videoStatus) {
if (videoStatus.state == PlanetKitVideoState.enabled) {
isMyVideoPaused = false;
isVideoEnabled = true;
} else if (videoStatus.state == PlanetKitVideoState.disabled) {
isVideoEnabled = false;
} else if (videoStatus.state == PlanetKitVideoState.paused) {
isMyVideoPaused = true;
isVideoEnabled = true;
}
});

通話の作成(発信側)

通話を作成するには、適切なPlanetKitMakeCallParamと共にPlanetKitManager.instance.makeCall()を呼び出します。

PlanetKitMakeCallParamを作成するには、PlanetKitMakeCallParamBuilder()を使用してください。このとき、ビデオ通話が作成されるようにPlanetKitMakeCallParamBuildersetMediaType(PlanetKitMediaType.audiovideo)を呼び出します。

Future<bool> makeCall(String peerId, String accessToken) async {
final builder = PlanetKitMakeCallParamBuilder()
.setMyUserId(_myUserId)
.setMyServiceId(_serviceId)
.setPeerUserId(peerId)
.setPeerServiceId(_serviceId)
.setAccessToken(accessToken)
.setMediaType(PlanetKitMediaType.audiovideo);

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;
}

if (ccParam.mediaType == PlanetKitMediaType.audiovideo ||
ccParam.mediaType == PlanetKitMediaType.video) {
isVideoEnabled = true;
}

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;
}

通話の応答(受信側)

一般的に通話を受信した後、通話を受けるかどうかを決める時間が必要です。通話に応答するには、PlanetKitCall.acceptCall()を呼び出します。

Tip

_call変数は、verifyCall()で検証済みのPlanetKitインスタンスです。

Future<void> acceptCall() async {
return await _call?.acceptCall() ?? false;
}

ビデオのレンダリング

ローカルビデオ(ローカルユーザーのビデオ)とリモートビデオ(ピアのビデオ)をレンダリングするには、PlanetKitVideoViewBuilderを使用してPlanetKitVideoViewを作成し、PlanetKitCallに追加する必要があります。

次のコードでは、ビデオが設定された状態でPlanetKitCallインスタンスが作成されていると仮定します。

class CallVideoView extends StatelessWidget {
const CallVideoView({super.key, this.call});
final PlanetKitCall? call;


Widget build(BuildContext context) {
final myVideoView = PlanetKitVideoViewBuilder.instance.create();
final peerVideoView = PlanetKitVideoViewBuilder.instance.create();

myVideoView.onCreate.listen((id) {
call?.addMyVideoView(id);
});

myVideoView.onDispose.listen((id) {
call?.removeMyVideoView(id);
});

peerVideoView.onCreate.listen((id) {
call?.addPeerVideoView(id);
});

peerVideoView.onDispose.listen((id) {
call?.removePeerVideoView(id);
});


return Row(
children: [
myVideoView,
peerVideoView
]
);
}
}

PlanetKitCall.addMyVideoView(viewId)はローカルユーザーのビューに該当するPlanetKitVideoViewが作成されたときに、PlanetKitCall.removeMyVideoView(viewId)はこのビューが解除(dispose)されたときに、呼び出される必要があります。

PlanetKitCall.addPeerVideoView(viewId)はピアのビューに該当するPlanetKitVideoViewが作成されたときに、PlanetKitCall.removePeerVideoView(viewId)はこのビューが解除されたときに、呼び出される必要があります。

通話の切断

通話を切断するには、endCall()を呼び出します。

Future<bool> endCall() async {
return await _call?.endCall() ?? false;
}

CallKit連携のための追加実装(任意)

Note

この手順は、iOSアプリケーションにのみ適用されます。

CallKitフレームワークを使用する場合、PlanetKitMakeCallParamおよびPlanetKitVerifyCallParamcallKitTypePlanetKitCallKitType.userに設定する必要があります。

その後、CXProviderDelegate.provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession)PlanetKitCall.notifyCallKitAudioActivation()を呼び出してPlanetKitでオーディオを有効にする必要があります。

関連サンプルコード

関連ドキュメント