6

I am trying to implement a Voice-only WebRTC app. I am running it on Chrome Version 29.0.1547.0 dev. My app uses Socket.IO for the signaling mechanism.

peerConnection.addIceCandidate() is giving me this error: Uncaught SyntaxError: An invalid or illegal string was specified.

and separately, peerConnection.setRemoteDescription(); is giving me this error: Uncaught TypeMismatchError: The type of an object was incompatible with the expected type of the parameter associated to the object.

Here's my code:

SERVER (in CoffeeScript)

app = require("express")()
server = require("http").createServer(app).listen(3000)
io = require("socket.io").listen(server)

app.get "/", (req, res) -> res.sendfile("index.html")
app.get "/client.js", (req, res) -> res.sendfile("client.js")

io.sockets.on "connection", (socket) ->
    socket.on "message", (data) ->
        socket.broadcast.emit "message", data

CLIENT (in JavaScript)

var socket = io.connect("http://localhost:3000");

var pc = new webkitRTCPeerConnection({
    "iceServers": [{"url": "stun:stun.l.google.com:19302"}]
});


navigator.getUserMedia = navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia;

navigator.getUserMedia({audio: true}, function (stream) {
    pc.addStream(stream);
}, function (error) { console.log(error); });


pc.onicecandidate = function (event) {
    if (!event || !event.candidate) return;
    socket.emit("message", {
        type: "iceCandidate",
        "candidate": event.candidate
    });
};


pc.onaddstream = function(event) {
    var audioElem = document.createElement("audio");
    audioElem.src = webkitURL.createObjectURL(event.stream);
    audioElem.autoplay = true;
    document.appendChild(audioElem);
    console.log("Got Remote Stream");
};


socket.on("message", function(data) {
    if (data.type === "iceCandidate") {
        console.log(data.candidate);

        candidate = new RTCIceCandidate(data.candidate);
        console.log(candidate);

        pc.addIceCandidate(candidate);

    } else if (data.type === "offer") {
        pc.setRemoteDescription(data.description);
        pc.createAnswer(function(description) {
            pc.setLocalDescription(description);
            socket.emit("message", {type: "answer", description: description});
        });
    } else if (data.type === "answer") {
        pc.setRemoteDescription(data.description);
    }
});


function offer() {
    pc.createOffer( function (description) {
        pc.setLocalDescription(description);
        socket.emit("message", {type: "offer", "description": description});
    });
};

The HTML just contains a button that calls offer().

I can confirm the ICECandidates and SessionDescriptions are transferring successfully from one client to the other.

What am I doing wrong? And how should I fix these and any other errors so that I can transfer audio from one client to the other?

PS: If you know about a good source documenting the WebRTC API (except the W3C documentation), please tell me about it!

Thanks!

Arjun Bajaj
  • 1,932
  • 7
  • 24
  • 41
  • Can you paste in an example of the data.candidate that you're passing to the RTCIceCandidate constructor? – tomtheengineer Jun 27 '13 at 20:22
  • 1
    I cannot give an answer to your specific question, but i found the [WebRTC-Series of html5rocks](http://www.html5rocks.com/en/tutorials/webrtc/basics/) a pretty good resource on that topic. – Raoul Jun 27 '13 at 21:50
  • I couldn t solve this even by caching the remote ice candidates until remote description is set. RTCIceCandidate {sdpMLineIndex: 1, sdpMid: "", candidate: "a=candidate:924013166 1 tcp 1509957375 192.168.57.1 0 typ host generation 0 ↵"} app.js:13895 Uncaught SyntaxError: An invalid or illegal string was specified. – Samson Nov 12 '13 at 23:24

1 Answers1

17

For that error the point is, ICE Candidates must be added only after successfully setting remote description.

Note that just after creating Offer (by Offerer), ice candidates are produced immediately. So, if somehow the answerer receives those candidates, before setting remote description (which in theory would arrive before candidates), you get error.

The same is true for offerer. It must set remote description before adding any ice candidate.

I see that in your javascript code you are not guaranteeing that remote description is set before adding ice candidates.

First of all you can check just before pc.addIceCandidate(candidate); if pc's remoteDescription is set. If you see that it is null (or undefined), you can locally store received ice candidates to add them after setting remoteDescription (or wait in offerer to send them in proper time.)

Mert Mertce
  • 1,614
  • 3
  • 21
  • 32
  • You wrote: `which in theory would arrive antes candidates`. I assume `antes` is a typo? What did you mean to say? – Gili Oct 31 '13 at 04:32
  • By mistake I have written spanish word. antes = before. I have corrected. – Mert Mertce Dec 24 '13 at 09:45
  • So, let me know if I'm doing this right. `onicecandidate` is called by the offering peer, right after the `RTCPeerConnection` is constructed. Then the answering peer runs `addIceCandidate` with the offering peer's candidate? – Costa Michailidis Jan 06 '15 at 22:43
  • For future readers that may be encountering a similar problem, basically the answerer needs to use `pc.onicecandidate = function(event) { /* Get event.candidate and give it to the offerer */ }`. Do this after creating the stream but before creating the answer. Then for the offerer, use `pc.addIceCandidate` with each of the ICE candidates obtained from the answerer (you'll need some way to transfer this data between the offerer and answerer, but should already have this by now). – Kat May 07 '15 at 16:57