2

I set up a basic video chat app using the WebRTC APIs in Chrome along with a WebSocket script I wrote myself following the W3C specs and other questions here on SO.

Sometimes though, when one PC sends ICE candidate info to the other PC via the WebSocket connection, a bunch of garbled text is attached to the end of the JSON-stringified candidate info. This problem only happens sometimes though, and it never happens with the SDP info sent via the createOffer and createAnswer methods.

Please see the following link for an example of what I'm talking about: http://s1290.beta.photobucket.com/user/HartleySan83/media/NGdata_zps0a7203e7.png.html?sort=3&o=0

Because the JSON-stringified candidate info always ends with '}}', by adding an if condition to the WebSocket server script, I was able to circumvent this problem and get the video chat app to work. Unfortunately, this is a hack that I'd like to avoid. Plus, I'd like to know why this is happening in the first place.

It's worth noting that when I either alert or echo the candidate info to the console on the client side before it's sent to the WebSocket server script, none of the extra garbled text is present, so I'm not sure why it's present with the candidate info on the server side and only sometimes.

The following is a code snippet of the client-side code where the candidate info is sent to the server-side script:

function startPeerConnection() {

  navigator.webkitGetUserMedia({ audio: true, video: true }, function (stream) {

    document.getElementById('vid1').src = webkitURL.createObjectURL(stream);

    pc = new webkitRTCPeerConnection(null);

    pc.onicecandidate = function (evt) {

      if (evt.candidate) {

        socket.send(JSON.stringify({ candidate: evt.candidate }));

      }

    };

    pc.onaddstream = function (evt) {

      document.getElementById('vid2').src = webkitURL.createObjectURL(evt.stream);

    };

    pc.addStream(stream);

  }, function () {});

}

And the following is the server-side code that unmasks the received WebSocket data:

$len = ord($buffer[1]) & 127;

if ($len === 126) {

  $masks_start = 4;

} else if ($len === 127) {

  $masks_start = 10;

} else {

  $masks_start = 2;

}

$masks = substr($buffer, $masks_start, 4);

$data = substr($buffer, $masks_start + 4);

$len = strlen($data);

$text = '';

for ($i = 0; $i < $len; $i++) {

  $text .= $data[$i] ^ $masks[$i % 4];

}

if (($end = strpos($text, '}}')) !== false) {
// This if condition eliminates the garbled text.
// Without it, a "Could not decode a text frame as UTF-8"
// error is output to the Chrome console.

  $text = substr($text, 0, $end + 2);

  $len = strlen($text);

}

if ($len <= 125) {

  $header = pack('C*', 129, $len);

} else if (($len > 125) && ($len < 65536)) {

  $header = pack('C*', 129, 126, ($len >> 8) & 255, $len & 255);

} else if ($len >= 65536) {

  $header = pack('C*', 129, 127, ($len >> 56) & 255, ($len >> 48) & 255, ($len >> 40) & 255, ($len >> 32) & 255, ($len >> 24) & 255, ($len >> 16) & 255, ($len >> 8) & 255, $len & 255);

}

$server_response = $header . $text;

foreach ($users as $user) {

  if ($user !== $users[$user_idx]) {

    @socket_write($user['socket'], $server_response, strlen($server_response));

  }

}

I've searched high and low on the Internet for anyone else with the same problem, but I can't find anyone or anything in the specs that talks about this, so I imagine it's some problem with my code.

Any guidance that anyone can offer as to the source of the problem would be much appreciated. Thank you.

HartleySan
  • 7,404
  • 14
  • 66
  • 119
  • looks like some binary data. anyway, that information is needed to tell remote peer how to establish connection with local ones or vise versa. – Yury Solovyov Jan 20 '13 at 17:06
  • I don't think that binary data is needed though, because when I simply cut off everything after the '}}' characters (i.e., when I get rid of all the binary data), then everything goes through fine and the remote video instantly pops up on both ends and works fine. I'll keep looking into the issue. – HartleySan Jan 21 '13 at 13:02

2 Answers2

3

