Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

JitsiLocalTrack.js 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /* global __filename, Promise */
  2. var logger = require("jitsi-meet-logger").getLogger(__filename);
  3. var JitsiTrack = require("./JitsiTrack");
  4. var RTCBrowserType = require("./RTCBrowserType");
  5. var JitsiTrackEvents = require('../../JitsiTrackEvents');
  6. var JitsiTrackErrors = require("../../JitsiTrackErrors");
  7. var RTCUtils = require("./RTCUtils");
  8. var VideoType = require('../../service/RTC/VideoType');
  9. /**
  10. * Represents a single media track (either audio or video).
  11. * @constructor
  12. */
  13. function JitsiLocalTrack(stream, videoType,
  14. resolution, deviceId)
  15. {
  16. this.videoType = videoType;
  17. this.dontFireRemoveEvent = false;
  18. this.resolution = resolution;
  19. this.deviceId = deviceId;
  20. this.startMuted = false;
  21. this.ssrc = null;
  22. this.disposed = false;
  23. //FIXME: This dependacy is not necessary.
  24. this.conference = null;
  25. JitsiTrack.call(this, null, stream,
  26. function () {
  27. if(!this.dontFireRemoveEvent)
  28. this.eventEmitter.emit(
  29. JitsiTrackEvents.LOCAL_TRACK_STOPPED);
  30. this.dontFireRemoveEvent = false;
  31. }.bind(this));
  32. this.initialMSID = this.getMSID();
  33. this.inMuteOrUnmuteProgress = false;
  34. }
  35. JitsiLocalTrack.prototype = Object.create(JitsiTrack.prototype);
  36. JitsiLocalTrack.prototype.constructor = JitsiLocalTrack;
  37. /**
  38. * Mutes the track. Will reject the Promise if there is mute/unmute operation
  39. * in progress.
  40. * @returns {Promise}
  41. */
  42. JitsiLocalTrack.prototype.mute = function () {
  43. return createMuteUnmutePromise(this, true);
  44. };
  45. /**
  46. * Unmutes the track. Will reject the Promise if there is mute/unmute operation
  47. * in progress.
  48. * @returns {Promise}
  49. */
  50. JitsiLocalTrack.prototype.unmute = function () {
  51. return createMuteUnmutePromise(this, false);
  52. };
  53. /**
  54. * Creates Promise for mute/unmute operation.
  55. * @param track the track that will be muted/unmuted
  56. * @param mute whether to mute or unmute the track
  57. */
  58. function createMuteUnmutePromise(track, mute)
  59. {
  60. return new Promise(function (resolve, reject) {
  61. if(this.inMuteOrUnmuteProgress) {
  62. reject(new Error(JitsiTrackErrors.TRACK_MUTE_UNMUTE_IN_PROGRESS));
  63. return;
  64. }
  65. this.inMuteOrUnmuteProgress = true;
  66. this._setMute(mute,
  67. function(){
  68. this.inMuteOrUnmuteProgress = false;
  69. resolve();
  70. }.bind(this),
  71. function(status){
  72. this.inMuteOrUnmuteProgress = false;
  73. reject(status);
  74. }.bind(this));
  75. }.bind(track));
  76. }
  77. /**
  78. * Mutes / unmutes the track.
  79. * @param mute {boolean} if true the track will be muted. Otherwise the track
  80. * will be unmuted.
  81. */
  82. JitsiLocalTrack.prototype._setMute = function (mute, resolve, reject) {
  83. if (this.isMuted() === mute) {
  84. resolve();
  85. return;
  86. }
  87. if(!this.rtc) {
  88. this.startMuted = mute;
  89. resolve();
  90. return;
  91. }
  92. var isAudio = this.isAudioTrack();
  93. this.dontFireRemoveEvent = false;
  94. var setStreamToNull = false;
  95. // the callback that will notify that operation had finished
  96. var callbackFunction = function() {
  97. if(setStreamToNull)
  98. this.stream = null;
  99. this.eventEmitter.emit(JitsiTrackEvents.TRACK_MUTE_CHANGED);
  100. resolve();
  101. }.bind(this);
  102. if ((window.location.protocol != "https:") ||
  103. (isAudio) || this.videoType === VideoType.DESKTOP ||
  104. // FIXME FF does not support 'removeStream' method used to mute
  105. RTCBrowserType.isFirefox()) {
  106. var tracks = this._getTracks();
  107. for (var idx = 0; idx < tracks.length; idx++) {
  108. tracks[idx].enabled = !mute;
  109. }
  110. if(isAudio)
  111. this.rtc.room.setAudioMute(mute, callbackFunction);
  112. else
  113. this.rtc.room.setVideoMute(mute, callbackFunction);
  114. } else {
  115. if (mute) {
  116. this.dontFireRemoveEvent = true;
  117. this.rtc.room.removeStream(this.stream, function () {},
  118. {mtype: this.type, type: "mute", ssrc: this.ssrc});
  119. RTCUtils.stopMediaStream(this.stream);
  120. setStreamToNull = true;
  121. if(isAudio)
  122. this.rtc.room.setAudioMute(mute, callbackFunction);
  123. else
  124. this.rtc.room.setVideoMute(mute, callbackFunction);
  125. //FIXME: Maybe here we should set the SRC for the containers to something
  126. } else {
  127. var self = this;
  128. // FIXME why are we doing all this audio type checks and
  129. // convoluted scenarios if we're going this way only
  130. // for VIDEO media and CAMERA type of video ?
  131. var streamOptions = {
  132. devices: (isAudio ? ["audio"] : ["video"]),
  133. resolution: self.resolution
  134. };
  135. if (isAudio) {
  136. streamOptions['micDeviceId'] = self.deviceId;
  137. } else if(self.videoType === VideoType.CAMERA) {
  138. streamOptions['cameraDeviceId'] = self.deviceId;
  139. }
  140. RTCUtils.obtainAudioAndVideoPermissions(streamOptions)
  141. .then(function (streamsInfo) {
  142. var streamInfo = null;
  143. for(var i = 0; i < streamsInfo.length; i++) {
  144. if(streamsInfo[i].type === self.type) {
  145. streamInfo = streamsInfo[i];
  146. self.stream = streamInfo.stream;
  147. // This is not good when video type changes after
  148. // unmute, but let's not crash here
  149. if (self.videoType != streamInfo.videoType) {
  150. logger.error(
  151. "Video type has changed after unmute!",
  152. self.videoType, streamInfo.videoType);
  153. self.videoType = streamInfo.videoType;
  154. }
  155. break;
  156. }
  157. }
  158. if(!streamInfo) {
  159. reject(new Error('track.no_stream_found'));
  160. return;
  161. }
  162. for(var i = 0; i < self.containers.length; i++)
  163. {
  164. self.containers[i]
  165. = RTCUtils.attachMediaStream(
  166. self.containers[i], self.stream);
  167. }
  168. self.rtc.room.addStream(self.stream,
  169. function () {
  170. if(isAudio)
  171. self.rtc.room.setAudioMute(
  172. mute, callbackFunction);
  173. else
  174. self.rtc.room.setVideoMute(
  175. mute, callbackFunction);
  176. }, {
  177. mtype: self.type,
  178. type: "unmute",
  179. ssrc: self.ssrc,
  180. msid: self.getMSID()});
  181. }).catch(function (error) {
  182. reject(error);
  183. });
  184. }
  185. }
  186. };
  187. /**
  188. * Stops sending the media track. And removes it from the HTML.
  189. * NOTE: Works for local tracks only.
  190. * @returns {Promise}
  191. */
  192. JitsiLocalTrack.prototype.dispose = function () {
  193. var promise = Promise.resolve();
  194. if (this.conference){
  195. promise = this.conference.removeTrack(this);
  196. }
  197. if (this.stream) {
  198. RTCUtils.stopMediaStream(this.stream);
  199. this.detach();
  200. }
  201. this.disposed = true;
  202. return promise;
  203. };
  204. /**
  205. * Returns <tt>true</tt> - if the stream is muted
  206. * and <tt>false</tt> otherwise.
  207. * @returns {boolean} <tt>true</tt> - if the stream is muted
  208. * and <tt>false</tt> otherwise.
  209. */
  210. JitsiLocalTrack.prototype.isMuted = function () {
  211. // this.stream will be null when we mute local video on Chrome
  212. if (!this.stream)
  213. return true;
  214. var tracks = [];
  215. var isAudio = this.isAudioTrack();
  216. if (isAudio) {
  217. tracks = this.stream.getAudioTracks();
  218. } else {
  219. if (!this.isActive())
  220. return true;
  221. tracks = this.stream.getVideoTracks();
  222. }
  223. for (var idx = 0; idx < tracks.length; idx++) {
  224. if(tracks[idx].enabled)
  225. return false;
  226. }
  227. return true;
  228. };
  229. /**
  230. * Private method. Updates rtc property of the track.
  231. * @param rtc the rtc instance.
  232. */
  233. JitsiLocalTrack.prototype._setRTC = function (rtc) {
  234. this.rtc = rtc;
  235. // We want to keep up with postponed events which should have been fired
  236. // on "attach" call, but for local track we not always have the conference
  237. // before attaching. However this may result in duplicated events if they
  238. // have been triggered on "attach" already.
  239. for(var i = 0; i < this.containers.length; i++)
  240. {
  241. this._maybeFireTrackAttached(this.containers[i]);
  242. }
  243. };
  244. /**
  245. * Updates the SSRC associated with the MediaStream in JitsiLocalTrack object.
  246. * @ssrc the new ssrc
  247. */
  248. JitsiLocalTrack.prototype._setSSRC = function (ssrc) {
  249. this.ssrc = ssrc;
  250. };
  251. //FIXME: This dependacy is not necessary. This is quick fix.
  252. /**
  253. * Sets the JitsiConference object associated with the track. This is temp
  254. * solution.
  255. * @param conference the JitsiConference object
  256. */
  257. JitsiLocalTrack.prototype._setConference = function(conference) {
  258. this.conference = conference;
  259. };
  260. /**
  261. * Gets the SSRC of this local track if it's available already or <tt>null</tt>
  262. * otherwise. That's because we don't know the SSRC until local description is
  263. * created.
  264. * In case of video and simulcast returns the the primarySSRC.
  265. * @returns {string} or {null}
  266. */
  267. JitsiLocalTrack.prototype.getSSRC = function () {
  268. if(this.ssrc && this.ssrc.groups && this.ssrc.groups.length)
  269. return this.ssrc.groups[0].primarySSRC;
  270. else if(this.ssrc && this.ssrc.ssrcs && this.ssrc.ssrcs.length)
  271. return this.ssrc.ssrcs[0];
  272. else
  273. return null;
  274. };
  275. /**
  276. * Return true;
  277. */
  278. JitsiLocalTrack.prototype.isLocal = function () {
  279. return true;
  280. };
  281. module.exports = JitsiLocalTrack;