Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

SimulcastReceiver.js 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. var SimulcastLogger = require("./SimulcastLogger");
  2. var SimulcastUtils = require("./SimulcastUtils");
  3. function SimulcastReceiver() {
  4. this.simulcastUtils = new SimulcastUtils();
  5. this.logger = new SimulcastLogger('SimulcastReceiver', 1);
  6. }
  7. SimulcastReceiver.prototype._remoteVideoSourceCache = '';
  8. SimulcastReceiver.prototype._remoteMaps = {
  9. msid2Quality: {},
  10. ssrc2Msid: {},
  11. msid2ssrc: {},
  12. receivingVideoStreams: {}
  13. };
  14. SimulcastReceiver.prototype._cacheRemoteVideoSources = function (lines) {
  15. this._remoteVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
  16. };
  17. SimulcastReceiver.prototype._restoreRemoteVideoSources = function (lines) {
  18. this.simulcastUtils._replaceVideoSources(lines, this._remoteVideoSourceCache);
  19. };
  20. SimulcastReceiver.prototype._ensureGoogConference = function (lines) {
  21. var sb;
  22. this.logger.info('Ensuring x-google-conference flag...')
  23. if (this.simulcastUtils._indexOfArray('a=x-google-flag:conference', lines) === this.simulcastUtils._emptyCompoundIndex) {
  24. // TODO(gp) do that for the audio as well as suggested by fippo.
  25. // Add the google conference flag
  26. sb = this.simulcastUtils._getVideoSources(lines);
  27. sb = ['a=x-google-flag:conference'].concat(sb);
  28. this.simulcastUtils._replaceVideoSources(lines, sb);
  29. }
  30. };
  31. SimulcastReceiver.prototype._restoreSimulcastGroups = function (sb) {
  32. this._restoreRemoteVideoSources(sb);
  33. };
  34. /**
  35. * Restores the simulcast groups of the remote description. In
  36. * transformRemoteDescription we remove those in order for the set remote
  37. * description to succeed. The focus needs the signal the groups to new
  38. * participants.
  39. *
  40. * @param desc
  41. * @returns {*}
  42. */
  43. SimulcastReceiver.prototype.reverseTransformRemoteDescription = function (desc) {
  44. var sb;
  45. if (!this.simulcastUtils.isValidDescription(desc)) {
  46. return desc;
  47. }
  48. if (config.enableSimulcast) {
  49. sb = desc.sdp.split('\r\n');
  50. this._restoreSimulcastGroups(sb);
  51. desc = new RTCSessionDescription({
  52. type: desc.type,
  53. sdp: sb.join('\r\n')
  54. });
  55. }
  56. return desc;
  57. };
  58. SimulcastUtils.prototype._ensureOrder = function (lines) {
  59. var videoSources, sb;
  60. videoSources = this.parseMedia(lines, ['video'])[0];
  61. sb = this._compileVideoSources(videoSources);
  62. this._replaceVideoSources(lines, sb);
  63. };
  64. SimulcastReceiver.prototype._updateRemoteMaps = function (lines) {
  65. var remoteVideoSources = this.simulcastUtils.parseMedia(lines, ['video'])[0],
  66. videoSource, quality;
  67. // (re) initialize the remote maps.
  68. this._remoteMaps.msid2Quality = {};
  69. this._remoteMaps.ssrc2Msid = {};
  70. this._remoteMaps.msid2ssrc = {};
  71. var self = this;
  72. if (remoteVideoSources.groups && remoteVideoSources.groups.length !== 0) {
  73. remoteVideoSources.groups.forEach(function (group) {
  74. if (group.semantics === 'SIM' && group.ssrcs && group.ssrcs.length !== 0) {
  75. quality = 0;
  76. group.ssrcs.forEach(function (ssrc) {
  77. videoSource = remoteVideoSources.sources[ssrc];
  78. self._remoteMaps.msid2Quality[videoSource.msid] = quality++;
  79. self._remoteMaps.ssrc2Msid[videoSource.ssrc] = videoSource.msid;
  80. self._remoteMaps.msid2ssrc[videoSource.msid] = videoSource.ssrc;
  81. });
  82. }
  83. });
  84. }
  85. };
  86. SimulcastReceiver.prototype._setReceivingVideoStream = function (resource, ssrc) {
  87. this._remoteMaps.receivingVideoStreams[resource] = ssrc;
  88. };
  89. /**
  90. * Returns a stream with single video track, the one currently being
  91. * received by this endpoint.
  92. *
  93. * @param stream the remote simulcast stream.
  94. * @returns {webkitMediaStream}
  95. */
  96. SimulcastReceiver.prototype.getReceivingVideoStream = function (stream) {
  97. var tracks, i, electedTrack, msid, quality = 0, receivingTrackId;
  98. var self = this;
  99. if (config.enableSimulcast) {
  100. stream.getVideoTracks().some(function (track) {
  101. return Object.keys(self._remoteMaps.receivingVideoStreams).some(function (resource) {
  102. var ssrc = self._remoteMaps.receivingVideoStreams[resource];
  103. var msid = self._remoteMaps.ssrc2Msid[ssrc];
  104. if (msid == [stream.id, track.id].join(' ')) {
  105. electedTrack = track;
  106. return true;
  107. }
  108. });
  109. });
  110. if (!electedTrack) {
  111. // we don't have an elected track, choose by initial quality.
  112. tracks = stream.getVideoTracks();
  113. for (i = 0; i < tracks.length; i++) {
  114. msid = [stream.id, tracks[i].id].join(' ');
  115. if (this._remoteMaps.msid2Quality[msid] === quality) {
  116. electedTrack = tracks[i];
  117. break;
  118. }
  119. }
  120. // TODO(gp) if the initialQuality could not be satisfied, lower
  121. // the requirement and try again.
  122. }
  123. }
  124. return (electedTrack)
  125. ? new webkitMediaStream([electedTrack])
  126. : stream;
  127. };
  128. SimulcastReceiver.prototype.getReceivingSSRC = function (jid) {
  129. var resource = Strophe.getResourceFromJid(jid);
  130. var ssrc = this._remoteMaps.receivingVideoStreams[resource];
  131. // If we haven't receiving a "changed" event yet, then we must be receiving
  132. // low quality (that the sender always streams).
  133. if (!ssrc && connection.jingle) {
  134. var session;
  135. var i, j, k;
  136. var keys = Object.keys(connection.jingle.sessions);
  137. for (i = 0; i < keys.length; i++) {
  138. var sid = keys[i];
  139. if (ssrc) {
  140. // stream found, stop.
  141. break;
  142. }
  143. session = connection.jingle.sessions[sid];
  144. if (session.remoteStreams) {
  145. for (j = 0; j < session.remoteStreams.length; j++) {
  146. var remoteStream = session.remoteStreams[j];
  147. if (ssrc) {
  148. // stream found, stop.
  149. break;
  150. }
  151. var tracks = remoteStream.getVideoTracks();
  152. if (tracks) {
  153. for (k = 0; k < tracks.length; k++) {
  154. var track = tracks[k];
  155. var msid = [remoteStream.id, track.id].join(' ');
  156. var _ssrc = this._remoteMaps.msid2ssrc[msid];
  157. var _jid = ssrc2jid[_ssrc];
  158. var quality = this._remoteMaps.msid2Quality[msid];
  159. if (jid == _jid && quality == 0) {
  160. ssrc = _ssrc;
  161. // stream found, stop.
  162. break;
  163. }
  164. }
  165. }
  166. }
  167. }
  168. }
  169. }
  170. return ssrc;
  171. };
  172. SimulcastReceiver.prototype.getReceivingVideoStreamBySSRC = function (ssrc)
  173. {
  174. var session, electedStream;
  175. var i, j, k;
  176. if (connection.jingle) {
  177. var keys = Object.keys(connection.jingle.sessions);
  178. for (i = 0; i < keys.length; i++) {
  179. var sid = keys[i];
  180. if (electedStream) {
  181. // stream found, stop.
  182. break;
  183. }
  184. session = connection.jingle.sessions[sid];
  185. if (session.remoteStreams) {
  186. for (j = 0; j < session.remoteStreams.length; j++) {
  187. var remoteStream = session.remoteStreams[j];
  188. if (electedStream) {
  189. // stream found, stop.
  190. break;
  191. }
  192. var tracks = remoteStream.getVideoTracks();
  193. if (tracks) {
  194. for (k = 0; k < tracks.length; k++) {
  195. var track = tracks[k];
  196. var msid = [remoteStream.id, track.id].join(' ');
  197. var tmp = this._remoteMaps.msid2ssrc[msid];
  198. if (tmp == ssrc) {
  199. electedStream = new webkitMediaStream([track]);
  200. // stream found, stop.
  201. break;
  202. }
  203. }
  204. }
  205. }
  206. }
  207. }
  208. }
  209. return {
  210. session: session,
  211. stream: electedStream
  212. };
  213. };
  214. /**
  215. * Gets the fully qualified msid (stream.id + track.id) associated to the
  216. * SSRC.
  217. *
  218. * @param ssrc
  219. * @returns {*}
  220. */
  221. SimulcastReceiver.prototype.getRemoteVideoStreamIdBySSRC = function (ssrc) {
  222. return this._remoteMaps.ssrc2Msid[ssrc];
  223. };
  224. /**
  225. * Removes the ssrc-group:SIM from the remote description bacause Chrome
  226. * either gets confused and thinks this is an FID group or, if an FID group
  227. * is already present, it fails to set the remote description.
  228. *
  229. * @param desc
  230. * @returns {*}
  231. */
  232. SimulcastReceiver.prototype.transformRemoteDescription = function (desc) {
  233. if (desc && desc.sdp) {
  234. var sb = desc.sdp.split('\r\n');
  235. this._updateRemoteMaps(sb);
  236. this._cacheRemoteVideoSources(sb);
  237. // NOTE(gp) this needs to be called after updateRemoteMaps because we
  238. // need the simulcast group in the _updateRemoteMaps() method.
  239. this.simulcastUtils._removeSimulcastGroup(sb);
  240. if (desc.sdp.indexOf('a=ssrc-group:SIM') !== -1) {
  241. // We don't need the goog conference flag if we're not doing
  242. // simulcast.
  243. this._ensureGoogConference(sb);
  244. }
  245. desc = new RTCSessionDescription({
  246. type: desc.type,
  247. sdp: sb.join('\r\n')
  248. });
  249. this.logger.fine(['Transformed remote description', desc.sdp].join(' '));
  250. }
  251. return desc;
  252. };
  253. module.exports = SimulcastReceiver;