You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

strophe.jingle.sessionbase.js 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /**
  2. * Base class for ColibriFocus and JingleSession.
  3. * @param connection Strophe connection object
  4. * @param sid my session identifier(resource)
  5. * @constructor
  6. */
  7. function SessionBase(connection, sid){
  8. this.connection = connection;
  9. this.sid = sid;
  10. }
  11. SessionBase.prototype.modifySources = function (successCallback) {
  12. var self = this;
  13. this.peerconnection.modifySources(function(){
  14. $(document).trigger('setLocalDescription.jingle', [self.sid]);
  15. if(successCallback) {
  16. successCallback();
  17. }
  18. });
  19. };
  20. SessionBase.prototype.addSource = function (elem, fromJid) {
  21. var self = this;
  22. // FIXME: dirty waiting
  23. if (!this.peerconnection.localDescription)
  24. {
  25. console.warn("addSource - localDescription not ready yet")
  26. setTimeout(function()
  27. {
  28. self.addSource(elem, fromJid);
  29. },
  30. 200
  31. );
  32. return;
  33. }
  34. this.peerconnection.addSource(elem);
  35. this.modifySources();
  36. };
  37. SessionBase.prototype.removeSource = function (elem, fromJid) {
  38. var self = this;
  39. // FIXME: dirty waiting
  40. if (!this.peerconnection.localDescription)
  41. {
  42. console.warn("removeSource - localDescription not ready yet")
  43. setTimeout(function()
  44. {
  45. self.removeSource(elem, fromJid);
  46. },
  47. 200
  48. );
  49. return;
  50. }
  51. this.peerconnection.removeSource(elem);
  52. this.modifySources();
  53. };
  54. /**
  55. * Switches video streams.
  56. * @param new_stream new stream that will be used as video of this session.
  57. * @param oldStream old video stream of this session.
  58. * @param success_callback callback executed after successful stream switch.
  59. */
  60. SessionBase.prototype.switchStreams = function (new_stream, oldStream, success_callback) {
  61. var self = this;
  62. // Stop the stream to trigger onended event for old stream
  63. oldStream.stop();
  64. // Remember SDP to figure out added/removed SSRCs
  65. var oldSdp = null;
  66. if(self.peerconnection) {
  67. if(self.peerconnection.localDescription) {
  68. oldSdp = new SDP(self.peerconnection.localDescription.sdp);
  69. }
  70. self.peerconnection.removeStream(oldStream);
  71. self.peerconnection.addStream(new_stream);
  72. }
  73. self.connection.jingle.localVideo = new_stream;
  74. self.connection.jingle.localStreams = [];
  75. self.connection.jingle.localStreams.push(self.connection.jingle.localAudio);
  76. self.connection.jingle.localStreams.push(self.connection.jingle.localVideo);
  77. // Conference is not active
  78. if(!oldSdp || !self.peerconnection) {
  79. success_callback();
  80. return;
  81. }
  82. self.peerconnection.switchstreams = true;
  83. self.modifySources(function() {
  84. console.log('modify sources done');
  85. var newSdp = new SDP(self.peerconnection.localDescription.sdp);
  86. console.log("SDPs", oldSdp, newSdp);
  87. self.notifyMySSRCUpdate(oldSdp, newSdp);
  88. success_callback();
  89. });
  90. };
  91. /**
  92. * Figures out added/removed ssrcs and send update IQs.
  93. * @param old_sdp SDP object for old description.
  94. * @param new_sdp SDP object for new description.
  95. */
  96. SessionBase.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
  97. var old_media = old_sdp.getMediaSsrcMap();
  98. var new_media = new_sdp.getMediaSsrcMap();
  99. //console.log("old/new medias: ", old_media, new_media);
  100. var toAdd = old_sdp.getNewMedia(new_sdp);
  101. var toRemove = new_sdp.getNewMedia(old_sdp);
  102. //console.log("to add", toAdd);
  103. //console.log("to remove", toRemove);
  104. if(Object.keys(toRemove).length > 0){
  105. this.sendSSRCUpdate(toRemove, null, false);
  106. }
  107. if(Object.keys(toAdd).length > 0){
  108. this.sendSSRCUpdate(toAdd, null, true);
  109. }
  110. };
  111. /**
  112. * Empty method that does nothing by default. It should send SSRC update IQs to session participants.
  113. * @param sdpMediaSsrcs array of
  114. * @param fromJid
  115. * @param isAdd
  116. */
  117. SessionBase.prototype.sendSSRCUpdate = function(sdpMediaSsrcs, fromJid, isAdd) {
  118. //FIXME: put default implementation here(maybe from JingleSession?)
  119. }
  120. /**
  121. * Sends SSRC update IQ.
  122. * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove.
  123. * @param sid session identifier that will be put into the IQ.
  124. * @param initiator initiator identifier.
  125. * @param toJid destination Jid
  126. * @param isAdd indicates if this is remove or add operation.
  127. */
  128. SessionBase.prototype.sendSSRCUpdateIq = function(sdpMediaSsrcs, sid, initiator, toJid, isAdd) {
  129. var self = this;
  130. var modify = $iq({to: toJid, type: 'set'})
  131. .c('jingle', {
  132. xmlns: 'urn:xmpp:jingle:1',
  133. action: isAdd ? 'addsource' : 'removesource',
  134. initiator: initiator,
  135. sid: sid
  136. }
  137. );
  138. // FIXME: only announce video ssrcs since we mix audio and dont need
  139. // the audio ssrcs therefore
  140. var modified = false;
  141. Object.keys(sdpMediaSsrcs).forEach(function(channelNum){
  142. modified = true;
  143. var channel = sdpMediaSsrcs[channelNum];
  144. modify.c('content', {name: channel.mediaType});
  145. // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly
  146. // generate sources from lines
  147. Object.keys(channel.ssrcs).forEach(function(ssrcNum) {
  148. var mediaSsrc = channel.ssrcs[ssrcNum];
  149. modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
  150. modify.attrs({ssrc: mediaSsrc.ssrc});
  151. // iterate over ssrc lines
  152. mediaSsrc.lines.forEach(function (line) {
  153. var idx = line.indexOf(' ');
  154. var kv = line.substr(idx + 1);
  155. modify.c('parameter');
  156. if (kv.indexOf(':') == -1) {
  157. modify.attrs({ name: kv });
  158. } else {
  159. modify.attrs({ name: kv.split(':', 2)[0] });
  160. modify.attrs({ value: kv.split(':', 2)[1] });
  161. }
  162. modify.up(); // end of parameter
  163. });
  164. modify.up(); // end of source
  165. });
  166. modify.up(); // end of content
  167. });
  168. if (modified) {
  169. self.connection.sendIQ(modify,
  170. function (res) {
  171. console.info('got modify result', res);
  172. },
  173. function (err) {
  174. console.error('got modify error', err);
  175. }
  176. );
  177. } else {
  178. console.log('modification not necessary');
  179. }
  180. };
  181. // SDP-based mute by going recvonly/sendrecv
  182. // FIXME: should probably black out the screen as well
  183. SessionBase.prototype.toggleVideoMute = function (callback) {
  184. var ismuted = false;
  185. var localVideo = connection.jingle.localVideo;
  186. for (var idx = 0; idx < localVideo.getVideoTracks().length; idx++) {
  187. ismuted = !localVideo.getVideoTracks()[idx].enabled;
  188. }
  189. for (var idx = 0; idx < localVideo.getVideoTracks().length; idx++) {
  190. localVideo.getVideoTracks()[idx].enabled = !localVideo.getVideoTracks()[idx].enabled;
  191. }
  192. this.peerconnection.hardMuteVideo(!ismuted);
  193. this.modifySources(callback(!ismuted));
  194. };