The SDK supports a preflight test API which can help determine Voice calling readiness. The API creates a test call and will provide information to help troubleshoot call related issues. This new API is a static member of the Device class and can be used by calling Device.runPreflight(token, options)
. For example:
import { Device, PreflightTest } from '@twilio/voice-sdk';
const preflightTest = Device.runPreflight(token, options);
preflightTest.on(PreflightTest.Events.Completed, (report) => {
console.log(report);
});
preflightTest.on(PreflightTest.Events.Failed, (error) => {
console.log(error);
});
Device.runPreflight(token, options)
requires a Twilio Access Token to initiate the test call. This access token will be passed directly to the Device's constructor and will be used to connect to a TwiML app that you associated with your Twilio Access Token. In order to get better results, the TwiML app should be able to record audio from a microphone and play it back to the browser. Please see Preflight Test TwiML App for details.
The PreflightTest.Options
parameter is a JavaScript object containing configuration settings. Available settings are listed below:
Property | Default | Description |
---|---|---|
codecPreferences |
['pcmu', 'opus'] |
An ordered list of preferred codecs. |
debug |
false |
Can be true or false . Set this property to true to enable debug logging in your browser console. |
edge |
roaming |
Specifies which Twilio edge to use when initiating the test call. Please see documentation on edges. |
fakeMicInput |
false |
If set to true , the test call will ignore microphone input and will use a default audio file. If set to false , the test call will capture the audio from the microphone. |
iceServers |
null |
An array of custom ICE servers to use to connect media. If you provide both STUN and TURN server configurations, the test will detect whether a TURN server is required to establish a connection. See Using Twilio NTS for Generating STUN/TURN Credentials |
signalingTimeoutMs |
10000 |
Ammount of time to wait for setting up signaling connection. |
The following example demonstrates how to use Twilio's Network Traversal Service to generate STUN/TURN credentials and how to specify a specific edge location.
import Client from 'twilio';
import { Device } from '@twilio/voice-sdk';
// Generate the STUN and TURN server credentials with a ttl of 120 seconds
const client = Client(twilioAccountSid, authToken);
const token = await client.tokens.create({ ttl: 120 });
let iceServers = token.iceServers;
// By default, global will be used as the default edge location.
// You can replace global with a specific edge name for each of the iceServer configuration.
iceServers = iceServers.map(config => {
let { url, urls, ...rest } = config;
url = url.replace('global', 'ashburn');
urls = urls.replace('global', 'ashburn');
return { url, urls, ...rest };
});
// Use the TURN credentials using the iceServers parameter
const preflightTest = Device.runPreflight(token, { iceServers });
// Read from the report object to determine whether TURN is required to connect to media
preflightTest.on('completed', (report) => {
console.log(report.isTurnRequired);
});
The PreflightTest
object that is returned by Device.runPreflight(token, options)
is an EventEmitter, and as such its events can be subscribed to via preflightTest.on(eventName, handler)
. The following is a list of all supported events that might get emitted throughout the duration of the test.
Raised when PreflightTest.status
has transitioned to PreflightTest.Status.Completed
. During this time, the report
is available and ready to be inspected. This will not trigger if a fatal error is encountered during the test. Example report:
{
"callSid": "CAa6a7a187a9cba2714d6fdccf472cc7b1",
/**
* The quality of the call, determined by the MOS (Mean Opinion Score) of the audio stream. Possible values include
* PreflightTest.CallQuality.Excellent - If the average mos is over 4.2
* PreflightTest.CallQuality.Great - If the average mos is between 4.1 and 4.2 both inclusive
* PreflightTest.CallQuality.Good - If the average mos is between 3.7 and 4.0 both inclusive
* PreflightTest.CallQuality.Fair - If the average mos is between 3.1 and 3.6 both inclusive
* PreflightTest.CallQuality.Degraded - If the average mos is 3.0 or below
*/
"callQuality": "excellent",
/**
* An array of WebRTC stats for the ICE candidates gathered when connecting to media.
* Each item is an RTCIceCandidateStats object which provides information related to an ICE candidate.
* See RTCIceCandidateStats for more details https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidateStats
*/
"iceCandidateStats": [...]
/**
* A WebRTC stats for the ICE candidate pair used to connect to media, if candidates were selected.
* Each item is an RTCIceCandidateStats object which provides information related to an ICE candidate.
* See RTCIceCandidateStats for more details https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidateStats
*/
"selectedIceCandidatePairStats": {
"localCandidate": {...},
"remoteCandidate": {...}
}
/**
* Whether a TURN server is required to connect to media.
* This is dependent on the selected ICE candidates, and will be true if either is of type "relay",
* false if both are of another type, or undefined if there are no selected ICE candidates.
* See `PreflightTest.Options.iceServers` for more details.
*/
"isTurnRequired": false,
/**
* Network related time measurements which includes millisecond timestamps
* and duration for each type of connection.
*/
"networkTiming": {
/**
* Measurements for establishing DTLS connection.
* This is measured from RTCDtlsTransport `connecting` to `connected` state.
* See RTCDtlsTransport state
* https://developer.mozilla.org/en-US/docs/Web/API/RTCDtlsTransport/state.
*/
"dtls": {
"start": 1584573229981,
"end": 1584573230166,
"duration": 185
},
/**
* Measurements for establishing ICE connection.
* This is measured from ICE connection `checking` to `connected` state.
* See RTCPeerConnection.iceConnectionState
* https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState.
*/
"ice": {
"start": 1584573229898,
"end": 1584573229982,
"duration": 84
},
/**
* Measurements for establishing a PeerConnection.
* This is measured from PeerConnection `connecting` to `connected` state.
* See RTCPeerConnection.connectionState
* https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState.
*/
"peerConnection": {
"start": 1584573229902,
"end": 1584573230167,
"duration": 265
},
/**
* Measurements for establishing Signaling connection.
* This is measured from initiating a connection using `device.connect()`,
* up to when RTCPeerConnection.signalingState transitions to `stable` state.
* See RTCPeerConnection.signalingState
* https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/signalingState
*/
"signaling": {
"start": 1595885835227,
"end": 1595885835573,
"duration": 346
}
},
/**
* RTC related stats that are extracted from WebRTC samples.
* This information includes maximum, minimum, and average values calculated for each stat.
*/
"stats": {
"jitter": {
"average": 35,
"max": 35,
"min": 35
},
"mos": {
"average": 2,
"max": 2,
"min": 2
},
"rtt": {
"average": 80.33,
"max": 88,
"min": 77
}
},
/**
* Timing measurement related to the test.
* Includes millisecond timestamps and duration.
*/
"testTiming": {
"start": 1584573229085,
"end": 1584573242279,
"duration": 13194
},
/**
* Calculated totals in RTC statistics samples.
*/
"totals": {
"bytesReceived": 62720,
"bytesSent": 93760,
"packetsLost": 0,
"packetsLostFraction": 0,
"packetsReceived": 392,
"packetsSent": 586
},
/**
* Array of samples collected during the test.
* See sample object format here
* https://www.twilio.com/docs/voice/sdks/javascript/twiliocall#sample-event
*/
"samples": [...],
/**
* The edge passed to `Device.runPreflight`.
*/
"selectedEdge": "roaming",
/**
* The edge that the test call was connected to.
*/
"edge": "ashburn",
/**
* PreflightTest.Warnings detected during the test.
*/
"warnings": [...]
}
Raised when PreflightTest.status
has transitioned to PreflightTest.Status.Connected
. This means, the connection to Twilio has been established.
Raised when PreflightTest.status
has transitioned to PreflightTest.Status.Failed
. This happens when establishing a connection to Twilio has failed or when a test call has encountered a fatal error. This is also raised if PreflightTest.stop
is called while the test is in progress. The error emitted from this event is coming from Device.on('error) and uses the same error format.
This event is published every second and is raised when the Call gets a webrtc sample object. The sample
object is coming from Call.on('sample') and uses the same sample
format.
Raised whenever the test encounters a warning. Example warning data:
{
/**
* Name of the warning.
* See https://www.twilio.com/docs/voice/voice-insights/api/call/details-sdk-call-quality-events
*/
name: 'insights-connection-error',
/**
* Description of the Warning.
*/
description: 'Received an error when attempting to connect to Insights gateway',
/**
* Optional RTCWarning data coming from Call.on('warning')
* See https://www.twilio.com/docs/voice/sdks/javascript/twiliocall#warning-event
*/
rtcWarning: {...}
}
You can access the following properties on the PreflightTest
object:
-
callSid
- The callsid generated for the test call. This is set when the client has finished connecting to Twilio. -
endTime
- A timestamp in milliseconds of when the test ended. This is set when the test has completed and raised thecompleted
event. -
latestSample
- The latest WebRTC sample collected. This is set whenever the connection emits asample
. Please see Call.on('sample') API for more details. -
report
- The report for this test. This is set when the test has completed and raised thecompleted
event. -
startTime
- A timestamp in milliseconds of when the test started. This is set right after callingDevice.runPreflight(token, options)
. -
status
- The status of the test. Below are the possible values for this property.Value Description Completed The connection to Twilio has been disconnected and the test call has completed. Connected The connection to Twilio has been established. Connecting Connecting to Twilio has started. Failed The test has stopped and failed.
Calling this method from the PreflightTest
object will stop the existing test and will raise a failed
event with an error code 31008
indicating that the call has been cancelled.
If PreflightTest.Options.fakeMicInput
is set to false
, Device.runPreflight(token, options)
API requires a token with a TwiML app that can record an audio from a microphone and the ability to play the recorded audio back to the browser. In order to achieve this, we need two TwiML endpoints: one to capture and record the audio, and another one to play the recorded audio.
In this example, we will use TwiML Bins for our TwiML app. Start by going to the TwiML Bin page in the Twilio Console.
Create a new TwiML Bin with the plus button on that screen and use Playback
as the friendly name. Then use the following template under the TwiML section.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>You said:</Say>
<Play loop="1">{{RecordingUrl}}</Play>
<Say>Now waiting for a few seconds to gather audio performance metrics.</Say>
<Pause length="3"/>
<Say>Hanging up now.</Say>
</Response>
Go ahead and click the Create button and copy the TwiML Bin's url located at the top of the screen.
Using the TwiML Bin page, let's create another TwiML Bin by clicking the plus button on that screen and use Record
as the friendly name. Then replace the action url in the following template with your TwiML Bin's Playback url that you created previously. Let's use this template under the TwiML section.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>Record a message in 3, 2, 1</Say>
<Record maxLength="5" action="https://my-record-twiml-url"></Record>
<Say>Did not detect a message to record</Say>
</Response>
Go ahead and click the Create button and copy the TwiML Bin's url located at the top of the screen.
Now that we have created our TwiML Bins, let's create our TwiML app by going to the TwiML Apps page. Click the plus button on that screen and enter a friendly name that you prefer. Under Voice request url, enter the TwiML Bin's Record url that you created in the previous section, and then click the Create button.
On that same page, open the TwiML app that you just created by clicking on it and make note of the SID
. You can now use this TwiML app to generate your token when calling Device.runPreflight(token, options)
API.
If PreflightTest.Options.fakeMicInput
is set to true
, Device.runPreflight(token, options)
API requires a token with a single TwiML app that can capture and play an audio. Following the previous steps, create a TwiML Bin using the following template, and name it Echo
.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Echo/>
</Response>
Now that we have our Echo
TwiML Bin, create your TwiML App and use the echo
TwiML Bin's url under Voice request url. Make note of the TwiML App SID
and use it to generate the token.