aboutsummaryrefslogtreecommitdiffstats
path: root/client-website/js/symple.player.webrtc.js
diff options
context:
space:
mode:
Diffstat (limited to 'client-website/js/symple.player.webrtc.js')
-rw-r--r--client-website/js/symple.player.webrtc.js372
1 files changed, 372 insertions, 0 deletions
diff --git a/client-website/js/symple.player.webrtc.js b/client-website/js/symple.player.webrtc.js
new file mode 100644
index 0000000..eb57fa7
--- /dev/null
+++ b/client-website/js/symple.player.webrtc.js
@@ -0,0 +1,372 @@
+// -----------------------------------------------------------------------------
+// WebRTC Engine
+//
+window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
+window.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
+window.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
+navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
+window.URL = window.webkitURL || window.URL;
+
+var myLocalStream = undefined;
+var isVideoMuted = false;
+var isAudioMuted = false;
+
+
+Symple.Media.registerEngine({
+ id: 'WebRTC',
+ name: 'WebRTC Player',
+ formats: 'VP8, Opus',
+ preference: 100,
+ support: (function() {
+ return typeof RTCPeerConnection != "undefined";
+ })()
+});
+
+
+Symple.Player.Engine.WebRTC = Symple.Player.Engine.extend({
+ init: function(player) {
+ Symple.log("SympleWebRTC: Init");
+ this._super(player);
+
+ this.rtcConfig = player.options.rtcConfig || {
+ iceServers: [
+ { url: "stun:stun.l.google.com:19302" }
+ ]
+ }
+ this.rtcOptions = player.options.rtcOptions || {
+ optional: [
+ {DtlsSrtpKeyAgreement: true} // FF <=> Chrome interop
+ ]
+ }
+ this.mediaConstraints = player.options.mediaConstraints || {}
+ //this.mediaConstraints = player.options.mediaConstraints || {
+ // 'mandatory': {
+ // 'OfferToReceiveAudio':true,
+ // 'OfferToReceiveVideo':true
+ // }
+ //};
+ },
+
+ setup: function() {
+ Symple.log("SympleWebRTC: Setup");
+
+ this._createPeerConnection();
+
+ // Note: Absolutely position video element so it scales to
+ // the parent element size. Need to test in other browsers.
+
+ if (typeof(this.video) == 'undefined') {
+ Symple.log("SympleWebRTC: Setup: Peer video");
+ this.video = $('<video autoplay></video>')
+ this.player.screen.prepend(this.video);
+ }
+
+ //this.video = $('<video width="100%" height="100%" style="position:absolute;left:0;top:0;"></video>'); // Chrome
+ //this.selfVideo = typeof(this.selfVideo) == 'undefined' ? $('<video></video>') : this.selfVideo;
+ //this.video = typeof(this.video) == 'undefined' ? $('<video></video>') : this.video; // style="position:absolute;left:0;top:0;" width="100%" height="100%" style="max-width:100%;height:auto;"
+ },
+
+ destroy: function() {
+ Symple.log("SympleWebRTC: Destroy");
+ this.sendLocalSDP = null;
+ this.sendLocalCandidate = null;
+
+ if (this.video) {
+ this.video[0].src = '';
+ this.video[0] = null;
+ this.video = null;
+ // Anything else required for video cleanup?
+ }
+
+ if (this.pc) {
+ this.pc.close();
+ this.pc = null;
+ // Anything else required for peer connection cleanup?
+ }
+ },
+
+ play: function(params) {
+ Symple.log("SympleWebRTC: Play", params);
+
+ // The 'playing' state will be set when candidates
+ // gathering is complete.
+ // TODO: Get state events from the video element
+ // to shift from local loading to playing state.
+
+ if (params && params.localMedia) {
+
+ // Get the local stream, show it in the local video element and send it
+ var self = this;
+ navigator.getUserMedia({ audio: !params.disableAudio, video: !params.disableVideo },
+
+ // successCallback
+ function (localStream) {
+
+ //self._createPeerConnection();
+ myLocalStream = localStream;
+
+ // Play the local stream
+ self.video[0].src = URL.createObjectURL(localStream);
+ self.pc.addStream(localStream);
+
+ //if (params.caller)
+ self.pc.createOffer(
+ function(desc) { self._onLocalSDP(desc); }, function(err) { self.setError("createOffer() Failed: " + err);});
+ //else
+ // self.pc.createAnswer(
+ // function(desc) { self._onLocalSDP(desc); },
+ // function() { // error
+ // self.setError("Cannot create local SDP answer");
+ // },
+ // null //this.mediaConstraints;
+ // )
+
+ //function gotDescription(desc) {
+ // pc.setLocalDescription(desc);
+ // signalingChannel.send(JSON.stringify({ "sdp": desc }));
+ //}
+ },
+
+ // errorCallback
+ function(err) {
+ self.setError("getUserMedia() Failed: " + err);
+ });
+ }
+ },
+
+ stop: function() {
+
+ if (this.video) {
+ this.video[0].src = '';
+ // Do not nullify
+ }
+
+ // TODO: Close peer connection?
+ if (this.pc) {
+ this.pc.close();
+ this.pc = null;
+ }
+
+ if (myLocalStream) {
+ myLocalStream.stop();
+ myLocalStream = undefined;
+ }
+
+ this.setState('stopped');
+ },
+
+ mute: function(flag) {
+ // Mute unless explicit false given
+ flag = flag === false ? false : true;
+ Symple.log("SympleWebRTC: Mute:", flag);
+
+ if (this.video) {
+ this.video.prop('muted', flag); //mute
+ }
+ },
+
+ muteLocal: function() {
+ Symple.log('mute');
+ if (this.video) {
+ this.video[0].muted = true;
+ }
+ },
+
+ toggleVideo: function() {
+ videoTracks = myLocalStream.getVideoTracks();
+ if (videoTracks.length === 0) {
+ Symple.log('No local video available.');
+ return;
+ }
+
+ if (isVideoMuted) {
+ for (i = 0; i < videoTracks.length; i++) {
+ videoTracks[i].enabled = true;
+ }
+ Symple.log('Video unmuted.');
+ } else {
+ for (i = 0; i < videoTracks.length; i++) {
+ videoTracks[i].enabled = false;
+ }
+ Symple.log('Video muted.');
+ }
+
+ isVideoMuted = !isVideoMuted;
+ },
+
+ toggleAudio: function() {
+ audioTracks = myLocalStream.getAudioTracks();
+
+ if (audioTracks.length === 0) {
+ Symple.log('No local audio available.');
+ return;
+ }
+
+ if (isAudioMuted) {
+ for (i = 0; i < audioTracks.length; i++) {
+ audioTracks[i].enabled = true;
+ }
+ Symple.log('Audio unmuted.');
+ } else {
+ for (i = 0; i < audioTracks.length; i++){
+ audioTracks[i].enabled = false;
+ }
+ Symple.log('Audio muted.');
+ }
+
+ isAudioMuted = !isAudioMuted;
+ },
+
+ fullscreenVideo: function() {
+ if (this.video[0].requestFullscreen) {
+ this.video[0].requestFullscreen();
+ } else if (this.video[0].mozRequestFullScreen) {
+ this.video[0].mozRequestFullScreen();
+ } else if (this.video[0].webkitRequestFullscreen) {
+ this.video[0].webkitRequestFullscreen();
+ }
+ },
+
+
+ // Initiates the player with local media capture
+ //startLocalMedia: function(params) {
+ //Symple.log("SympleWebRTC: Play", params);
+
+ // The 'playing' state will be set when candidates
+ // gathering is complete.
+ // TODO: Get state events from the video element
+ // to shift from local loading to playing state.
+ //},
+
+ //
+ // Called when local SDP is ready to be sent to the peer.
+ sendLocalSDP: new Function,
+
+ //
+ // Called when a local candidate is ready to be sent to the peer.
+ sendLocalCandidate: new Function,
+
+ //
+ // Called when remote SDP is received from the peer.
+ onRemoteSDP: function(desc) {
+ Symple.log('SympleWebRTC: Recieve remote SDP:', desc)
+ if (!desc || !desc.type || !desc.sdp)
+ throw "Invalid SDP data"
+
+ //if (desc.type != "offer")
+ // throw "Only SDP offers are supported"
+
+ var self = this;
+ this.pc.setRemoteDescription(new RTCSessionDescription(desc),
+ function() {
+ Symple.log('SympleWebRTC: SDP success');
+ //alert('success')
+ },
+ function(message) {
+ console.error('SympleWebRTC: SDP error:', message);
+ self.setError("Cannot parse remote SDP offer");
+ }
+ );
+
+ if (desc.type == "offer") {
+ this.pc.createAnswer(
+ function(answer) { // success
+ self._onLocalSDP(answer);
+ //alert('answer')
+ },
+ function() { // error
+ self.setError("Cannot create local SDP answer");
+ },
+ null //this.mediaConstraints
+ );
+ }
+ },
+
+ //
+ // Called when remote candidate is received from the peer.
+ onRemoteCandidate: function(candidate) {
+ //Symple.log("SympleWebRTC: Recieve remote candiate ", candidate);
+ if (!this.pc)
+ throw 'The peer connection is not initialized' // call onRemoteSDP first
+
+ this.pc.addIceCandidate(new RTCIceCandidate({
+ //sdpMid: candidate.sdpMid,
+ sdpMLineIndex: candidate.sdpMLineIndex,
+ candidate: candidate.candidate
+ }));
+ },
+
+
+ //
+ // Private methods
+ //
+
+ //
+ // Called when local SDP is received from the peer.
+ _onLocalSDP: function(desc) {
+ try {
+ this.pc.setLocalDescription(desc);
+ this.sendLocalSDP(desc);
+ }
+ catch (e) {
+ Symple.log("Failed to send local SDP:", e);
+ }
+ },
+
+ _createPeerConnection: function() {
+ if (this.pc)
+ throw 'The peer connection is already initialized'
+
+ Symple.log("SympleWebRTC: Creating peer connection: ", this.rtcConfig);
+
+ var self = this;
+ this.pc = new RTCPeerConnection(this.rtcConfig, this.rtcOptions);
+ this.pc.onicecandidate = function(event) {
+ if (event.candidate) {
+ //Symple.log("SympleWebRTC: Local candidate gathered:", event.candidate);
+ self.sendLocalCandidate(event.candidate);
+ }
+ else {
+ Symple.log("SympleWebRTC: Local candidate gathering complete");
+ }
+ };
+ this.pc.onaddstream = function(event) {
+ Symple.log("SympleWebRTC: Remote stream added:", URL.createObjectURL(event.stream));
+
+ // Set the state to playing once candidates have completed gathering.
+ // This is the best we can do until ICE onstatechange is implemented.
+ self.setState('playing');
+
+ self.video[0].src = URL.createObjectURL(event.stream);
+ self.video[0].play();
+ };
+ this.pc.onremovestream = function(event) {
+ Symple.log("SympleWebRTC: Remote stream removed:", event);
+ self.video[0].stop();
+ };
+
+ // Note: The following state events are completely unreliable.
+ // Hopefully when the spec is complete this will change, but
+ // until then we need to "guess" the state.
+ //this.pc.onconnecting = function(event) { Symple.log("SympleWebRTC: onconnecting:", event); };
+ //this.pc.onopen = function(event) { Symple.log("SympleWebRTC: onopen:", event); };
+ //this.pc.onicechange = function(event) { Symple.log("SympleWebRTC: onicechange :", event); };
+ //this.pc.onstatechange = function(event) { Symple.log("SympleWebRTC: onstatechange :", event); };
+
+ Symple.log("SympleWebRTC: Setupd RTCPeerConnnection with config: " + JSON.stringify(this.rtcConfig));
+ }
+});
+
+
+//
+// Helpers
+
+Symple.Media.iceCandidateType = function(candidateSDP) {
+ if (candidateSDP.indexOf("typ relay") != -1)
+ return "turn";
+ if (candidateSDP.indexOf("typ srflx") != -1)
+ return "stun";
+ if (candidateSDP.indexOf("typ host") != -1)
+ return "host";
+ return "unknown";
+}