WebRTC not adding datachannel in SDP answer - javascript

I can successfully create a datachannel by calling peerConnection.createDataChannel("some/label") and then peerConnection.createOffer(), in which case the datachannel appears in the SDP offer (i.e. there is an m=application there).
But now I want the other peer to create the datachannel, and therefore have it appear in the SDP answer. Same thing: I call createDataChannel before peerConnection.createAnswer(). Except that this time, I don't see an m=application in the SDP answer.
Shouldn't it be possible to create a datachannel from the second peer, and shouldn't it appear in the SDP answer?
My code looks like this:
if (type === 'offer') {
peerConnection.createDataChannel("some/channel");
peerConnection.createAnswer().then(createdDescription).catch(errorHandler);
}
I tried with both Firefox and Google Chrome, and I have the same issue, i.e. I don't see an m=application in the SDP answer.

SDP follows an "offer-answer model" described in RFC 3264. If the offer did not negotiate datachannel, the answer can not contain an additional m-line negotiating it.
The answerer will, after sending their answer, have to send an additional offer with a datachannel m-line.
The negotiationneeded event may fire to indicate this is required.

Related

When RTCPeerConnection.onIcecandidate() event get invoked?

when sdp set to local?
when answer set to remoteDescription?
when any data or streams added to RTCPeerConnection?
They should start firing as soon as you've set a local description, whether it be an offer or an answer.
Think of it as an optimization: partial updates to the localDescription. If you wait a couple of seconds to inspect localDescription then the SDP will contain all ICE candidates already, and you won't need to listen to any events. It works to just send the SDP and ignore these events. But this is slow.
To speed up connection establishment, the initial localDescription provided is incomplete, missing ICE candidates, since they take time to generate. This lets you signal the SDP early — unblocking the other end — provided you promise to follow up and send the missing candidates as they are generated (which is when the event fires).
Yes, onicecandidate event fire as soon as we set offer or answer to localDescription but it will not fire the event till we wouldn't create the datachannel or any streaming channel first.

What about to remove listener once we signal the ice candidate to other peer?

