1-to-1 call
This page explains the main 1-to-1 calling features, including how to place, answer, manage, and end a call in your app.
Key classes
NCCallEngine
NCCallEngine is the core class in Call SDK for Web. It manages call behavior on the client, including placing calls, answering, hanging up, controlling audio and video devices, and managing call logs.
| Method | Description |
|---|---|
| setCallEventsListener | Register app-level events such as incoming calls, call end, participant state changes, and media type changes |
| setConnectionQualityListener | Register a listener for call quality, including uplink and downlink quality and volume. See the API documentation for details. |
| setVideoView | Bind video players for the users you want to watch, including the local user and remote users |
| removeVideoView | Remove the video player for a specific user when you no longer want to watch that user's video |
| playMedia | Play audio or video for a specific user. Before you play video for a user, call setVideoView first. |
| createVideoConfigBuilder | Create a video configuration builder |
| setVideoConfig | Apply the video configuration |
| createAudioConfigBuilder | Create an audio configuration builder |
| createAndroidPushConfigBuilder | Create an Android push configuration builder |
| setAudioConfig | Apply the audio configuration |
| startCall | Start a call. calleeIds can be a single userId string or a userId array. |
| joinCall | Join an ongoing group call directly |
| inviteToCall | Invite other users during a call |
| acceptCall | Answer a call |
| endCall | Hang up a call |
| requestChangeMediaType | Request a media type change during a call |
| cancelChangeMediaType | Cancel a pending media type change request |
| replyChangeMediaType | Reply to a media type change request |
| getCurrentCallSession | Get the current call |
| getOngoingCallLogs | Get unfinished call logs from the server |
| getHistoryCallLogs | Query the current user's call history with pagination |
| deleteCallLogsForMe | Delete call logs in batches by callId |
| deleteAllCallLogsForMe | Delete all call logs |
| enableCamera | Turn the camera on or off by passing true or false |
| switchCamera | Switch the camera |
| enableMicrophone | Turn the microphone on or off by passing true or false |
| getAudioInputDevices | Get the list of audio input devices, such as microphones |
| selectAudioInputDevice | Select a microphone input device |
| getAudioOutputDevices | Get the list of audio output devices, such as speakers |
| selectAudioOutputDevice | Select an audio output device |
| getVideoInputDevices | Get the camera (video input) device list |
| isMicrophoneEnabled | Check whether the local microphone is enabled |
| getVersion | Get the SDK version |
NCCallSession
NCCallSession represents a call and provides methods for reading call details.
| Method | Description |
|---|---|
| getCallId() | Get the call ID |
| getCallState() | Get the call state as NCCallState |
| getCallType() | Get the call type as NCCallType |
| getMediaType() | Get the call media type as NCCallMediaType |
| getParticipants() | Get the participant list. Each item contains the participant user ID and the participant state as NCCallUserState. |
| getCallerUserId() | Get the caller user ID |
| getInvitorUserId() | Get the inviter user ID |
| getStartTime() | Get the call start time |
| getDuration() | Get the call duration |
INCCallEventsListener
INCCallEventsListener provides callbacks for incoming calls, call state changes, and call logs.
| Callback | Description |
|---|---|
| onCallReceived | Fired when the local user receives an incoming call. session is the current call session and extra is the extra data passed by the caller. |
| onCallEnded | Fired when a call ends. In a group call, a local hang-up does not always mean the entire call has ended. reason is the end reason. |
| onCallConnected | Fired when the call is established. The SDK publishes media internally after this point. |
| onLocalCallLogReceived | Fired when the local call log as INCCallLog is available |
| onServerCallLogReceived | Fired when the server pushes a call log as INCCallLog |
| onMediaTypeChangeRequestReceived | Fired when a media type change request arrives. This callback is supported only in 1-to-1 calls. The parameters include the requesting userId, the transactionId, and the target mediaType. |
| onMediaTypeChangeResultReceived | Fired when the media type change result is available. This callback is supported only in 1-to-1 calls. |
| onRemoteCameraStateChanged | Fired when the remote camera state changes. disabled === true means the camera is off. |
| onRemoteMicrophoneStateChanged | Fired when the remote microphone state changes. disabled === true means the microphone is off. |
| onRemoteUserInvited | Fired in group calls when remote users are invited to join |
| onRemoteUserStateChanged | Fired when a participant state changes during the call |
| onUserMediaAvailable | Fired when audio or video for a specific user is ready to play. Use it together with playMedia. |
| onStartTimeReceived | Fired when the call start time is available. The payload includes callId, callType, and callStartTime. |
Call flow
A 1-to-1 call in Call SDK is a call between two users. The flow is:
- The caller starts a call. The SDK creates audio and video resources for the caller. The callee receives
onCallReceived. - After the callee answers, the SDK creates audio and video resources for the callee.
- When the answer succeeds, the caller's and callee's media resources are published to Nexconn media servers.
- Nexconn signaling notifies both sides that the other user's media is available.
- Both sides call
setVideoViewto bind the video they want to watch. Audio is handled internally by the SDK. - Both sides can call
playMediainsideonUserMediaAvailableto play the target user's media. Always check both the user ID and the media type. Do not play locally captured audio on the same device, or you might cause echo or feedback.
For the caller, startCall returns a promise that resolves to { code: number, callId?: string }. For the callee, onCallReceived provides the NCCallSession object. Both sides can call getCurrentCallSession on the NCCallEngine instance to get the current call.
Prerequisites
- Audio and video calling is enabled for your App Key.
- You have initialized the Call SDK and obtained an
NCCallEngineinstance. In this document,ncCallEnginerefers to that initialized instance.
Register the listener
Call SDK for Web provides INCCallEventsListener so your app can react to events from remote users and the server. For example, you can answer or reject incoming calls, accept or reject media type upgrade requests, or show the call duration after the call start time becomes available.
After you get the NCCallEngine instance, call setCallEventsListener():
// Register app-level call events.
ncCallEngine.setCallEventsListener({
/**
* Incoming call notification.
* @param session Current call session
* @param extra Extra data passed by the caller when the call was started
*/
async onCallReceived(session: NCCallSession, extra?: string) {
const callId = session.getCallId();
console.log(`Incoming call, callId: ${callId}`);
const { code } = await ncCallEngine.acceptCall(callId);
console.log(`Accept call result, code: ${code}`);
},
/**
* Fired when the call ends.
* In a group call, a local hang-up does not always mean the entire call has ended.
* @param session Current call session
* @param reason End reason
*/
onCallEnded(session: NCCallSession, reason: NCCallEndReason) {
console.log(`Call ended, callId: ${session.getCallId()}, reason: ${reason}`);
},
/**
* Fired when the call is established. The SDK publishes media internally after this point.
* @param session Current call session
*/
onCallConnected(session: NCCallSession) {
console.log(`Call connected, callId: ${session.getCallId()}`);
},
/**
* Fired when the local call log is available.
* @param record Call log. See `INCCallLog` in the API documentation.
*/
onLocalCallLogReceived(record: INCCallLog) {
console.log('Local call log', record);
},
/**
* Fired when a server call log is delivered.
* @param record Call log. See `INCCallLog` in the API documentation.
*/
onServerCallLogReceived(record: INCCallLog) {
console.log('Server call log', record);
},
/**
* Fired when a media type change request arrives. Supported only in 1-to-1 calls.
* @param userId User ID that requested the change
* @param transactionId Transaction ID for this request and response pair
* @param mediaType Target media type
*/
onMediaTypeChangeRequestReceived(
userId: string,
transactionId: string,
mediaType: NCCallMediaType,
) {
console.log(`Media type change request, userId: ${userId}, transactionId: ${transactionId}, mediaType: ${mediaType}`);
ncCallEngine.replyChangeMediaType(transactionId, true);
},
/**
* Fired when the media type change result is available. Supported only in 1-to-1 calls.
* @param info.userId Related user ID
* @param info.transactionId Transaction ID that matches the original request
* @param info.mediaType Negotiated media type
* @param info.result Change result
*/
onMediaTypeChangeResultReceived(info: {
userId: string;
transactionId: string;
mediaType: NCCallMediaType;
result: NCCallMediaTypeChangeResult;
}) {
console.log('Media type change result', info);
},
/**
* Fired when the remote camera state changes.
* @param callId Call ID
* @param userId Remote user ID
* @param disabled `true` means the camera is off
*/
onRemoteCameraStateChanged(callId: string, userId: string, disabled: boolean) {
console.log(`Remote camera state, callId: ${callId}, userId: ${userId}, disabled: ${disabled}`);
},
/**
* Fired when the remote microphone state changes.
* @param callId Call ID
* @param userId Remote user ID
* @param disabled `true` means the microphone is off
*/
onRemoteMicrophoneStateChanged(callId: string, userId: string, disabled: boolean) {
console.log(`Remote microphone state, callId: ${callId}, userId: ${userId}, disabled: ${disabled}`);
},
/**
* Fired in a group call when remote users are invited.
* @param inviteeUserList Invited user IDs
* @param inviterUserId User ID that sent the invitation
* @param callId Call ID
*/
onRemoteUserInvited(inviteeUserList: string[], inviterUserId: string, callId: string) {
console.log(`Remote users invited, callId: ${callId}, inviteeUserList: ${inviteeUserList}, inviterUserId: ${inviterUserId}`);
},
/**
* Fired when a participant state changes during the call.
* @param callId Call ID
* @param userId User whose state changed
* @param state New state. See `NCCallUserState`.
* @param reason Optional reason for the state change
*/
onRemoteUserStateChanged(
callId: string,
userId: string,
state: NCCallUserState,
reason?: NCCallEndReason,
) {
console.log(`Participant state changed, callId: ${callId}, userId: ${userId}, state: ${NCCallUserState[state]}, reason: ${reason}`);
},
/**
* Fired when audio or video for a specific user is ready to play.
* Do not play locally captured audio on the same device, or you might cause echo or feedback.
* @param userId User ID
* @param mediaType Media type
*/
onUserMediaAvailable(userId: string, mediaType: NCCallMediaType) {
ncCallEngine.playMedia(userId, mediaType);
},
/**
* Fired when the call start time becomes available.
* @param info.callId Call ID
* @param info.callType Call type. See `NCCallType`.
* @param info.callStartTime Call start timestamp determined by the server or engine
*/
onStartTimeReceived(info: { callId: string; callType: NCCallType; callStartTime: number }) {
console.log(`Call timer started, callId: ${info.callId}, startTime: ${info.callStartTime}`);
},
});
If you do not register INCCallEventsListener, you cannot receive incoming call events such as onCallReceived. Register it as soon as possible after you initialize the Call SDK.
Add media playback elements
A video playback element is an HTMLVideoElement <video> that displays a video stream. To show local and remote video, add the following two elements to your page. The element with the ID local_video_element_id is for local video, and the element with the ID remote_video_element_id is for remote video. Audio playback uses an internal Audio object created by the SDK, so your app does not need to manage it.
<video id="local_video_element_id"></video>
<video id="remote_video_element_id"></video>
After these elements are ready, call ncCallEngine.setVideoView to bind the video elements for the users you want to watch. After you bind a video view for user A, the SDK notifies your app through onUserMediaAvailable when A's video becomes available. You can then call ncCallEngine.playMedia() to start playback. Do not play locally captured audio on the same device, or you might cause echo or feedback.
/**
* Set media players for the users you want to watch, including the local user and the remote user.
* @param list A list of media player bindings
* @param item.userId User ID
* @param item.videoElement A video element mounted in your page
* @param item.useTinyStream Whether to subscribe to the remote user's low-resolution stream
* @returns code Whether the call succeeded
*/
setVideoView(list: { userId: string; videoElement: HTMLVideoElement; useTinyStream?: boolean }[]): { code: number }
Set the local and remote video players before placing or answering the call:
ncCallEngine.setVideoView([
{
userId: '<local-userId>',
videoElement: document.getElementById('local_video_element_id') as HTMLVideoElement,
},
{
userId: '<remote-userId>',
videoElement: document.getElementById('remote_video_element_id') as HTMLVideoElement,
useTinyStream: true,
},
]);
You must set the local video player before placing or answering a call. You can set the remote player's view in advance, or only after the remote user reaches NCCallUserState.ONCALL in onRemoteUserStateChanged. If you want the remote low-resolution stream, set useTinyStream to true.
Start a call
The startCall method accepts optional pushConfig and extra parameters. pushConfig carries mobile push settings through INCCallPushConfig, including pushTitle, pushContent, and platform-specific push attributes. extra carries custom data that is passed through to the callee.
Call startCall with the callee user ID, the call type, and the media type. If the call starts successfully, the SDK returns a numeric code and, on success, a callId.
/**
* Start a call.
* @param calleeIds Callee user ID. For a one-to-one call, you can pass a single string.
* @param callType Call type: single or multi-party
* @param mediaType Call media type: audio or video
* @param pushConfig Optional mobile push configuration
* @param extra Optional passthrough data for the callee
* @returns callId Session ID created after the call starts successfully
*/
const { code, callId } = await ncCallEngine.startCall(
'<userId>',
NCCallType.SINGLE,
NCCallMediaType.VIDEO,
);
Answer a call
The callee must register INCCallEventsListener before the app can receive onCallReceived and other call events.
Use acceptCall to answer the call or endCall to reject it. If the call is answered, Call SDK establishes the media session automatically.
ncCallEngine.setCallEventsListener({
/**
* Incoming call notification.
* When a call comes in, you can answer or hang up.
* @param session Current call session
* @param extra Extra data passed by the caller when the call was started
*/
async onCallReceived(session: NCCallSession, extra?: string) {
const callId = session.getCallId();
const { code } = await ncCallEngine.acceptCall(callId);
console.log(`Accept call result, code: ${code}`);
}
});
Call SDK receives call events through the IM connection to Nexconn, which is established by NCEngine.connect(). If the network disconnects unexpectedly, the SDK reconnects automatically.
Manage the current call
Audio
During the call, both the caller and the callee can turn the microphone on or off by calling enableMicrophone with true or false. If one side changes the microphone state, the other side receives onRemoteMicrophoneStateChanged.
/**
* Turn the microphone on or off.
* @param enable `true` to turn it on, `false` to turn it off
* @description This call can fail during an active call if resource propagation fails and the remote side does not receive the local state update.
*/
async enableMicrophone(enable: boolean): Promise<{ code: number }>
/**
* Select the microphone input device.
* After the switch succeeds, `onUserMediaAvailable` receives the newly selected microphone stream,
* and you can call `ncCallEngine.playMedia()` to play it.
* @param microphoneId Microphone device ID from the browser. Call `getAudioInputDevices` to get the list.
* @description You can call this before the call or during the call.
*/
async selectAudioInputDevice(microphoneId: string): Promise<{ code: number }>
/**
* Select the audio output device, such as a speaker.
* @param deviceId Speaker device ID
*/
async selectAudioOutputDevice(deviceId: string): Promise<{ code: number }>
Video
During the call, both the caller and the callee can turn the camera on or off by calling enableCamera with true or false. If one side changes the camera state, the other side receives onRemoteCameraStateChanged.
/**
* Turn the camera on or off.
* @param enable `true` to turn it on, `false` to turn it off
* @description This call can fail during an active call if resource propagation fails and the remote side does not receive the local state update.
*/
async enableCamera(enable: boolean): Promise<{ code: number }>
/**
* Switch the camera.
* After the switch succeeds, `onUserMediaAvailable` receives the newly selected camera stream,
* and you can call `ncCallEngine.playMedia()` to display it.
* @param cameraId Camera device ID from the browser. Call `getVideoInputDevices` to get the list.
* @description You can call this before the call or during the call.
*/
async switchCamera(cameraId: string): Promise<{ code: number }>
Change the media type
Only 1-to-1 calls support media type changes during a call.
Call SDK defines NCCallMediaType.VIDEO for video calls and NCCallMediaType.AUDIO for audio calls. During a 1-to-1 call, either side can call requestChangeMediaType to request a media type change.
/**
* Request a media type change during the call.
* @param mediaType Target media type
* @returns transactionId Transaction ID used in later cancel or reply calls
*/
async requestChangeMediaType(
mediaType: NCCallMediaType.AUDIO | NCCallMediaType.VIDEO,
): Promise<{ code: number; transactionId?: string }>
The requesting side can also call cancelChangeMediaType to cancel the request.
/**
* Cancel a pending media type change request.
* @param transactionId Transaction ID returned by the original media type change request
*/
async cancelChangeMediaType(transactionId: string): Promise<{ code: number }>
The remote user receives the request through onMediaTypeChangeRequestReceived. After that, the remote side can call replyChangeMediaType on the NCCallEngine instance to accept or reject the change.
/**
* Reply to a media type change request.
* @param transactionId Transaction ID received in `onMediaTypeChangeRequestReceived`
*/
async replyChangeMediaType(transactionId: string, isAgree: boolean): Promise<{ code: number }>
The final result is delivered to both sides through onMediaTypeChangeResultReceived.
/**
* Media type change result. Supported only in 1-to-1 calls.
* @param info.userId Related user ID
* @param info.transactionId Transaction ID that uniquely identifies this request and response
* @param info.mediaType Final media type
* @param info.result Change result
* - The requester canceled the request
* - The responder rejected the request
* - The server allowed the media type change
*/
onMediaTypeChangeResultReceived(info: {
userId: string,
transactionId: string,
mediaType: NCCallMediaType,
result: NCCallMediaTypeChangeResult
}): void
End the call
Note
The
endCallmethod accepts an optionalpushConfigparameter.pushConfigcarries mobile push settings throughINCCallPushConfig, includingpushTitle,pushContent, and platform-specific push attributes.
Use endCall to hang up an active call or reject an incoming call. Both the caller and the callee can call endCall(). If one side ends the call, the other side receives INCCallEventsListener.onCallEnded().
/**
* End a call.
* @param callId Optional. If omitted, the SDK ends the current primary session.
* If provided, it can end either the main call or a ringing call that has not been answered yet.
* A ringing call that has not been answered means a call that arrives while another call is already active.
*/
async endCall(callId?: string, pushConfig?: INCCallPushConfig): Promise<{ code: number }>
In a 1-to-1 call, if either side hangs up, the call ends for both sides and Nexconn stops billing immediately.
Answer an incoming call while already in a call
You can answer a new incoming call while another call is active. Because only one active call is allowed at a time, the SDK ends the current call internally before answering the new one.
Note
Call SDK does not support holding and resuming calls during another active call.
Use acceptCall to answer the incoming call. The new call becomes the current active call.
/**
* Answer a call.
* @param callId Call ID
*/
async acceptCall(callId: string, pushConfig?: INCCallPushConfig): Promise<{ code: number }>
Get call logs
Get call logs after the call ends
After the call ends, or after the user rejects the call, the engine can deliver the server-side call log through onServerCallLogReceived as INCCallLog. In one-to-one calls, the SDK can also deliver a locally constructed call log through onLocalCallLogReceived.
/** Server call log delivered as INCCallLog */
onServerCallLogReceived(record: INCCallLog): void
/** Local call log delivered as INCCallLog */
onLocalCallLogReceived(record: INCCallLog): void
Query server-side call logs
You can also call getHistoryCallLogs on the NCCallEngine instance to fetch the user's call history from the Nexconn server.
The order parameter of getHistoryCallLogs supports reverse-order queries. For example, when you page through recent calls, pass -1 for startTime on the first request. To continue, pass the returned syncTime as the next startTime.
/**
* Query the current user's call history with pagination.
* @param startTime Start timestamp. Pass -1 for the first request.
* @param count Number of records per request. Pass a positive integer.
* @param order Sort order. The default is ascending.
* 0 means ascending and queries after `startTime`.
* 1 means descending and queries before `startTime`.
* @returns result.hasMore Whether more call logs are available
* @returns result.callLogList Call log list as INCCallLog[]
* @returns result.syncTime When hasMore is true, pass this value as the next `startTime`
*/
async getHistoryCallLogs(startTime: number, count: number, order: 0 | 1 = 0): Promise<{ code: number; result?: INCCallLogInfo }>
Manage audio and video configuration
Call SDK uses the browser's default camera and microphone for the local user. The default video resolution is W640_H480, and the default frame rate is FPS_15. You can build custom video and audio configurations with NCVideoConfigBuilder and NCAudioConfigBuilder, then apply them through setVideoConfig and setAudioConfig.
Set the local video capture configuration
Create a NCVideoConfigBuilder first. Use it to configure the video resolution, frame rate, camera ID, and video bitrate.
import { NCCallVideoResolution, NCCallVideoFrameRate } from '@nexconn/call';
/**
* Create a video configuration builder.
* @returns NCVideoConfigBuilder Used to configure video capture settings such as
* camera ID, frame rate, and resolution. Call build(), then apply the result through setVideoConfig().
*/
const videoConfigBuilder = ncCallEngine.createVideoConfigBuilder();
// Set the resolution.
videoConfigBuilder
.setVideoResolution(NCCallVideoResolution.W640_H360)
.setFrameRate(NCCallVideoFrameRate.FPS_24)
// Call `ncCallEngine.getVideoInputDevices()` to get available values.
.setDefaultCameraId(cameraId)
.setBitrate(min, max);
// Build the video configuration.
const videoConfig = videoConfigBuilder.build();
Apply the video configuration:
/**
* Apply the video configuration.
* It takes effect the next time media capture starts, such as after you switch the camera
* or establish a new call.
*/
ncCallEngine.setVideoConfig(videoConfig);
Change the local audio capture configuration
Create a NCAudioConfigBuilder first. Use it to configure the microphone ID, audio sample rate, and audio bitrate.
Build the audio configuration:
/**
* Create an audio configuration builder.
* @returns NCAudioConfigBuilder Used to configure audio capture settings such as the microphone ID.
* Call build(), then apply the result through setAudioConfig().
*/
const audioConfigBuilder = ncCallEngine.createAudioConfigBuilder();
audioConfigBuilder
// Call `ncCallEngine.getAudioInputDevices()` to get available values.
.setDefaultMicrophoneId(microphoneId)
.setBitrate(min, max)
.setSampleRate(sampleRate);
// Build the audio configuration.
const audioConfig = audioConfigBuilder.build();
Apply the audio configuration:
/**
* Apply the audio configuration.
* It takes effect the next time media capture starts, such as when a new call is established.
*/
ncCallEngine.setAudioConfig(audioConfig);
Manage call quality
Users can receive notifications when call quality changes so they can check their network connection and reduce call interruptions.
Set IConnectionQualityListener
To monitor call quality changes, register IConnectionQualityListener. Call setConnectionQualityListener() on the NCCallEngine instance.
/**
* Register a call quality listener, including send and receive quality and volume.
* @param listener Type `IConnectionQualityListener`. See the API documentation for definitions and callback frequency.
*/
setConnectionQualityListener(listener: IConnectionQualityListener): void
IConnectionQualityListener includes the following callbacks:
| Callback | Description |
|---|---|
| onSendQualityChanged | The local send link quality changed |
| onReceiveQualityChanged | The receive link quality changed. Data can include multiple users. |
| onAudioVolumeChanged | The call volume changed |