Well, I finally found the problem. My server-side WebSocket code was indeed wrong. The problem was that I was miscalculating the length. I, unfortunately, was relying on some page I found about WebSockets in PHP, and as it turns out, the page had a number of errors in its code, which I slowly started to realize more and more. Anyway, here's the proper way to calculate the length of messages sent from a client to the server:

$len = ord($buffer[1]) & 127; // This is the default payload length.

if ($len === 126) { // If 126, then need to use the payload length at the 3rd and 4th bytes.

  $masks_start = 4;

  $len = (ord($buffer[2]) << 8) + ord($buffer[3]);

} else if ($len === 127) { // If 127, then need to use the next 8 bytes to calculate the length.

  $masks_start = 10;

  $len = (ord($buffer[2]) << 56) + (ord($buffer[3]) << 48) + (ord($buffer[4]) << 40) + (ord($buffer[5]) << 32) + (ord($buffer[6]) << 24) + (ord($buffer[7]) << 16) + (ord($buffer[8]) << 8) + ord($buffer[9]);

} else { // Otherwise, the default payload length is correct.

  $masks_start = 2;

}

After doing that, everything worked great. Well, I'm still haven't figured out how to properly close a WebSocket connection, but other than that, the WebRTC video is working great.

HartleySan
  • 7,404
  • 14
  • 66
  • 119
0

Is the garbled binary data being added by client1 before it sends it to the Websocket server? Or are you only seeing it on the client2 after it's been processed by the websocket server? I ask because I ran into a similar problem where my signaling server (SignalR in this case) had a bug that corrupted the SDP I was sending in-between PeerConnections.

Community
  • 1
  • 1
Ken Smith
  • 20,305
  • 15
  • 100
  • 147
  • I'm not sure, but I think the garbled text is being added by the WebSocket server/connection. The following is a screenshot of the command line console output generated by the server script and the strings output to the JS console in Chrome right before I send the strings to the WebSocket server script: http://s172.beta.photobucket.com/user/HartleySan/media/Commandlineandconsoleoutput_zpsfc3e6342.png.html As you can hopefully see, no extra data is output to the Chrome console, so I have to assume that it's being added by the server script and/or connection. It only happens sometimes too. – HartleySan Jan 22 '13 at 17:17
  • Also, I'm not using a third-party script. All my code is code I wrote myself based off of information I got from the WebRTC/WebSocket specs and other answers here on SO. As such, I think I understand everything going on in my scripts, but obviously not, as something is causing this garbled text to sometimes be generated. – HartleySan Jan 22 '13 at 17:20
  • One other thing, I did a search for the "Could not decode a text frame as UTF-8" message, and I found the following article, which seems related to my issue: http://blog.fgribreau.com/2012/05/how-to-fix-could-not-decode-text-frame.html The guy recommends two methods for dealing with this: 1) Trim the string (as I am doing now; this works, but I'd like to avoid it, if possible). 2) Use the escape/unescape and encodeURIComponent/decodeURIComponent functions in JS. I'd prefer to use method #2, but when I try, it doesn't change anything. – HartleySan Jan 22 '13 at 17:40
  • It definitely sounds like it's your websocket implementation (whether on the client or server) that's causing you grief. The only advice I'd give is to note that websockets is a pretty low-level protocol, and that there are a lot of libraries out there (both on the server and on the client) that abstract out a lot of those low-level details. Might be worth investigating them, if only to see how they handle encoding. – Ken Smith Jan 22 '13 at 18:14
  • Yeah, after thinking about your previous post some more last night, I have to agree, it's most likely an issue with my WebSocket implementation. Do you have any recommendations in regards to a good WebSocket library? Thank you. – HartleySan Jan 22 '13 at 22:31
  • Depends on which back-end technology you want to use - for various reasons, the client-side libraries are usually tied to a particular back-end implementation. I've got an ASP.NET back-end, and have been using SignalR, and am mostly very happy with it. But see here for more alternatives: http://en.wikipedia.org/wiki/Comparison_of_WebSocket_implementations – Ken Smith Jan 22 '13 at 22:56