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

SimulcastReceiver.js 8.5KB

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