I found the following lines of statements (code) in Pro JavaScript Development while googlin.
// Google chrome often finds multiple candidates, so let's ensure we only
// ever get the first it supplies by removing the event handler once a
// candidate has been found
that.peerConnection.onicecandidate = null;
...
that.peerConnection.addIceCandidate(new IceCandidate(JSON.parse(candidate)))
Is it good practice to avoid icecandidate once we find the candidate?
Nope. Don't do that!
There are different types of ice candidates: host, srflx, prflx, relay.
So, it is not guaranteed that on first negotiation between peers they would be connected. They will try to connect with different routes(don't know exactly what I should say) STUN, TURN servers. With the first attempt of negotiation by using STUN server let's assume both peers are connected successfully. But what if they are not connected? They will try to connect with TURN server.
Thus, if we remove the onicecandidate listener by assigning it to null after we got the ice candidate, then we're not guaranteed to be connected between peers.

WebRTC - RTCDataChannel.readyState is not 'open'

I'm trying to make a text chat, using WebRTC and data channels, but like the title suggests, it isn't working.
And the peer that's being contacted never has ondatachannel being called. Where did I screw up?
Code: https://hastebin.com/satoweyige.php
Do not use {RtpDataChannels: true} and burn the book you found that it. This is non-standard and known to lead to errors.

WebRTC: Unable to successfully complete signalling process using DataChannel

I've been having trouble establishing a WebRTC session and am trying to simplify the issue as much as possible. So I've written up a simple copy & paste example, where you just paste the offer/answer into webforms and click submit.
The HTML+JS, all in one file, can be found here: http://pastebin.com/Ktmb3mVf
I'm on a local network, and am therefore removing the ICE server initialisation process to make this example as bare-bones as possible.
Here are the steps I'm carrying out in the example:
Page 1
Page 1 (loads page), enters a channel name (e.g. test) and clicks create.
A new Host object is created, new PeerConnection() and createDataChannel are called.
createOffer is called, and the resulting offerSDP is pasted into the offer textarea.
Page 2
Copy offerSDP from Page 1 and paste into offer textarea on Page 2, click join.
New Guest object is created, PeerConnection and an ondatachannel handler is set.
setRemoteDescription is called for the Guest object, with the offerSDP data.
createAnswer is called and the result is pasted into the answer textarea box.
Page 1
The answerSDP is copied from Page 2 and pasted into the answer textarea of Page 1, submit answer is clicked.
Host.setRemoteDescription is called with the answerSDP data. This creates a SessionDescription, then peer.setRemoteDescription is called with the resulting data.
Those are the steps carried out in the example, but it seems I'm missing something critical. After the offerer's remoteDescription is set with the answerSDP, I try to send a test message on the dataChannel:
Chrome 40
"-- complete"
> host.dataChannel.send('hello world');
VM1387:2 Uncaught DOMException: Failed to execute 'send' on 'RTCDataChannel': RTCDataChannel.readyState is not 'open'
Firefox 35
"-- complete"
ICE failed, see about:webrtc for more details
> host.dataChannel.send('hello world');
InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
I also had a more complicated demo operating, with a WebSocket signalling server, and ICE candidates listed, but was getting the same error. So I hope this simplification can help to track down the issue.
Again, the single-file code link: http://pastebin.com/Ktmb3mVf
To enable webRTC clients to connect to each other, you need ICE. While STUN and TURN, which you don't need for such a test, are part of that, even without these helpers you still need to use ICE to tell the other end which IP/port/protocol to connect to.
There are two ways to do this: Google's "trickle ice", where the SDP (answer/offer) is passed on without any ICE candidates. These are then transported over a separate signaling layer and added as they are discovered. This speeds up the connection process, as ICE takes time and some late ICE candidates might not be needed.
The classic method is to wait until all ICE candidates have been gathered, and then generate the SDP with these already included.
I have modified your latest version to do that: http://pastebin.com/g2YVvrRd
You also need to wait for the datachannel/connection to become available before being able to use it, so I've moved the sending of the message to the channels onopen event.
The significant changes to the original code:
The interface callbacks were removed from Host.prototype.createOffer and Guest.prototype.createAnswer, instead we attach the provided callback function to the respective objects for later use.
self.cb = cb;
Both Host and Guest have an added ICE handler for the PeerConnection:
var self = this;
this.peer.onicecandidate = function (event) {
// This event is called for every discovered ICE candidate.
// If this was trickle ICE, you'd pass them on here.
// An event without an actual candidate signals the end of the
// ICE collection process, which is what we need for classic ICE.
if (!event.candidate) {
// We fetch the up to date description from the PeerConnection
// It now contains lines with the available ICE candidates
self.offer = self.peer.localDescription;
// Now we move on to the deferred callback function
self.cb(self.offer);
}
}
For the guest self.offer becomes self.answer
The interface handler $("#submitAnswer").click() does not send the message anymore, instead it is send when the datachannel is ready in the onopen event defined in setChannelEvents().
channel.onopen = function () {
console.log('** channel.onopen');
channel.send('hello world!');
};

RTCSessionDescription and video/audio broadcast

Once I have exchanged session description between two peers. How can I allow the user to prevent audio and/or video broadcast? Do I need to exchange the Session Descriptions again?
"Broadcast" is probably not the correct term since PeerConnections are always unicast peer-to-peer.
To acquire an audio/video stream from the user's devices you call getUserMedia() and to send these to the other peer you call addStream() on the PeerConnection object.
So to allow the user to not send the acquired stream just let her choose whether to call addStream() or not. E.g. show a popup saying "Send Audio/Video to the other user?". If she chooses "Yes" call addStream() on the PeerConnection object, otherwise just don't call it.
EDIT to answer question in comment:
If you'd like to stop sending of audio and/or video just call removeStream() on the PeerConnection object with the stream to remove as parameter. This will per the API spec trigger a renegotiation.
See http://dev.w3.org/2011/webrtc/editor/webrtc.html#interface-definition for further details.

Categories