1-to-1 audio call
This page provides a code example for implementing a 1-to-1 audio call.
Prerequisites
Before you begin, you must do the following:
- Initialize PlanetKit.
- Get a proper access token.
- Check the overall API usage flow in 1-to-1 call flow.
Considerations for implementing a 1-to-1 audio call
To receive call notifications on the callee side, you must implement a notification system or integrate an external push notification system such as Apple Push Notification service (APNs) or Firebase Cloud Messaging (FCM).
You also need to know what information must be delivered to the callee. Refer to Role of the app server, which describes cc_param
, the data that must be delivered by your application.
After calling makeCall()
or verifyCall()
, you must check the reason
property of the returned PlanetKitMakeCallResult
or PlanetKitVerifyCallResult
.
- If the
reason
isPlanetKitStartFailReason.none
, it means success. - Otherwise, it means failure and you must take an appropriate action based on the
reason
.
Request device permissions
Use Dart permission_handler package to request phone and microphone permission.
import 'package:permission_handler/permission_handler.dart';
final status = await [Permission.microphone, Permission.phone, Permission.bluetoothConnect].request();
Prepare variables
Prepare variables for key properties and event handlers.
PlanetKitCallEventHandler
is used to handle status change events of a 1-to-1 call. For more information, see 1-to-1 call flow.PlanetKitMyMediaStatusHandler
is used to handle the local user's media status change events. You can update the local user's UI based on these events, such as when the mic becomes muted or unmuted, or when the audio description is updated.
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"));
Make an outgoing call (caller side)
To make a call, call PlanetKitManager.instance.makeCall()
with a PlanetKitMakeCallParam
that you can create using PlanetKitMakeCallParamBuilder()
.
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;
}
Respond to an incoming call (callee side)
When you receive a push message from the app server, you must parse the cc_param
from the message and create a ccParam
. This data is a required argument that must be passed when responding to a call.
To respond to a call, call PlanetKitManager.instance.verifyCall()
with a proper PlanetKitVerifyCallParam
. To build a PlanetKitVerifyCallParam
, use 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;
}
Accept an incoming call (callee side)
In general, the recipient needs time to decide whether to accept a new call after responding to the call. To accept a call, call PlanetKitCall.acceptCall()
.
The _call
variable is a PlanetKitCall
instance that has been verified by verifyCall()
.
Future<void> acceptCall() async {
return await _call?.acceptCall() ?? false;
}
End a call
To end a call, call endCall()
.
Future<bool> endCall() async {
return await _call?.endCall() ?? false;
}
Additional implementation for CallKit integration (Optional)
This step applies to iOS applications only.
When using the CallKit framework, you must set callKitType
to PlanetKitCallKitType.user
in PlanetKitMakeCallParam
and PlanetKitVerifyCallParam
.
Then, you must call PlanetKitCall.notifyCallKitAudioActivation()
on CXProviderDelegate.provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession)
to ensure audio activation in PlanetKit.