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.

330 lines
10 KiB

  1. // TODO(fancycode): Should load through AMD if possible.
  2. /* global SimpleWebRTC, OC, OCA: false */
  3. var webrtc;
  4. var spreedMappingTable = [];
  5. (function(OCA, OC) {
  6. 'use strict';
  7. OCA.SpreedMe = OCA.SpreedMe || {};
  8. /**
  9. * @private
  10. */
  11. function openEventSource() {
  12. // Connect to the messages endpoint and pull for new messages
  13. var messageEventSource = new OC.EventSource(OC.generateUrl('/apps/spreed/messages'));
  14. var previousUsersInRoom = [];
  15. Array.prototype.diff = function(a) {
  16. return this.filter(function(i) {
  17. return a.indexOf(i) < 0;
  18. });
  19. };
  20. messageEventSource.listen('usersInRoom', function(users) {
  21. var currentUsersInRoom = [];
  22. users.forEach(function(user) {
  23. currentUsersInRoom.push(user['sessionId']);
  24. spreedMappingTable[user['sessionId']] = user['userId'];
  25. });
  26. var currentUsersNo = currentUsersInRoom.length;
  27. if(currentUsersNo === 0) {
  28. currentUsersNo = 1;
  29. }
  30. var appContentElement = $('#app-content'),
  31. participantsClass = 'participants-' + currentUsersNo;
  32. if (!appContentElement.hasClass(participantsClass)) {
  33. appContentElement.attr('class', '').addClass(participantsClass);
  34. }
  35. var disconnectedUsers = previousUsersInRoom.diff(currentUsersInRoom);
  36. disconnectedUsers.forEach(function(user) {
  37. console.log('XXX Remove peer', user);
  38. OCA.SpreedMe.webrtc.removePeers(user);
  39. });
  40. previousUsersInRoom = currentUsersInRoom;
  41. });
  42. messageEventSource.listen('message', function(message) {
  43. message = JSON.parse(message);
  44. var peers = self.webrtc.getPeers(message.from, message.roomType);
  45. var peer;
  46. if (message.type === 'offer') {
  47. if (peers.length) {
  48. peers.forEach(function(p) {
  49. if (p.sid === message.sid) {
  50. peer = p;
  51. }
  52. });
  53. }
  54. if (!peer) {
  55. peer = self.webrtc.createPeer({
  56. id: message.from,
  57. sid: message.sid,
  58. type: message.roomType,
  59. enableDataChannels: false,
  60. sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
  61. broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null
  62. });
  63. OCA.SpreedMe.webrtc.emit('createdPeer', peer);
  64. }
  65. peer.handleMessage(message);
  66. } else if(message.type === 'speaking') {
  67. console.log('received speaking event from ', message.payload);
  68. OCA.SpreedMe.speakers.add(message.payload);
  69. } else if(message.type === 'stoppedSpeaking') {
  70. console.log('received stoppedSpeaking event from ', message.payload);
  71. OCA.SpreedMe.speakers.remove(message.payload);
  72. } else if (peers.length) {
  73. peers.forEach(function(peer) {
  74. if (message.sid) {
  75. if (peer.sid === message.sid) {
  76. peer.handleMessage(message);
  77. }
  78. } else {
  79. peer.handleMessage(message);
  80. }
  81. });
  82. }
  83. });
  84. messageEventSource.listen('__internal__', function(data) {
  85. if (data === 'close') {
  86. console.log('signaling connection closed - will reopen');
  87. setTimeout(openEventSource, 0);
  88. }
  89. });
  90. }
  91. function initWebRTC() {
  92. 'use strict';
  93. openEventSource();
  94. webrtc = new SimpleWebRTC({
  95. localVideoEl: 'localVideo',
  96. remoteVideosEl: '',
  97. autoRequestMedia: true,
  98. debug: false,
  99. media: {
  100. audio: true,
  101. video: {
  102. width: { max: 1280 },
  103. height: { max: 720 }
  104. }
  105. },
  106. autoAdjustMic: false,
  107. detectSpeakingEvents: true,
  108. connection: OCA.SpreedMe.XhrConnection,
  109. supportDataChannel: false,
  110. nick: OC.getCurrentUser()['displayName']
  111. });
  112. OCA.SpreedMe.webrtc = webrtc;
  113. var $appContent = $('#app-content');
  114. var spreedListofSpeakers = {};
  115. var latestSpeakerId = null;
  116. OCA.SpreedMe.speakers = {
  117. showStatus: function() {
  118. var data = [];
  119. for (var currentId in spreedListofSpeakers) {
  120. // skip loop if the property is from prototype
  121. if (!spreedListofSpeakers.hasOwnProperty(currentId)) continue;
  122. var currentTime = spreedListofSpeakers[currentId];
  123. var id = currentId.replace('\\', '');
  124. data.push([id, currentTime]);
  125. }
  126. console.table(data);
  127. },
  128. unsanitizeId: function(id) {
  129. return id.replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&")
  130. },
  131. sanitizeId: function(id) {
  132. return id.replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&")
  133. },
  134. getContainerId: function(id) {
  135. if (id === OCA.SpreedMe.XhrConnection.getSessionid()) {
  136. return '#localVideoContainer';
  137. } else {
  138. var sanitizedId = OCA.SpreedMe.speakers.sanitizeId(id);
  139. return '#container_' + sanitizedId + '_type_incoming';
  140. }
  141. },
  142. switchVideoToId: function(id) {
  143. var videoSpeakingElement = $('#video-speaking');
  144. if (latestSpeakerId !== null) {
  145. console.log('move existing promoted user back');
  146. // move old video to new location
  147. var oldSpeakerContainer = $(OCA.SpreedMe.speakers.getContainerId(latestSpeakerId));
  148. oldSpeakerContainer.find('.videoContainer').remove();
  149. videoSpeakingElement.find('video').detach().prependTo(OCA.SpreedMe.speakers.getContainerId(latestSpeakerId));
  150. }
  151. console.log('change promoted speaker after speaking');
  152. // add new user to it
  153. var newSpeakerContainer = $(OCA.SpreedMe.speakers.getContainerId(id));
  154. newSpeakerContainer.find('video').detach().prependTo(videoSpeakingElement);
  155. newSpeakerContainer.prepend($('<div class="videoContainer"></div>'));
  156. latestSpeakerId = id;
  157. },
  158. add: function(id) {
  159. var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
  160. spreedListofSpeakers[sanitizedId] = (new Date()).getTime();
  161. if (latestSpeakerId === id) {
  162. console.log('latest speaker is already promoted');
  163. return;
  164. }
  165. console.log('change promoted speaker after speaking');
  166. OCA.SpreedMe.speakers.switchVideoToId(id);
  167. },
  168. remove: function(id) {
  169. var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
  170. spreedListofSpeakers[sanitizedId] = -1;
  171. if (latestSpeakerId !== id) {
  172. console.log('stopped speaker is not promoted');
  173. return;
  174. }
  175. console.log('change promoted speaker after speakingStopped');
  176. var mostRecentTime = 0,
  177. mostRecentId = null;
  178. for (var currentId in spreedListofSpeakers) {
  179. // skip loop if the property is from prototype
  180. if (!spreedListofSpeakers.hasOwnProperty(currentId)) continue;
  181. var currentTime = spreedListofSpeakers[currentId];
  182. if (currentTime > mostRecentTime) {
  183. mostRecentTime = currentTime;
  184. mostRecentId = currentId
  185. }
  186. }
  187. if (mostRecentId !== null) {
  188. console.log('promoted new speaker');
  189. OCA.SpreedMe.speakers.switchVideoToId(mostRecentId);
  190. } else {
  191. console.log('no recent speaker to promote');
  192. }
  193. }
  194. };
  195. OCA.SpreedMe.webrtc.on('createdPeer', function (peer) {
  196. peer.pc.on('PeerConnectionTrace', function (event) {
  197. console.log('trace', event);
  198. });
  199. });
  200. OCA.SpreedMe.webrtc.on('localMediaError', function(error) {
  201. console.log('Access to microphone & camera failed', error);
  202. var message, messageAdditional;
  203. if (error.name === "NotAllowedError") {
  204. if (error.message && error.message.indexOf("Only secure origins") !== -1) {
  205. message = t('spreed', 'Access to microphone & camera is only possible with HTTPS');
  206. messageAdditional = t('spreed', 'Please adjust your configuration');
  207. } else {
  208. message = t('spreed', 'Access to microphone & camera was denied');
  209. $('#emptycontent p').hide();
  210. }
  211. } else {
  212. message = t('spreed', 'Error while accessing microphone & camera: {error}', {error: error.message || error.name});
  213. $('#emptycontent p').hide();
  214. }
  215. $('#emptycontent h2').text(message);
  216. $('#emptycontent p').text(messageAdditional);
  217. });
  218. OCA.SpreedMe.webrtc.on('joinedRoom', function(name) {
  219. $('#app-content').removeClass('icon-loading');
  220. $('.videoView').removeClass('hidden');
  221. OCA.SpreedMe.app.syncAndSetActiveRoom(name);
  222. });
  223. OCA.SpreedMe.webrtc.on('videoAdded', function(video, peer) {
  224. console.log('video added', peer);
  225. var remotes = document.getElementById('videos');
  226. if (remotes) {
  227. // Indicator for username
  228. var userIndicator = document.createElement('div');
  229. userIndicator.className = 'nameIndicator';
  230. userIndicator.textContent = peer.nick;
  231. // Generic container
  232. var container = document.createElement('div');
  233. container.className = 'videoContainer';
  234. container.id = 'container_' + OCA.SpreedMe.webrtc.getDomId(peer);
  235. container.appendChild(video);
  236. container.appendChild(userIndicator);
  237. video.oncontextmenu = function() {
  238. return false;
  239. };
  240. // show the ice connection state
  241. if (peer && peer.pc) {
  242. peer.pc.on('iceConnectionStateChange', function () {
  243. switch (peer.pc.iceConnectionState) {
  244. case 'checking':
  245. console.log('Connecting to peer...');
  246. break;
  247. case 'connected':
  248. case 'completed': // on caller side
  249. console.log('Connection established.');
  250. break;
  251. case 'disconnected':
  252. // If the peer is still disconnected after 5 seconds
  253. // we close the video connection.
  254. setTimeout(function() {
  255. if(peer.pc.iceConnectionState === 'disconnected') {
  256. OCA.SpreedMe.webrtc.removePeers(peer.id);
  257. }
  258. }, 5000);
  259. console.log('Disconnected.');
  260. break;
  261. case 'failed':
  262. console.log('Connection failed.');
  263. break;
  264. case 'closed':
  265. console.log('Connection closed.');
  266. break;
  267. }
  268. });
  269. }
  270. $(container).prependTo($('#videos'));
  271. }
  272. });
  273. OCA.SpreedMe.webrtc.on('speaking', function(){
  274. console.log('local speaking');
  275. OCA.SpreedMe.webrtc.sendToAll('speaking', OCA.SpreedMe.XhrConnection.getSessionid());
  276. });
  277. OCA.SpreedMe.webrtc.on('stoppedSpeaking', function(){
  278. console.log('local stoppedSpeaking');
  279. OCA.SpreedMe.webrtc.sendToAll('stoppedSpeaking', OCA.SpreedMe.XhrConnection.getSessionid());
  280. });
  281. // a peer was removed
  282. OCA.SpreedMe.webrtc.on('videoRemoved', function(video, peer) {
  283. // a removed peer can't speak anymore ;)
  284. OCA.SpreedMe.speakers.remove(peer);
  285. var remotes = document.getElementById('videos');
  286. var el = document.getElementById(peer ? 'container_' + OCA.SpreedMe.webrtc.getDomId(peer) : 'localScreenContainer');
  287. if (remotes && el) {
  288. remotes.removeChild(el);
  289. }
  290. });
  291. }
  292. OCA.SpreedMe.initWebRTC = initWebRTC;
  293. })(OCA, OC);