A Twilio.PreflightTest
object represents a test call to Twilio which provides information to help troubleshoot call-related issues when using the Voice JavaScript SDK.
You will never instantiate a PreflightTest
instance directly, but it's returned when you call Device.runPreflight(token, options).
Example:
1import { Device } from '@twilio/voice-sdk';23const preflightTest = Device.runPreflight(token, options);45preflightTest.on('completed', (report) => {6console.log(report);7});89preflightTest.on('failed', (error) => {10console.log(error);11});
You will need an Access Token to initiate the test call. The Access Token will be associated with a TwiML application.
You likely already have a TwiML app with a Voice Request URL configured to send an HTTP POST
request to an endpoint on your project's backend.
For the best test results, the endpoint with which you've configured your TwiML App should be able to record audio from a microphone and play it back to the browser.
Your backend application can be modified to handle this behavior, but you can also set up a new TwiML Application configured with TwiML bins specifically for PreflightTest
s. See the TwiML Apps for PreflightTest section below for more information.
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.
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.
Passes the report
object to the completed
event handler.
preflightTest.on('completed', handler(report));
See the Report section below to view an example of this report.
Raised when PreflightTest.status
has transitioned to PreflightTest.Status.Connected
. This indicates that 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.
Passes a TwilioError
object or a DOMError
object to the failed
event handler.
preflightTest.on('failed', handler(error));
Raised when the test Call gets a WebRTC sample object. The event is published every second.
Passes the RTCSample object to the sample
event handler.
preflightTest.on('sample', handler(sample));
Raised whenever the test Call encounters a warning.
Passes the name of the warning (string
) and the Warning object to the warning
event handler.
preflightTest.on('warning', handler(name, warning));
The Call SID for the test call. This is set when the test Device
instance has finished connecting to Twilio.
A timestamp in milliseconds of when the test ended. This is set when the test has completed and raised the completed event.
The latest WebRTC sample collected. This is set whenever the Call emits a sample
.
Returns an RTCSample object or undefined
.
The report for this test. This is set when the test has completed and raised the completed
event.
See the Report section below for more information on the report object.
A timestamp in milliseconds of when the test started. This is set right after calling Device.runPreflight(token, options)
.
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. |
Below is an example report returned to the completed
event handler or accessed via preflightTest.report
.
1{2"callSid": "CAa6a7a187a9cba2714d6fdccf472cc7b1",3"edge": "ashburn",4"iceCandidateStats": [...],5"networkTiming": {6"signaling": {7"start": 1628271850137,8"end": 1628271850958,9"duration": 82110},11"dtls": {12"start": 1628271851032,13"end": 1628271851192,14"duration": 16015},16"ice": {17"start": 1628271850956,18"end": 1628271851032,19"duration": 7620},21"peerConnection": {22"start": 1628271850965,23"end": 1628271851193,24"duration": 22825}26},27"samples": [28{29"audioInputLevel": 10999,30"audioOutputLevel": 1725,31"bytesReceived": 2880,32"bytesSent": 6080,33"codecName": "PCMU",34"jitter": 0,35"mos": null,36"packetsLost": 0,37"packetsLostFraction": 0,38"packetsReceived": 18,39"packetsSent": 38,40"rtt": 66,41"timestamp": 1628271851961.724,42"totals": {43"bytesReceived": 2880,44"bytesSent": 6080,45"packetsLost": 0,46"packetsLostFraction": 0,47"packetsReceived": 18,48"packetsSent": 3849}50}51],52"selectedEdge": "roaming",53"stats": {54"jitter": {55"average": 1.2273,56"max": 3,57"min": 058},59"mos": {60"average": 4.4,61"max": 4.406226172226452,62"min": 4.39052359217017763},64"rtt": {65"average": 69.045,66"max": 84,67"min": 5968}69},70"testTiming": {71"start": 1628271849623,72"end": 1628271874932,73"duration": 2530974},75"totals": {76"bytesReceived": 109280,77"bytesSent": 182080,78"packetsLost": 0,79"packetsLostFraction": 0,80"packetsReceived": 683,81"packetsSent": 113882},83"warnings": [84{85"name": "low-bytes-received",86"description": "Received an RTCWarning. See .rtcWarning for the RTCWarning",87"rtcWarning": {88"values": [890,900,91092],93"samples": [94{95"audioInputLevel": 11718,96"audioOutputLevel": 0,97"bytesReceived": 0,98"bytesSent": 8000,99"codecName": "PCMU",100"jitter": 1,101"mos": 4.404255907879351,102"packetsLost": 0,103"packetsLostFraction": 0,104"packetsReceived": 0,105"packetsSent": 50,106"rtt": 61,107"timestamp": 1628271856961.176,108"totals": {109"bytesReceived": 29920,110"bytesSent": 46080,111"packetsLost": 0,112"packetsLostFraction": 0,113"packetsReceived": 187,114"packetsSent": 288115}116},117{118"audioInputLevel": 11261,119"audioOutputLevel": 0,120"bytesReceived": 0,121"bytesSent": 8000,122"codecName": "PCMU",123"jitter": 1,124"mos": 4.404255907879351,125"packetsLost": 0,126"packetsLostFraction": 0,127"packetsReceived": 0,128"packetsSent": 50,129"rtt": 61,130"timestamp": 1628271857960.764,131"totals": {132"bytesReceived": 29920,133"bytesSent": 54080,134"packetsLost": 0,135"packetsLostFraction": 0,136"packetsReceived": 187,137"packetsSent": 338138}139},140],141"name": "bytesReceived",142"threshold": {143"name": "min",144"value": 1145}146}147}148],149"selectedIceCandidatePairStats": {150"localCandidate": {151"id": "RTCIceCandidate_XXXXXXXX",152"timestamp": 1628271851961.724,153"type": "local-candidate",154"transportId": "RTCTransport_0_1",155"isRemote": false,156"networkType": "wifi",157"ip": "xx.xx.x.xx",158"address": "xx.xx.x.xx",159"port": 44444,160"protocol": "udp",161"candidateType": "prflx",162"priority": 1853759231163},164"remoteCandidate": {165"id": "RTCIceCandidate_XXXXXXXX",166"timestamp": 1628271851961.724,167"type": "remote-candidate",168"transportId": "RTCTransport_0_1",169"isRemote": true,170"ip": "xx.xxx.xxx.xxx",171"address": "xx.xxx.xxx.xxx",172"port": 11111,173"protocol": "udp",174"candidateType": "host",175"priority": 2130706431176}177},178"isTurnRequired": false,179"callQuality": "excellent"180}
Report Properties
Property: callSid
Description:
The Call SID for the test call.
Property: edge
Description:
The Edge location that the test call was connected to
Property: iceCandidateStats
Description:
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.
Property: networkTiming
Description:
Measurements for establishing DTLS connection. Properties:
dtls: This is measured from RTCDtlsTransport connecting
to connected
state.
ice
: Measurements for establishing ICE connection.
checking
to connected
state. See the documentation for RTCPeerConnection.iceConnectionState for more information.peerConnection
: Measurements for establishing a Peer Connection
signaling
: Measurements for establishing Signaling connection.
device.connect()
up to when RTCPeerConnection.signalingState
transitions to stable
state. See the documentation for RTCPeerConnection.signalingState for more information.Property: samples
Description:
WebRTC samples collected during the test. See the object format on the RTCSample Interface reference page.
Property: selectedEdge
Description:
The edge passed to Device.runPreflight
Property: stats
Description:
RTC-related stats that are extracted from WebRTC samples. This information includes maximum, minimum, and average values calculated for each statistic.
Property: testTiming
Description:
Timing measurement related to the test. Includes millisecond timestamps and duration.
Property: totals
Description:
Calculated totals in RTC statistics samples.
Property: warnings
Description:
PreflightTest.Warnings detected during the test
Property: selectedIceCandidatePairStats
Description:
A WebRTC stats object for the ICE candidate pair used to connect to media, if candidates were selected. Each item is an RTCIceCandidatePairStats object which provides information related to an ICE candidate.
Property: isTurnRequired
Description:
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.
Property: callQuality
Description:
The quality of the call, determined by the MOS (Mean Opinion Score) of the audio stream. Possible values include:
mos
is over 4.2mos
is between 4.1 and 4.2 both inclusivemos
is between 3.7 and 4.0 both inclusivemos
is between 3.1 and 3.6 both inclusivemos
is 3.0 or belowPlease see our example application if you don't want to set up the required TwiML Apps manually.
Twilio.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. Below are example TwiML Apps that you can use and some setup instructions.
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.
Playback TwiML Bin
Create a new TwiML Bin with the plus button on that screen and use "Playback" as the friendly name.
Then use the following TwiML under the TwiML section.
1<?xml version="1.0" encoding="UTF-8"?>23<Response>4<Say>You said:</Say>5<Play loop="1">{{RecordingUrl}}</Play>6<Say>Now waiting for a few seconds to gather audio performance metrics.</Say>7<Pause length="3"/>8<Say>Hanging up now.</Say>9</Response>
Record TwiML Bin
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 "Playback" TwiML Bin's URL that you created previously:
1<?xml version="1.0" encoding="UTF-8"?>23<Response>4<Say>Record a message in 3, 2, 1</Say>5<Record maxLength="5" action="https://my-record-twimlBin-url"></Record>6<Say>Did not detect a message to record</Say>7</Response>
Creating the TwiML Application
Now that we have created our TwiML Bins, let's create our TwiML app by going to the TwiML Apps page.
You can now use this TwiML app to generate your Access Token when calling Device.runPreflight(token, options)
.
If PreflightTest.Options.fakeMicInput
is set to true
, Device.runPreflight(token, options)
API requires an Access Token with a TwiML app that can capture and play audio.
Following the previous steps, create a TwiML Bin using the following TwiML and create a new TwiML Application pointed to that TwiML Bin's URL.
1<?xml version="1.0" encoding="UTF-8"?>23<Response>4<Echo/>5</Response>