- From: Bernard Aboba <Bernard.Aboba@microsoft.com>
- Date: Wed, 13 Aug 2014 23:00:02 +0000
- To: "public-ortc@w3.org" <public-ortc@w3.org>
- Message-ID: <43572b44205b43489116b292e528ef66@SN2PR03MB031.namprd03.prod.outlook.com>
Peter Thatcher has suggested a simplification of the ICE restart proposal. Rather than having a gather() method within the RTCIceListener, the ICE Listener can be constructed with a gather policy. Also, rather than having ICE gathering state in either the RTCIceListener or the RTCIceTransport, gathering begins when an RTCIceListener is constructed, and fires an "icegatheringcomplete" event when ICE gathering is completed. Assuming that the onlocalcandidate Event Handler and the getLocalCandidates() method remain on the RTCIceTransport (it may be possible to only have these on the RTCIceListener), then the RTCIceTransport and RTCIceListener interfaces would look like this: [Constructor(RTCIceListener iceListener), Constructor(RTCIceOptions options)] interface RTCIceTransport : RTCStatsProvider { readonly attribute RTCIceListener iceListener; readonly attribute RTCIceRole role; readonly attribute RTCIceComponent component; readonly attribute RTCIceTransportState state; sequence<RTCIceCandidate> getLocalCandidates (); sequence<RTCIceCandidate> getRemoteCandidates (); void start (RTCIceParameters remoteParameters, optional RTCIceRole role, optional RTCIceListener listener); void stop (); RTCIceParameters? getRemoteParameters (); RTCIceTransport createAssociatedTransport (); void addRemoteCandidate (RTCIceCandidate remoteCandidate); void setRemoteCandidates (sequence<RTCIceCandidate> remoteCandidates); attribute EventHandler? onlocalcandidate; attribute EventHandler? onicestatechange; }; [Constructor(RTCIceGatherOptions options)] interface RTCIceListener { readonly attribute boolean complete; RTCIceParameters getLocalParameters (); sequence<RTCIceCandidate> getLocalCandidates (); attribute EventHandler? onerror; attribute EventHandler? onlocalcandidate; attribute EventHandler? oncomplete; }; dictionary RTCIceOptions { sequence<RTCIceServer> iceServers; }; dictionary RTCIceGatherOptions { RTCIceGatherPolicy gatherPolicy; sequence<RTCIceServer> iceservers; }; [Constructor(DOMString type)] interface RTCIceGatheringCompleteEvent : Event { }; NOTE: If the onlocalcandidate Event Handler and the getLocalCandidates() method are moved to the RTCIceListener, the RTCIceListener would then have all the ICE candidate gathering functionality, so that it may make sense to rename it the "RTCIceGatherer". The above changes don't appear to have much impact on the sample code. Some rewritten examples appear below. EXAMPLE 3 // Assume we already have a way to signal. This is an example // of how to offer ICE and DTLS parameters and ICE candidates and // get back ICE and DTLS parameters and ICE candidates, and start // both ICE and DTLS, assuming that RTP and RTCP are multiplexed. function initiate(mySignaller) { var iceOptions = ...; var ice = new RTCIceTransport(iceOptions); var dtls = new RTCDtlsTransport(ice); // ... get tracks and RTP objects from other example mySignaller.mySendInitiate({ "ice": ice.iceListener.getLocalParameters(), "dtls": dtls.getLocalParameters(), // ... include RTP info from other example }, function(remote) { // Start the ICE transport with an implicit gather policy of "all" ice.start(remote.ice, RTCIceRole.controlling); dtls.start(remote.dtls); // ... start RTP senders and receivers from other example }); ice.onlocalcandidate = function(candidate) { mySignaller.mySendLocalCandidate(candidate); } mySignaller.onRemoteCandidate = function(candidate) { ice.addRemoteCandidate(candidate); } } EXAMPLE 4 // Assume we already have a way to signal and remote info is // signalled to us. This is an example of how to answer with ICE and DTLS // and DTLS parameters and ICE candidates and start both ICE and DTLS, // assuming that RTP and RTCP are multiplexed. // function accept(mySignaller, remote) { var iceOptions = ...; var ice = new RTCIceTransport(iceOptions); var dtls = new RTCDtlsTransport(ice); // ... get tracks and RTP objects from other example ice.onlocalcandidate = function(candidate) { mySignaller.mySendLocalCandidate(candidate); } mySignaller.onRemoteCandidate = function(candidate) { ice.addRemoteCandidate(candidate); } mySignaller.mySendAccept({ "ice": ice.iceListener.getLocalParameters(), "dtls": dtls.getLocalParameters() // ... include RTP info from other example }); // Start the ICE transport with an implicit gather policy of "all" ice.start(remote.ice, RTCIceRole.controlled); dtls.start(remote.dtls); // ... start RTP senders and receivers from other example } EXAMPLE 5 // This is an example of how to utilize distinct ICE transports for Audio and Video // As well as for RTP and RTCP. If both sides can multiplex audio/video // and/or RTP/RTCP then the multiplexing will occur. // // Assume we have an audioTrack and a videoTrack to send. // //create the RTP and RTCP ICE transports for audio and video var audioRtpIceTransport = new RTCIceTransport(...); var audioRtcpIceTransport = audioRtpIceTransport.createAssociatedTransport(); var videoRtpIceTransport = new RTCIceTransport(...); var videoRtcpIceTransport = audioRtpIceTransport.createAssociatedTransport(); // // Prepare the audio and video ICE transports audioRtpIceTransport.onlocalcandidate = function (event) {sendLocalCandidate(audioRtpIceTransport, event.candidate,"audio")}; audioRtcpIceTransport.onlocalcandidate = function (event) {sendLocalCandidate(audioRtcpIceTransport, event.candidate,"audio")}; videoRtpIceTransport.onlocalcandidate = function (event) {sendLocalCandidate(videoRtpIceTransport, event.candidate,"video")}; videoRtcpIceTransport.onlocalcandidate = function (event) {sendLocalCandidate(videoRtcpIceTransport, event.candidate,"video")}; audioRtpIceTransport.onicestatechange = ... ; audioRtpIceTransport.iceListener.oncomplete = ... ; audioRtpIceTransport.iceListener.onerror = errorHandler; audioRtcpIceTransport.onicestatechange = ... ; audioRtcpIceTransport.iceListener.oncomplete = ... ; audioRtcpIceTransport.iceListener.onerror = errorHandler; videoRtpIceTransport.onicestatechange = ... ; videoRtpIceTransport.iceListener.oncomplete = ... ; videoRtpIceTransport.iceListener.onerror = errorHandler; videoRctpIceTransport.onicestatechange = ... ; videoRctpIceTransport.iceListener.oncomplete = ... ; videoRctpIceTransport.iceListener.onerror = errorHandler; //Prepare the remote candidate handler mySignaller.onRemoteCandidate = function(remote) { switch (remote.kind) { case "audio": if (remote.component === RTCIceComponent.RTP){ audioRtpIceTransport.addRemoteCandidate(remote.candidate); } else { audioRtcpIceTransport.addRemoteCandidate(remote.candidate); }; break; case "video": if (remote.component === RTCIceComponent.RTP){ videoRtpIceTransport.addRemoteCandidate(remote.candidate); } else { videoRtcpIceTransport.addRemoteCandidate(remote.candidate); }; break; default: log('Invalid media type received'); }; } // Create the DTLS transports var audioRtpDtlsTransport = new RTCDtlsTransport(audioRtpIceTransport); var audioRtcpDtlsTransport = new RTCDtlsTransport(audioRtcpIceTransport); var videoRtpDtlsTransport = new RTCDtlsTransport(videoRtpIceTransport); var videoRtcpDtlsTransport = new RTCDtlsTransport(videoRtcpIceTransport); // // Create the sender and receiver objects var audioSender = new RtpSender(audioTrack, audioRtpDtlsTransport, audioRtcpDtlsTransport); var videoSender = new RtpSender(videoTrack, videoRtpDtlsTransport, videoRtcpDtlsTransport); var audioReceiver = new RtpReceiver(audioRtpDtlsTransport, audioRtcpDtlsTransport); var videoReceiver = new RtpReceiver(videoRtpDtlsTransport, videoRtcpDtlsTransport); // // Retrieve the receiver and sender capabilities var recvAudioCaps = RTCRtpReceiver.getCapabilities("audio"); var recvVideoCaps = RTCRtpReceiver.getCapabilities("video"); var sendAudioCaps = RTCRtpSender.getCapabilities("audio"); var recvVideoCaps = RTCRtpSender.getCapabilities("video"); // // At this point, ICE/DTLS parameters and Send/Receive capabilities can be exchanged. mySignaller.myOfferTracks({ // Indicate that the initiator would prefer to multiplex both A/V and RTP/RTCP "bundle": true, // Indicate that the initiator is willing to multiplex RTP/RTCP without A/V mux "rtcpMux": true, // Offer the ICE parameters "audioRtpIce": audioRtpIceTransport.iceListener.getLocalParameters(), "audioRtcpIce": audioRtcpIceTransport.iceListener.getLocalParameters(), "videoRtpIce": videoRtpIceTransport.iceListener.getLocalParameters(), "videoRtcpIce": videoRtcpIceTransport.iceListener.getLocalParameters(), // Offer the DTLS parameters "audioRtpDtls": audioRtpDtlsTransport.getLocalParameters(), "audioRtcpDtls": audioRtcpDtlsTransport.getLocalParameters(), "videoRtpDtls": videoRtpDtlsTransport.getLocalParameters(), "videoRtcpDtls": videoRtcpDtlsTransport.getLocalParameters(), // Offer the receiver and sender audio and video capabilities. "recvAudioCaps": recvAudioCaps, "recvVideoCaps": recvVideoCaps, "sendAudioCaps": sendAudioCaps, "sendVideoCaps": sendVideoCaps }, function(answer) { // The responder answers with its preferences, parameters and capabilities // // Derive the send and receive parameters, assuming that RTP/RTCP mux will be enabled. var audioSendParams = myCapsToSendParams(sendAudioCaps, answer.recvAudioCaps); var videoSendParams = myCapsToSendParams(sendVideoCaps, answer.recvVideoCaps); var audioRecvParams = myCapsToRecvParams(recvAudioCaps, answer.sendAudioCaps); var videoRecvParams = myCapsToRecvParams(recvVideoCaps, answer.sendVideoCaps); // // If the responder wishes to enable bundle, we will enable it if (answer.bundle) { // Only start the single ICE and DTLS transport that is needed. // No need for the ICE Transport Controller. audioRtpIceTransport.start(answer.audioRtpIce, RTCIceRole.controlling); audioRtpDtlsTransport.start(remote.audioRtpDtls); // // Replace the transport on the Sender and Receiver objects // audioSender.setTransport(audioRtpDtlsTransport); videoSender.setTransport(audioRtpDtlsTransport); audioReceiver.setTransport(audioRtpDtlsTransport); videoReceiver.setTransport(audioRtpDtlsTransport); // If BUNDLE was requested, then also assume RTP/RTCP mux answer.rtcpMux = true; } else { if (answer.rtcpMux){ // The peer doesn't want BUNDLE, but it does want to multiplex RTP/RTCP // Create the ICE Transport Controller object var controller = new RTCIceTransportController(); controller.addTransport(audioRtpIceTransport); controller.addTransport(videoRtpIceTransport); // Start the audio and video ICE transports, with an implicit gather policy of "all" audioRtpIceTransport.start(answer.audioRtpIce, RTCIceRole.controlling); videoRtpIceTransport.start(answer.videoRtpIce, RTCIceRole.controlling); // Start the audio and video DTLS transports audioRtpDtlsTransport.onerror = errorHandler; audioRtpDtlsTransport.start(answer.audioRtpDtls); videoRtpDtlsTransport.onerror = errorHandler; videoRtpDtlsTransport.start(answer.videoRtpDtls); // Replace the transport on the Sender and Receiver objects // audioSender.setTransport(audioRtpDtlsTransport); videoSender.setTransport(videoRtpDtlsTransport); audioReceiver.setTransport(audioRtpDtlsTransport); videoReceiver.setTransport(videoRtpDtlsTransport); }; // Check if the responder does not want BUNDLE // and does not want RTP/RTCP multiplexing if (!answer.rtcpMux) { // Create the ICE Transport Controller object var controller = new RTCIceTransportController(); controller.addTransport(audioRtpIceTransport); controller.addTransport(videoRtpIceTransport); // Prepare the ICE transports // Start the ICE transports, with an implicit gather policy of "all" audioRtpIceTransport.start(answer.audioRtpIce, RTCIceRole.controlling); audioRtcpIceTransport.start(answer.audioRtcpIce, RTCIceRole.controlling); videoRtpIceTransport.start(answer.videoRtpIce, RTCIceRole.controlling); videoRtcpIceTransport.start(answer.videoRtcpIce, RTCIceRole.controlling); // Start the DTLS transports that are needed audioRtpDtlsTransport.start(answer.audioRtpDtls); audioRtcpDtlsTransport.start(answer.audioRtcpDtls); videoRtpDtlsTransport.start(answer.videoRtpDtls); videoRtcpDtlsTransport.start(answer.videoRtcpDtls); // Disable RTP/RTCP multiplexing audioSendParams.rtcp.mux = false; videoSendParams.rtcp.mux = false; audioRecvParams.rtcp.mux = false; videoRecvParams.rtcp.mux = false; }; // // Set the audio and video send and receive parameters. audioSender.send(audioSendParams); videoSender.send(videoSendParams); audioReceiver.receive(audioRecvParams); videoReceiver.receive(videoRecvParams); }); // Now we can render/play // audioReceiver.track and videoReceiver.track. // Helper functions function log(text) { console.log('Time: ' + (performance.now() / 1000).toFixed(2) + ' ' + text); } function errorHandler (error) { log('Error encountered: ' + error.name); } function sendLocalCandidate(transport, candidate, kind){ mySignaller.mySendLocalCandidate({ "candidate": candidate, "kind": kind, "component": transport.component }); } EXAMPLE 6 // Example to demonstrate forking when RTP and RTCP are not multiplexed. var iceGatherOptions = new RTCIceGatherOptions(); iceGatherOptions.gatherPolicy = RTCIceGatherPolicy.relay; iceGatherOptions.iceservers = ... ; var iceRtpListener = new RTCIceListener(iceGatherOptions); var iceBaseRtpTransport = new RTCIceTransport(iceRtpListener); //create the RTCP ICE transport, which inherits the iceGatherOptions var iceBaseRtcpTransport = iceBaseRtpTransport.createAssociatedTransport(); mySendInitiate( { "icertp": iceBaseRtpTransport.iceListener.getLocalParameters(), "icertcp": iceBaseRtcpTransport.iceListener.getLocalParameters() }, function(response) { // We may get N responses var iceRtpTransport = new RTCIceTransport(iceRtpListener); // Create new ice RTCP transport based on the (implicitly created) iceListener var iceRtcpTransport = iceRtpTransport.createAssociatedTransport(); // check to make sure the RTCRtpIceListener objects are set up as expected. assert(iceRtpTransport.iceListener == iceBaseRtpTransport.iceListener); assert(iceRtcpTransport.iceListener == iceBaseRtcpTransport.iceListener); // Create the ICE Transport Controller object var controller = new RTCIceTransportController(); controller.addTransport(iceRtpTransport); controller.addTransport(iceRtcpTransport); // Start the ICE transports (using the "relay" gather policy inherited from the iceListener) iceRtpTransport.start(response.icertp, RTCIceRole.controlling); iceRtcpTransport.start(response.icertcp, RTCIceRole.controlling); // ... setup DTLS, RTP, SCTP, etc. }); iceBaseRtpTransport.onlocalcandidate = mySendLocalRtpCandidate; iceBaseRtcpTransport.onlocalcandidate = mySendLocalRtcpCandidate;
Received on Wednesday, 13 August 2014 23:00:43 UTC