Browse Source

now firefox to firefox works. It looks like there is a conection between firefox and chrome to but I have not gotten the media to play back. I dont know why the video is not playing back in the video tag... any one please have a look

Dennis Mårtensson 12 years ago
parent
commit
e9b831374d
3 changed files with 224 additions and 86 deletions
  1. 3 2
      site/script.js
  2. 0 13
      site/server.js
  3. 221 71
      site/webrtc.io.js

+ 3 - 2
site/script.js

@@ -1,5 +1,5 @@
 var videos = [];
-var PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection;
+var PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.RTCPeerConnection;
 
 function getNumPerRow() {
   var len = videos.length;
@@ -164,10 +164,11 @@ function initChat() {
 function init() {
   if(PeerConnection) {
     rtc.createStream({
-      "video": true,
+      "video": {"mandatory": {}, "optional": []},
       "audio": true
     }, function(stream) {
       document.getElementById('you').src = URL.createObjectURL(stream);
+      document.getElementById('you').play();
       videos.push(document.getElementById('you'));
       //rtc.attachStream(stream, 'you');
       subdivideVideos();

+ 0 - 13
site/server.js

@@ -27,19 +27,6 @@ app.get('/webrtc.io.js', function(req, res) {
   res.sendfile(__dirname + '/webrtc.io.js');
 });
 
-
-webRTC.rtc.on('connect', function(rtc) {
-  //Client connected
-});
-
-webRTC.rtc.on('send answer', function(rtc) {
-  //answer sent
-});
-
-webRTC.rtc.on('disconnect', function(rtc) {
-  //Client disconnect 
-});
-
 webRTC.rtc.on('chat_msg', function(data, socket) {
   var roomList = webRTC.rtc.rooms[data.room] || [];
 

+ 221 - 71
site/webrtc.io.js

@@ -1,10 +1,40 @@
 //CLIENT
 
- // Fallbacks for vendor-specific variables until the spec is finalized.
+// Fallbacks for vendor-specific variables until the spec is finalized.
+
+var PeerConnection = (window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection || window.mozRTCPeerConnection);
+var URL = (window.URL || window.webkitURL || window.msURL || window.oURL);
+var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
+var nativeRTCIceCandidate = (window.mozRTCIceCandidate || window.RTCIceCandidate);
+var nativeRTCSessionDescription = (window.mozRTCSessionDescription || window.RTCSessionDescription); // order is very important: "RTCSessionDescription" defined in Nighly but useless
+
+var sdpConstraints = {
+  'mandatory': {
+    'OfferToReceiveAudio': true,
+    'OfferToReceiveVideo': true
+  }
+};
 
-var PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection;
-var URL = window.URL || window.webkitURL || window.msURL || window.oURL;
-var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+if (navigator.webkitGetUserMedia) {
+  if (!webkitMediaStream.prototype.getVideoTracks) {
+    webkitMediaStream.prototype.getVideoTracks = function() {
+      return this.videoTracks;
+    };
+    webkitMediaStream.prototype.getAudioTracks = function() {
+      return this.audioTracks;
+    };
+  }
+
+  // New syntax of getXXXStreams method in M26.
+  if (!webkitRTCPeerConnection.prototype.getLocalStreams) {
+    webkitRTCPeerConnection.prototype.getLocalStreams = function() {
+      return this.localStreams;
+    };
+    webkitRTCPeerConnection.prototype.getRemoteStreams = function() {
+      return this.remoteStreams;
+    };
+  }
+}
 
 (function() {
 
@@ -44,7 +74,21 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
   };
 
   // Holds the STUN/ICE server to use for PeerConnections.
-  rtc.SERVER = {iceServers:[{url:"stun:stun.l.google.com:19302"}]};
+  rtc.SERVER = function() {
+    if (navigator.mozGetUserMedia) {
+      return {
+        "iceServers": [{
+          "url": "stun:23.21.150.121"
+        }]
+      };
+    }
+    return {
+      "iceServers": [{
+        "url": "stun:stun.l.google.com:19302"
+      }]
+    };
+  }
+
 
   // Reference to the lone PeerConnection instance.
   rtc.peerConnections = {};
@@ -61,18 +105,32 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
   rtc.dataChannels = {};
 
   // PeerConnection datachannel configuration
-  rtc.dataChannelConfig = {optional: [ {RtpDataChannels: true} ] };
+  rtc.dataChannelConfig = {
+    "optional": [{
+      "RtpDataChannels": true
+    }, {
+      "DtlsSrtpKeyAgreement": true
+    }]
+  };
+
+  rtc.pc_constraints = {
+    "optional": [{
+      "DtlsSrtpKeyAgreement": true
+    }]
+  }
 
 
   // check whether data channel is supported.
   rtc.checkDataChannelSupport = function() {
     try {
       // raises exception if createDataChannel is not supported
-      var pc = new PeerConnection(rtc.SERVER, rtc.dataChannelConfig);
-      var channel = pc.createDataChannel('supportCheck', {reliable: false});
+      var pc = new PeerConnection(rtc.SERVER(), rtc.dataChannelConfig);
+      var channel = pc.createDataChannel('supportCheck', {
+        reliable: false
+      });
       channel.close();
       return true;
-    } catch(e) {
+    } catch (e) {
       return false;
     }
   };
@@ -91,7 +149,7 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
 
       rtc._socket.send(JSON.stringify({
         "eventName": "join_room",
-        "data":{
+        "data": {
           "room": room
         }
       }));
@@ -119,7 +177,7 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
       });
 
       rtc.on('receive_ice_candidate', function(data) {
-        var candidate = new RTCIceCandidate(data);
+        var candidate = new nativeRTCIceCandidate(data);
         rtc.peerConnections[data.socketId].addIceCandidate(candidate);
         rtc.fire('receive ice candidate', candidate);
       });
@@ -174,24 +232,24 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
   };
 
   rtc.createPeerConnection = function(id) {
-    var config;
-    if (rtc.dataChannelSupport)
-      config = rtc.dataChannelConfig;
 
-    var pc = rtc.peerConnections[id] = new PeerConnection(rtc.SERVER, config);
+    var config = rtc.pc_constraints;
+    if (rtc.dataChannelSupport) config = rtc.dataChannelConfig;
+
+    var pc = rtc.peerConnections[id] = new PeerConnection(rtc.SERVER(), config);
     pc.onicecandidate = function(event) {
       if (event.candidate) {
-         rtc._socket.send(JSON.stringify({
-           "eventName": "send_ice_candidate",
-           "data": {
-              "label": event.candidate.label,
-              "candidate": event.candidate.candidate,
-              "socketId": id
-           }
-         }));
-       }
-       rtc.fire('ice candidate', event.candidate);
-     };
+        rtc._socket.send(JSON.stringify({
+          "eventName": "send_ice_candidate",
+          "data": {
+            "label": event.candidate.sdpMLineIndex,
+            "candidate": event.candidate.candidate,
+            "socketId": id
+          }
+        }));
+      }
+      rtc.fire('ice candidate', event.candidate);
+    };
 
     pc.onopen = function() {
       // TODO: Finalize this API
@@ -204,7 +262,7 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
     };
 
     if (rtc.dataChannelSupport) {
-      pc.ondatachannel = function (evt) {
+      pc.ondatachannel = function(evt) {
         console.log('data channel connecting ' + id);
         rtc.addDataChannel(id, evt.channel);
       };
@@ -215,60 +273,73 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
 
   rtc.sendOffer = function(socketId) {
     var pc = rtc.peerConnections[socketId];
-    pc.createOffer( function(session_description) {
-    pc.setLocalDescription(session_description);
-    rtc._socket.send(JSON.stringify({
+
+    var constraints = {
+      "optional": [],
+      "mandatory": {
+        "MozDontOfferDataChannel": true
+      }
+    };
+    // temporary measure to remove Moz* constraints in Chrome
+    if (navigator.webkitGetUserMedia) {
+      for (prop in constraints.mandatory) {
+        if (prop.indexOf("Moz") != -1) {
+          delete constraints.mandatory[prop];
+        }
+      }
+    }
+    constraints = mergeConstraints(constraints, sdpConstraints);
+
+    pc.createOffer(function(session_description) {
+      session_description.sdp = preferOpus(session_description.sdp);
+      pc.setLocalDescription(session_description);
+      rtc._socket.send(JSON.stringify({
         "eventName": "send_offer",
-        "data":{
-            "socketId": socketId,
-            "sdp": session_description
-            }
-        }));
-    });
+        "data": {
+          "socketId": socketId,
+          "sdp": session_description
+        }
+      }));
+    }, null, sdpConstraints);
   };
 
-
   rtc.receiveOffer = function(socketId, sdp) {
     var pc = rtc.peerConnections[socketId];
-    pc.setRemoteDescription(new RTCSessionDescription(sdp));
-    rtc.sendAnswer(socketId);
+    rtc.sendAnswer(socketId, sdp);
   };
 
-
-  rtc.sendAnswer = function(socketId) {
+  rtc.sendAnswer = function(socketId, sdp) {
     var pc = rtc.peerConnections[socketId];
-    pc.createAnswer( function(session_description) {
-    pc.setLocalDescription(session_description);
-    rtc._socket.send(JSON.stringify({
+    pc.setRemoteDescription(new nativeRTCSessionDescription(sdp));
+    pc.createAnswer(function(session_description) {
+      pc.setLocalDescription(session_description);
+      rtc._socket.send(JSON.stringify({
         "eventName": "send_answer",
-        "data":{
-            "socketId": socketId,
-            "sdp": session_description
-            }
+        "data": {
+          "socketId": socketId,
+          "sdp": session_description
         }
-    ));
-    //TODO Unused variable!?
-    var offer = pc.remoteDescription;
-    });
+      }));
+      //TODO Unused variable!?
+      var offer = pc.remoteDescription;
+    }, null, sdpConstraints);
   };
 
 
   rtc.receiveAnswer = function(socketId, sdp) {
     var pc = rtc.peerConnections[socketId];
-    pc.setRemoteDescription(new RTCSessionDescription(sdp));
+    pc.setRemoteDescription(new nativeRTCSessionDescription(sdp));
   };
 
 
   rtc.createStream = function(opt, onSuccess, onFail) {
     var options;
-    onSuccess = onSuccess ||
-    function() {};
-    onFail = onFail ||
-    function() {};
+    onSuccess = onSuccess || function() {};
+    onFail = onFail || function() {};
 
     options = {
-      video: !!opt.video,
-      audio: !!opt.audio
+      video: !! opt.video,
+      audio: !! opt.audio
     };
 
     if (getUserMedia) {
@@ -300,7 +371,14 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
   };
 
   rtc.attachStream = function(stream, domId) {
-    document.getElementById(domId).src = URL.createObjectURL(stream);
+    var element = document.getElementById(domId);
+    if (navigator.mozGetUserMedia) {
+      console.log("Attaching media stream");
+      element.mozSrcObject = stream;
+      element.play();
+    } else {
+      element.src = webkitURL.createObjectURL(stream);
+    }
   };
 
 
@@ -308,7 +386,7 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
     if (!rtc.dataChannelSupport) {
       //TODO this should be an exception
       alert('webRTC data channel is not yet supported in this browser,' +
-            ' or you must turn on experimental flags');
+        ' or you must turn on experimental flags');
       return;
     }
 
@@ -320,22 +398,21 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
       pc = pcOrId;
       id = undefined;
       for (var key in rtc.peerConnections) {
-        if (rtc.peerConnections[key] === pc)
-          id = key;
+        if (rtc.peerConnections[key] === pc) id = key;
       }
     }
 
-    if (!id)
-      throw new Error ('attempt to createDataChannel with unknown id');
+    if (!id) throw new Error('attempt to createDataChannel with unknown id');
 
-    if (!pc || !(pc instanceof PeerConnection))
-      throw new Error ('attempt to createDataChannel without peerConnection');
+    if (!pc || !(pc instanceof PeerConnection)) throw new Error('attempt to createDataChannel without peerConnection');
 
     // need a label
     label = label || 'fileTransfer' || String(id);
 
     // chrome only supports reliable false atm.
-    var options = {reliable: false};
+    var options = {
+      reliable: false
+    };
 
     var channel;
     try {
@@ -379,11 +456,10 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
   };
 
   rtc.addDataChannels = function() {
-    if (!rtc.dataChannelSupport)
-      return;
+    if (!rtc.dataChannelSupport) return;
 
     for (var connection in rtc.peerConnections)
-      rtc.createDataChannel(connection);
+    rtc.createDataChannel(connection);
   };
 
 
@@ -395,3 +471,77 @@ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || nav
   });
 
 }).call(this);
+
+function preferOpus(sdp) {
+  var sdpLines = sdp.split('\r\n');
+
+  // Search for m line.
+  for (var i = 0; i < sdpLines.length; i++) {
+    if (sdpLines[i].search('m=audio') !== -1) {
+      var mLineIndex = i;
+      break;
+    }
+  }
+  if (mLineIndex === null) return sdp;
+
+  // If Opus is available, set it as the default in m line.
+  for (var i = 0; i < sdpLines.length; i++) {
+    if (sdpLines[i].search('opus/48000') !== -1) {
+      var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
+      if (opusPayload) sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], opusPayload);
+      break;
+    }
+  }
+
+  // Remove CN in m line and sdp.
+  sdpLines = removeCN(sdpLines, mLineIndex);
+
+  sdp = sdpLines.join('\r\n');
+  return sdp;
+}
+
+function extractSdp(sdpLine, pattern) {
+  var result = sdpLine.match(pattern);
+  return (result && result.length == 2) ? result[1] : null;
+}
+
+function setDefaultCodec(mLine, payload) {
+  var elements = mLine.split(' ');
+  var newLine = new Array();
+  var index = 0;
+  for (var i = 0; i < elements.length; i++) {
+    if (index === 3) // Format of media starts from the fourth.
+    newLine[index++] = payload; // Put target payload to the first.
+    if (elements[i] !== payload) newLine[index++] = elements[i];
+  }
+  return newLine.join(' ');
+}
+
+function removeCN(sdpLines, mLineIndex) {
+  var mLineElements = sdpLines[mLineIndex].split(' ');
+  // Scan from end for the convenience of removing an item.
+  for (var i = sdpLines.length - 1; i >= 0; i--) {
+    var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
+    if (payload) {
+      var cnPos = mLineElements.indexOf(payload);
+      if (cnPos !== -1) {
+        // Remove CN payload from m line.
+        mLineElements.splice(cnPos, 1);
+      }
+      // Remove CN line in sdp
+      sdpLines.splice(i, 1);
+    }
+  }
+
+  sdpLines[mLineIndex] = mLineElements.join(' ');
+  return sdpLines;
+}
+
+function mergeConstraints(cons1, cons2) {
+  var merged = cons1;
+  for (var name in cons2.mandatory) {
+    merged.mandatory[name] = cons2.mandatory[name];
+  }
+  merged.optional.concat(cons2.optional);
+  return merged;
+}