vobiz_webrtc SDK signs in to Vobiz over SIP on a WebSocket and carries the audio over WebRTC.
Source code, API reference and example app: vobiz-ai/vobiz-flutter-sdk
Features
- Make and receive calls, to phone numbers and SIP endpoints
- Hold, mute, loudspeaker and DTMF
- Call quality metrics: MOS, jitter, round-trip time and packet loss
- Sign-in with short-lived access tokens, renewed without dropping a call
- Caller ID, call recording and custom
X-VH-SIP headers - Calls that survive short network drops
- Incoming calls while the app is closed, through Firebase push
- Conference calls, through your server
Usage
Credentials
To make and receive calls with the SDK, you need:- A Vobiz Console account.
- Your Auth ID and Auth Token, from API Credentials on the dashboard. They stay on your server.
- An application in Voice Applications whose Answer URL is on your server. Copy its Application ID: calls that the app places go to this Answer URL.
- A Vobiz number from Numbers, to use as the caller ID.
Install the SDK
pubspec.yaml
flutter pub get. The SDK needs Flutter 3.19 or later and Android 7.0 (API 24) or later.
Android configuration
Add these permissions toandroid/app/src/main/AndroidManifest.xml:
AndroidManifest.xml
BLUETOOTH_CONNECT is a runtime permission on Android 12 and later: ask for it before you send audio to a headset.
Basic usage
Vobiz client
VobizClient is the core class of the SDK. It signs in, places and answers calls, and reports what happens on streams.
SdkConfig, for example VobizClient(config: const SdkConfig(autoReconnect: false)):
Logging configuration
Turn ondebug to print SDK logs while you develop, or pass a Logger to send every line to your own logging:
[INFO], [WARN] or [ERROR]. The SDK never logs access tokens.
Sign in to the Vobiz client
Sign each user in with a short-lived access token that your server mints. The SDK asks your server for a new token before the current one expires, and registers again without dropping a call. 1. Mint the token on your server. Behind your own user authentication, call the Vobiz API with your Auth ID and Auth Token, read from the server’s environment:server.js
<sub>_<auth_id>, for example alice_MA_XXXXXXXX.
2. Sign in from the app. VobizBackendAccessTokenProvider posts {"reason": ...} to your token URL and reads {"token": ...} from the reply:
RegistrationState.registered before you place a call. When the user signs out, call session.dispose() and then client.disconnect().
The client has four ways to sign in:
Make a call
makeCall places a call and returns its ID:
To set to the number that the app dialled and the X-VH- headers included. Your Answer URL decides what the call does. For example, it can dial the number, using X-VH-Caller-ID as the caller ID:
makeCall throws an ArgumentError for an invalid header or caller ID before anything is dialled. See Custom SIP headers for the header rules.
Accept a call
Listen tocallEvents to know when a call rings, connects or ends. Then accept the call from your answer button:
<Dial> to the user’s SIP identity:
<User>.
Decline or end a call
DTMF (dual-tone multi-frequency)
Mute a call
client.isMuted gives the current state.
Toggle the loudspeaker
client.audioRoutes lists the outputs (availableRoutes()) and switches between them (selectRoute(route)).
Put a call on hold
VobizException (hold_failed or resume_failed), and the call carries on as it was.
Call quality metrics
Once audio flows, the SDK measures the call every 2 seconds and reports aCallQualityEvent on callMediaEvents. You don’t need to turn anything on:
client.getCallStats(). It returns rttMs, jitterMs and packetLossPercent.
CallQualitySample properties
A value is
null until WebRTC has measured it. mos is an estimate from a simplified ITU-T G.107 E-model, not a measurement.
CallQualityLevel enum
levelChanged is true only when the level moves, and the level moves only after two samples in a row agree, so a warning doesn’t flicker.
Advanced usage: push notifications
How push works on Vobiz
The SDK signs in over a WebSocket. When Android closes your app, the socket closes, and Vobiz can’t ring the app. To ring a closed app, your server answers the call and wakes the app with a Firebase Cloud Messaging (FCM) push:- A call reaches your Vobiz number, and Vobiz requests your server’s Answer URL.
- Your server sends the user’s devices a high-priority FCM data message.
-
The app shows the system call screen and signs in. Your server waits for that, then dials the user with a
PushRefSIP header set to the push’scall_id: -
If the caller hangs up first, your server sends a
call_endedmessage.
example/push_bridge) does all four steps. Its push data has these keys, and every value is a string:
Add push notifications to your Android app
The SDK reads the push (VobizIncomingPush) and finds the call that it announced. The call screen in these steps comes from the flutter_callkit_incoming plugin, as in the SDK’s example app.
-
Add Firebase to your app. Create a Firebase project, run
flutterfire configureto add its configuration, and then add the packages: -
Send the device’s FCM token to your server, with the signed-in user, so your server knows where to push:
-
Show the call screen when a push arrives. Register one top-level handler for pushes in the background and in the foreground:
Themain.dart
ckprefix keeps the plugin’sCallEventapart from the SDK’s. -
Answer the call that the user accepted. Accept on the call screen opens your app. Sign in, then answer the INVITE whose
PushRefmatches the push, and only that one: -
Report a decline. The app hasn’t signed in, so it can’t reject the call itself. When the user taps Decline, send the push’s
call_idto your server and have it hang up the caller. The example push server’sPOST /push/declinedoes this. Also stop ringing on your server afterttl_ms, in case the decline never arrives.
Best practices for push notifications on Android
-
Ask for permissions when the user signs in, not on the first ring. Android 13 and later need the notification permission (
FirebaseMessaging.instance.requestPermission()). On Android 14 and later, the call screen needs Full screen notifications to take over a locked screen: check it withck.FlutterCallkitIncoming.canUseFullScreenIntent()and ask for it withrequestFullIntentPermission(). Without it, the call shows as a heads-up notification. - Test a release build, with the app swiped away. Android delivers no pushes to a force-stopped app, and stopping a debug build from your IDE force-stops it.
-
Handle late pushes. A push can arrive after the caller has given up.
push.isExpired()istrueoncettl_mshas passed: show a missed call instead of ringing. -
Handle an early answer. The user can tap Accept before the app has signed in. Keep the push, as step 4 does, and answer when the matching INVITE arrives. If it hasn’t arrived within about 60 seconds, end the call screen with
ck.FlutterCallkitIncoming.endCall(id). -
Show call waiting when the user is on a call. When
push.lineBusyistrue, show a notification instead of the full-screen call screen. -
Remove the camera permissions that flutter_callkit_incoming adds if your app is audio-only. Otherwise, Play Console asks you to justify a camera foreground service:
AndroidManifest.xml
Conference calls
VobizConference turns the current call into a Vobiz conference room, so you can add people and manage them. Moving calls and dialling people need your Auth Token, so the SDK asks your server, which makes the Vobiz API calls:
add moves both sides of the call into a new room, and then dials the person in. People you add see callerId, which must be a number on your account. Your server implements four routes, and each one answers with the room’s state:
The example push server in the SDK repository implements all four.
Additional resources
- SDK source, API reference and example app
- SIP endpoints
- Custom SIP headers
- The
<User>element - Vobiz WebRTC Playground, for calls from a browser