您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

strophe.jingle.sessionbase.js 7.9KB

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