You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
With the Application having multiple candidate pairs at its disposal and an ability to switch the transport between them, it needs a way to gather up-to-date information about the available candidate pairs. With this information, the Application can decide the best candidate pair to use for transport at any given time.
Information about candidate pair can be queried through getStats, which contains, among other things, the current and total RTT, and the number of ICE checks sent, received, and responded.
But ICE checks are sent only over the candidate pair actively used for transport at a frequent interval (around once every couple of seconds). ICE checks are sent less frequently over any inactive candidate pairs (once every ~15 seconds). So information about an inactive, alternate candidate pair could be very stale, or indeed the pair may no longer be connected, when the Application wants to switch the transport to it.
On the other hand, the Application may want to avoid sending ICE checks on an inactive candidate pair, eg. on a reliable or a power-sensitive interface.
Proposal
Introduce a new API to allow applications to (at a minimum):
know when the ICE agent is about to send an ICE check, and possibly prevent it
know when an ICE check concludes with either a response or a timeout
send an ICE check on an available candidate pair
There are a couple of options for the shape of such an API. With both options, an ICE check begins with either
an ICE check event when the check is initiated by the ICE Agent
a call to a new checkCandidatePair() method on RTCIceTransport by the App
Option 1: Linked Promises
This option allows the App to follow the course of an ICE check at each step of the way - from initiation to being sent to the conclusion.
Also, information about each step becomes available as soon as the step concludes, eg. the sentTime is known when the ICE check is sent while the responseTime is known if and when the response is received.
partial interfaceRTCIceTransport {
// Send an ICE check. Resolves when the check is actually sent.
Promise<RTCIceCheckRequest> checkCandidatePair(RTCIceCandidatePair pair);
// Fired before ICE agent sends an ICE check.// Cancellable, unless triggered check or nomination or app initiated.
attribute EventHandler /* RTCIceCheckEvent */ onicecandidatepaircheck;
}
interfaceRTCIceCheckEvent : Event { // Cancellable
readonly attribute RTCIceCandidatePair candidatePair;
// Resolves when the check is actually sent. Rejected => send failure.
readonly attribute Promise<RTCIceCheckRequest> request;
}
interfaceRTCIceCheckRequest {
readonly attribute ArrayBuffer transactionId;
readonly attribute DOMHighResTimeStamp sentTime;
// Resolves when response is received. Rejected => timeout.
readonly attribute Promise<RTCIceCheckResponse> response;
}
interfaceRTCIceCheckResponse {
readonly attribute DOMHighResTimeStamp receivedTime;
// No error => success.
readonly attribute RTCIceCheckResponseError? error;
}
Option 2: Flat events
This option only conveys the beginning and conclusion of an ICE check to the App.
All information gleaned from the ICE check is available together at the conclusion of the check.
The main drawback of this option is that the App doesn't know exactly when an ICE check has been sent in the case of an ICE agent-initiated check.
partial interfaceRTCIceTransport {
// Send an ICE check. Resolves when the check is actually sent.
Promise<undefined> checkCandidatePair(RTCIceCandidatePair pair);
// Fired before ICE agent sends an ICE check.// Cancellable, unless triggered check or nomination or app initiated.
attribute EventHandler /* RTCIceCandidatePairEvent */ onicecandidatepaircheck;
// Fired when an ICE check concludes.
attribute EventHandler /* RTCIceCheckEvent */ onicecandidatepaircheckcomplete;
}
interfaceRTCIceCheckEvent : Event {
readonly attribute RTCIceCandidatePair candidatePair;
readonly attribute ArrayBuffer transactionId;
readonly attribute DOMHighResTimeStamp sentTime;
// No receivedTime => timeout.
readonly attribute DOMHighResTimeStamp? receivedTime;
// No error => success.
readonly attribute RTCIceCheckResponseError? error;
}
ICE agent interactions
Both options allow an App to modify ICE check behaviour in the same way. Here is a proposal for how these modifications are handled by the ICE agent:
Prevent an ICE check
At the beginning of an ICE session, or after an ICE restart, the ICE agent goes through the process of forming checklists of candidate pairs and then performing connectivity checks (RFC 8445 section 6.1.4.2). While a data session is in progress, the ICE agent continues to send ICE checks on the active and any other candidate pairs as keeplives (RFC 8445 section 11).
If an App prevents a scheduled ICE check from being sent, the ICE agent takes no further action at the current expiration of the schedule timer, and defers until the timer expires again.
It is possible that at the next timer expiration, the same candidate pair is picked to send a check over. This is expected, and it is up to the App to ensure that either
the ICE agent is eventually able to send ICE checks over all available pairs, or
the App itself sends ICE checks over any pairs that are in need of keepalives, or
the App accepts that pairs may become stale or disconnected from a lack of keepalives.
The API also does not permit the App from preventing a response being sent for a received ICE check, or indeed knowing when a response is being sent at all.
Send an ICE check
When the App requests an ICE check to be sent, the ICE agent follows the same process as performing an agent-initiated check, i.e. send a binding request over the candidate pair (RFC 8445 section 7.2.4) and change the candidate pair state to In-Progress.
If the requested candidate pair is already in In-Progress state, the method fails with a rejected promise.
If the interval since the previous check - over any candidate pair and whether initiated by the ICE agent or the App - is less than the Ta timer value (50ms, RFC 8445 appendix B.1), the method fails with a rejected promise. Here, Option 1 gives the App a clear indication of when the previous check was sent (i.e. when a Promise<RTCIceCheckRequest> resolved) while Option 2 only indicates this for App-initiated checks. With Option 2, this leaves a small margin for an unfortunate timing conflict when the method may fail without the App at fault.
User agents could implement a stronger safeguard by throwing an error synchronously if the method is called repeatedly within a window longer than the Ta timer value.
A typical App can let through all ICE checks in the beginning of a session until some viable candidate pairs are discovered, and then engage the prevent and send mechanisms to finely control ICE checks over the remained of the session.
Example usage
Option 1: Linked Promises
constpc=…;constice=pc.getTransceivers()[0].sender.transport.iceTransport;ice.onicecandidatepaircheck=async(event)=>{if(shouldNotCheck(event.candidatePair)){event.preventDefault();// prevent a checkreturn;}constrequest=awaitevent.request;handleCheck(request);}constrequest=awaitice.checkCandidatePair(alternatePair);// send a checkhandleCheck(request);functionhandleCheck(request){try{constresponse=awaitrequest.response;constrtt=response.receivedTime-request.sentTime;// … do something with rtt …if(response.error){// … do something with error …}}catch(error){// … do something with timeout …}}
Option 2: Flat events
constpc=…;constice=pc.getTransceivers()[0].sender.transport.iceTransport;ice.onicecandidatepaircheck=(event)=>{if(shouldNotCheck(event.candidatePair)){event.preventDefault();// prevent a check}}ice.onicecandidatepaircheckcomplete=handleCheck;ice.checkCandidatePair(alternatePair);// send a checkfunctionhandleCheck(event){if(event.receivedTime){constrtt=event.receivedTime-event.sentTime;// … do something with rtt …}else{// … do something with timeout …}if(event.error){// … do something with error …}}
Background
Following the incremental approach for improving ICE control capabilities, mechanisms are now defined to:
Problem:
With the Application having multiple candidate pairs at its disposal and an ability to switch the transport between them, it needs a way to gather up-to-date information about the available candidate pairs. With this information, the Application can decide the best candidate pair to use for transport at any given time.
Information about candidate pair can be queried through getStats, which contains, among other things, the current and total RTT, and the number of ICE checks sent, received, and responded.
But ICE checks are sent only over the candidate pair actively used for transport at a frequent interval (around once every couple of seconds). ICE checks are sent less frequently over any inactive candidate pairs (once every ~15 seconds). So information about an inactive, alternate candidate pair could be very stale, or indeed the pair may no longer be connected, when the Application wants to switch the transport to it.
On the other hand, the Application may want to avoid sending ICE checks on an inactive candidate pair, eg. on a reliable or a power-sensitive interface.
Proposal
Introduce a new API to allow applications to (at a minimum):
There are a couple of options for the shape of such an API. With both options, an ICE check begins with either
Option 1: Linked Promises
This option allows the App to follow the course of an ICE check at each step of the way - from initiation to being sent to the conclusion.
Also, information about each step becomes available as soon as the step concludes, eg. the sentTime is known when the ICE check is sent while the responseTime is known if and when the response is received.
Option 2: Flat events
This option only conveys the beginning and conclusion of an ICE check to the App.
All information gleaned from the ICE check is available together at the conclusion of the check.
The main drawback of this option is that the App doesn't know exactly when an ICE check has been sent in the case of an ICE agent-initiated check.
ICE agent interactions
Both options allow an App to modify ICE check behaviour in the same way. Here is a proposal for how these modifications are handled by the ICE agent:
Prevent an ICE check
At the beginning of an ICE session, or after an ICE restart, the ICE agent goes through the process of forming checklists of candidate pairs and then performing connectivity checks (RFC 8445 section 6.1.4.2). While a data session is in progress, the ICE agent continues to send ICE checks on the active and any other candidate pairs as keeplives (RFC 8445 section 11).
If an App prevents a scheduled ICE check from being sent, the ICE agent takes no further action at the current expiration of the schedule timer, and defers until the timer expires again.
It is possible that at the next timer expiration, the same candidate pair is picked to send a check over. This is expected, and it is up to the App to ensure that either
The App is not permitted to prevent the sending of triggered checks (RFC 8445 section 6.1.4.1), and as a corollary, nominations (RFC 8445 section 8.1.1).
The API also does not permit the App from preventing a response being sent for a received ICE check, or indeed knowing when a response is being sent at all.
Send an ICE check
When the App requests an ICE check to be sent, the ICE agent follows the same process as performing an agent-initiated check, i.e. send a binding request over the candidate pair (RFC 8445 section 7.2.4) and change the candidate pair state to In-Progress.
If the requested candidate pair is already in In-Progress state, the method fails with a rejected promise.
If the interval since the previous check - over any candidate pair and whether initiated by the ICE agent or the App - is less than the Ta timer value (50ms, RFC 8445 appendix B.1), the method fails with a rejected promise. Here, Option 1 gives the App a clear indication of when the previous check was sent (i.e. when a
Promise<RTCIceCheckRequest>
resolved) while Option 2 only indicates this for App-initiated checks. With Option 2, this leaves a small margin for an unfortunate timing conflict when the method may fail without the App at fault.User agents could implement a stronger safeguard by throwing an error synchronously if the method is called repeatedly within a window longer than the Ta timer value.
A typical App can let through all ICE checks in the beginning of a session until some viable candidate pairs are discovered, and then engage the prevent and send mechanisms to finely control ICE checks over the remained of the session.
Example usage
Option 1: Linked Promises
Option 2: Flat events
cc: @pthatcherg
The text was updated successfully, but these errors were encountered: