1-to-1 video call
This page provides a code example for making a 1-to-1 video 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 video call
To receive call notifications on the callee side, you must implement a notification system or integrate an external push notification system such as 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 PlanetKitCallResult
.
- If the
reason
isPlanetKitStartFailReason.NONE
, it means success. - Otherwise, it means failure and you must take an appropriate action based on the
reason
.
Request permissions
An application must acquire the following runtime permissions before making calls.
Manifest.permission.READ_PHONE_STATE
Manifest.permission.RECORD_AUDIO
Manifest.permission.BLUETOOTH_CONNECT
(FortargetSdkVersion
31 or higher only)Manifest.permission.CAMERA
Prepare variables
Prepare variables for key properties and an event listener.
PlanetKitMyMediaStatusListener
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, when the audio description is updated, or when the video status is updated.
val myUserId = "test user id" // The local user's user ID
val serviceId = "test service id" // Service ID
// PlanetKitMyMediaStatusListener object
val myMediaStatusListener = object : PlanetKitMyMediaStatusListener {
override fun onMyAudioMuted() {
// This is called when the local user's audio is muted.
// Write your own code here.
}
override fun onMyAudioUnmuted() {
// This is called when the local user's audio is unmuted.
// Write your own code here.
}
override fun onMyAudioDescriptionUpdated(audioDescription: PlanetKitAudioDescription) {
// This is called when the local user's audio description is updated.
// Write your own code here.
}
override fun onVideoStatusUpdated(videoStatus: PlanetKitVideoStatus) {
// This is called when the local user's video status is updated.
// Write your own code here.
}
}
Preview the local user's video
The preview feature allows the local user to preview their own video regardless of the 1-to-1 call session.
To starting previewing the local user's video, call startPreview()
of PlanetKitCameraManager
.
fun startPreviewExample(preview: PlanetKitVideoView)
{
PlanetKit.getCameraManager().startPreview(preview)
}
To stop previewing the local user's video, call stopPreview()
of PlanetKitCameraManager
.
fun stopPreviewExample(preview: PlanetKitVideoView)
{
PlanetKit.getCameraManager().stopPreview(preview)
}
Make an outgoing call (caller side)
To make a call, call PlanetKit.makeCall()
with a proper PlanetKitMakeCallParam
. To build a PlanetKitMakeCallParam
, use PlanetKitMakeCallParam.Builder
.
- To make a video call, call
mediaType()
ofPlanetKitMakeCallParam.Builder
withPlanetKitMediaType.AUDIOVIDEO
. - The caller's initial video state can be set with
setInitialMyVideoState()
ofPlanetKitMakeCallParam.Builder
.- The default value of the
initialMyVideoState
property isPlanetKitInitialMyVideoState.RESUME
. - If the caller's initial video state is set to paused by calling
setInitialMyVideoState()
withPlanetKitInitialMyVideoState.PAUSE
, the caller's video is not transmitted. The caller's video can be transmitted by callingresumeMyVideo()
after the call is connected.
- The default value of the
fun makeCallExample(peerId: String, accessToken: String, initialMyVideoState: PlanetKitInitialMyVideoState)
{
val makeCallParam = PlanetKitMakeCallParam.Builder()
.myId(myUserId)
.myServiceId(serviceId)
.peerId(peerId)
.peerServiceId(serviceId)
.accessToken(accessToken)
.mediaType(PlanetKitMediaType.AUDIOVIDEO)
.setInitialMyVideoState(initialMyVideoState)
.build()
var result = PlanetKit.makeCall(makeCallParam, makeCallListener = object : MakeCallListener
{
override fun onWaitConnected(call: PlanetKitCall)
{
// This is called after making a call.
// Write your own code here.
}
override fun onConnected(call: PlanetKitCall, param: PlanetKitCallConnectedParam)
{
// This is called after the call is connected.
// Write your own code here.
}
override fun onDisconnected(call: PlanetKitCall, param: PlanetKitDisconnectedParam)
{
// This is called after the call is disconnected.
// Write your own code here.
}
})
if (result.reason == PlanetKitStartFailReason.NONE)
{
// The "result.call" instance is the main instance to call APIs from now on.
// You must keep it to control this call.
// See call.instanceId and PlanetKit.getCall(instanceId).
}
else
{
// result.reason: PlanetKitStartFailReason describes why makeCall failed.
}
}
Respond to an incoming call (callee side)
Android apps typically use Firebase Cloud Messaging (FCM) for the push notification. Android provides a base class called FirebaseMessagingService
for receiving messages from FCM.
When you receive a push message, 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, build PlanetKitVerifyCallParam
and then call PlanetKit.verifyCall()
.
class YourFirebaseMessagingService : FirebaseMessagingService()
{
override fun onMessageReceived(message: RemoteMessage)
{
val cCParamStr = message.data["cc_param"]
if (cCParamStr.isNullOrBlank())
{
Log.e("Push", "Empty message from application server")
return
}
val cCParam = PlanetKitCCParam.create(cCParamStr)
if (cCParam == null) {
Log.e(TAG, "Can not create PlanetKitCCParam. Check cCParamStr=$cCParamStr")
return
}
Log.d("Push","ccParam{peerId=${cCParam.peerId}, peerServiceId=${cCParam.peerServiceId}, mediaType=${cCParam.mediaType}")
val verifyCallParam = PlanetKitVerifyCallParam.Builder()
.myId(myUserId)
.serviceId(serviceId)
.cCParam(cCParam)
.build()
val result = PlanetKit.verifyCall(verifyCallParam, verifyListener = object : VerifyListener
{
override fun onVerified(call: PlanetKitCall, peerStartMessage: PlanetKitCallStartMessage?, peerUseResponderPreparation: Boolean)
{
// This is called after verifying a call.
// Write your own code here.
// For example, you can show a notification and switch to your ringing screen.
//
// The "call" is the verified call instance.
// If you send an instance of call through 'Intent of Android',
// then you can use the "call.instanceId".
// You can get the instance of call with the API "PlanetKit.getCall(instanceId)".
}
override fun onDisconnected(call: PlanetKitCall, param: PlanetKitDisconnectedParam)
{
// This is called after the call is disconnected.
// Write your own code here.
}
})
// Store result.call or result.call.instanceId for acceptCall().
if (result.reason == PlanetKitStartFailReason.NONE)
{
// The "result.call" instance is the main instance to call APIs from now on.
// You must keep it to control this call.
// See call.instanceId and PlanetKit.getCall(instanceId).
}
else
{
// result.reason: PlanetKitStartFailReason describes why verifyCall failed.
}
}
}
Accept an incoming call (callee side)
In general, the callee needs time to decide whether to accept a new call after responding to the call. To accept a call, call acceptCall()
.
- The callee's initial video state can be set with the
initialMyVideoState
parameter ofacceptCall()
.- The default value of the
initialMyVideoState
parameter isPlanetKitInitialMyVideoState.RESUME
. - If the callee's initial video state is set to paused by passing
PlanetKitInitialMyVideoState.PAUSE
, the callee's video is not transmitted. The callee's video can be transmitted by callingresumeMyVideo()
after the call is connected.
- The default value of the
The call
variable is the PlanetKitCall
instance that has been verified by verifyCall()
.
fun acceptCallExample(instanceId: Int, initialMyVideoState: PlanetKitInitialMyVideoState): Boolean
{
var call = PlanetKit.getCall(instanceId) ?: return false
call.acceptCall(listener = object : AcceptCallListener {
override fun onConnected(call: PlanetKitCall, param: PlanetKitCallConnectedParam) {
// This is called after the call is connected.
// Write your own code here.
}
override fun onDisconnected(call: PlanetKitCall, param: PlanetKitDisconnectedParam) {
// This is called after the call is disconnected.
// Write your own code here.
}
}, initialMyVideoState
)
return true
}
Set the media status listener for the local user (Optional)
Once the call is connected, add the media status listener for the local user depending on your needs.
fun setMyMediaStatusListenerExample(instanceId: Int): Boolean
{
var call = PlanetKit.getCall(instanceId) ?: return false
call.getMyMediaStatus()?.addHandler(myMediaStatusListener, null) {
}
return true
}
Add a video view for the local user
Call addMyVideoView()
to add a video view where the local user's video will be rendered. The rendering of the video starts or stops according to the local user's video status.
fun addMyVideoViewExample(instanceId: Int, myVideoView: PlanetKitVideoView): Boolean
{
var call = PlanetKit.getCall(instanceId) ?: return false
call.addMyVideoView(myVideoView)
return true
}
Add a video view for the peer
Call addPeerVideoView()
to add a video view where the peer's video will be rendered. The rendering of the video starts or stops according to the peer's video status.
fun addPeerVideoViewExample(instanceId: Int, peerVideoView: PlanetKitVideoView): Boolean
{
var call = PlanetKit.getCall(instanceId) ?: return false
call.addPeerVideoView(peerVideoView)
return true
}
End a call
To end a call, call endCall()
.
fun endCallExample(instanceId: Int): Boolean
{
var call = PlanetKit.getCall(instanceId) ?: return false
call.endCall()
return true
}