Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

LargeVideo.js 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. var Avatar = require("../avatar/Avatar");
  2. var RTCBrowserType = require("../../RTC/RTCBrowserType");
  3. var UIUtil = require("../util/UIUtil");
  4. var UIEvents = require("../../../service/UI/UIEvents");
  5. var xmpp = require("../../xmpp/xmpp");
  6. // FIXME: With Temasys we have to re-select everytime
  7. //var video = $('#largeVideo');
  8. var currentVideoWidth = null;
  9. var currentVideoHeight = null;
  10. // By default we use camera
  11. var getVideoSize = getCameraVideoSize;
  12. var getVideoPosition = getCameraVideoPosition;
  13. var currentSmallVideo = null;
  14. var oldSmallVideo = null;
  15. /**
  16. * Sets the size and position of the given video element.
  17. *
  18. * @param video the video element to position
  19. * @param width the desired video width
  20. * @param height the desired video height
  21. * @param horizontalIndent the left and right indent
  22. * @param verticalIndent the top and bottom indent
  23. */
  24. function positionVideo(video,
  25. width,
  26. height,
  27. horizontalIndent,
  28. verticalIndent,
  29. animate) {
  30. if(animate)
  31. {
  32. video.animate({
  33. width: width,
  34. height: height,
  35. top: verticalIndent,
  36. bottom: verticalIndent,
  37. left: horizontalIndent,
  38. right: horizontalIndent
  39. },
  40. {
  41. queue: false,
  42. duration: 500
  43. });
  44. }
  45. else
  46. {
  47. video.width(width);
  48. video.height(height);
  49. video.css({ top: verticalIndent + 'px',
  50. bottom: verticalIndent + 'px',
  51. left: horizontalIndent + 'px',
  52. right: horizontalIndent + 'px'});
  53. }
  54. }
  55. /**
  56. * Returns an array of the video dimensions, so that it keeps it's aspect
  57. * ratio and fits available area with it's larger dimension. This method
  58. * ensures that whole video will be visible and can leave empty areas.
  59. *
  60. * @return an array with 2 elements, the video width and the video height
  61. */
  62. function getDesktopVideoSize(videoWidth,
  63. videoHeight,
  64. videoSpaceWidth,
  65. videoSpaceHeight) {
  66. if (!videoWidth)
  67. videoWidth = currentVideoWidth;
  68. if (!videoHeight)
  69. videoHeight = currentVideoHeight;
  70. var aspectRatio = videoWidth / videoHeight;
  71. var availableWidth = Math.max(videoWidth, videoSpaceWidth);
  72. var availableHeight = Math.max(videoHeight, videoSpaceHeight);
  73. videoSpaceHeight -= $('#remoteVideos').outerHeight();
  74. if (availableWidth / aspectRatio >= videoSpaceHeight)
  75. {
  76. availableHeight = videoSpaceHeight;
  77. availableWidth = availableHeight * aspectRatio;
  78. }
  79. if (availableHeight * aspectRatio >= videoSpaceWidth)
  80. {
  81. availableWidth = videoSpaceWidth;
  82. availableHeight = availableWidth / aspectRatio;
  83. }
  84. return [availableWidth, availableHeight];
  85. }
  86. /**
  87. * Returns an array of the video horizontal and vertical indents,
  88. * so that if fits its parent.
  89. *
  90. * @return an array with 2 elements, the horizontal indent and the vertical
  91. * indent
  92. */
  93. function getCameraVideoPosition(videoWidth,
  94. videoHeight,
  95. videoSpaceWidth,
  96. videoSpaceHeight) {
  97. // Parent height isn't completely calculated when we position the video in
  98. // full screen mode and this is why we use the screen height in this case.
  99. // Need to think it further at some point and implement it properly.
  100. var isFullScreen = document.fullScreen ||
  101. document.mozFullScreen ||
  102. document.webkitIsFullScreen;
  103. if (isFullScreen)
  104. videoSpaceHeight = window.innerHeight;
  105. var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
  106. var verticalIndent = (videoSpaceHeight - videoHeight) / 2;
  107. return [horizontalIndent, verticalIndent];
  108. }
  109. /**
  110. * Returns an array of the video horizontal and vertical indents.
  111. * Centers horizontally and top aligns vertically.
  112. *
  113. * @return an array with 2 elements, the horizontal indent and the vertical
  114. * indent
  115. */
  116. function getDesktopVideoPosition(videoWidth,
  117. videoHeight,
  118. videoSpaceWidth,
  119. videoSpaceHeight) {
  120. var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
  121. var verticalIndent = 0;// Top aligned
  122. return [horizontalIndent, verticalIndent];
  123. }
  124. /**
  125. * Returns an array of the video dimensions, so that it covers the screen.
  126. * It leaves no empty areas, but some parts of the video might not be visible.
  127. *
  128. * @return an array with 2 elements, the video width and the video height
  129. */
  130. function getCameraVideoSize(videoWidth,
  131. videoHeight,
  132. videoSpaceWidth,
  133. videoSpaceHeight) {
  134. if (!videoWidth)
  135. videoWidth = currentVideoWidth;
  136. if (!videoHeight)
  137. videoHeight = currentVideoHeight;
  138. var aspectRatio = videoWidth / videoHeight;
  139. var availableWidth = Math.max(videoWidth, videoSpaceWidth);
  140. var availableHeight = Math.max(videoHeight, videoSpaceHeight);
  141. if (availableWidth / aspectRatio < videoSpaceHeight) {
  142. availableHeight = videoSpaceHeight;
  143. availableWidth = availableHeight * aspectRatio;
  144. }
  145. if (availableHeight * aspectRatio < videoSpaceWidth) {
  146. availableWidth = videoSpaceWidth;
  147. availableHeight = availableWidth / aspectRatio;
  148. }
  149. return [availableWidth, availableHeight];
  150. }
  151. /**
  152. * Updates the src of the active speaker avatar
  153. * @param jid of the current active speaker
  154. */
  155. function updateActiveSpeakerAvatarSrc() {
  156. var avatar = $("#activeSpeakerAvatar")[0];
  157. var jid = currentSmallVideo.peerJid;
  158. var url = Avatar.getGravatarUrl(jid);
  159. if(avatar.src === url)
  160. return;
  161. var isMuted = null;
  162. if(!LargeVideo.VideoLayout.isInLastN(currentSmallVideo.resourceJid)) {
  163. isMuted = true;
  164. }
  165. else
  166. {
  167. isMuted = APP.RTC.isVideoMuted(jid);
  168. }
  169. if (jid && isMuted !== null) {
  170. avatar.src = url;
  171. $("#largeVideo").css("visibility", isMuted ? "hidden" : "visible");
  172. currentSmallVideo.showAvatar(isMuted);
  173. }
  174. }
  175. function changeVideo(isVisible) {
  176. updateActiveSpeakerAvatarSrc();
  177. APP.RTC.setVideoSrc($('#largeVideo')[0], currentSmallVideo.getSrc());
  178. var videoTransform = document.getElementById('largeVideo')
  179. .style.webkitTransform;
  180. var flipX = currentSmallVideo.flipX;
  181. if (flipX && videoTransform !== 'scaleX(-1)') {
  182. document.getElementById('largeVideo').style.webkitTransform
  183. = "scaleX(-1)";
  184. }
  185. else if (!flipX && videoTransform === 'scaleX(-1)') {
  186. document.getElementById('largeVideo').style.webkitTransform
  187. = "none";
  188. }
  189. var isDesktop = APP.RTC.isVideoSrcDesktop(currentSmallVideo.peerJid);
  190. // Change the way we'll be measuring and positioning large video
  191. getVideoSize = isDesktop
  192. ? getDesktopVideoSize
  193. : getCameraVideoSize;
  194. getVideoPosition = isDesktop
  195. ? getDesktopVideoPosition
  196. : getCameraVideoPosition;
  197. // Only if the large video is currently visible.
  198. // Disable previous dominant speaker video.
  199. if (oldSmallVideo) {
  200. oldSmallVideo.enableDominantSpeaker(false);
  201. }
  202. // Enable new dominant speaker in the remote videos section.
  203. if (currentSmallVideo) {
  204. currentSmallVideo.enableDominantSpeaker(true);
  205. }
  206. if (isVisible) {
  207. $('#largeVideo').fadeIn(300);
  208. }
  209. if(oldSmallVideo)
  210. oldSmallVideo.showAvatar();
  211. }
  212. var LargeVideo = {
  213. init: function (VideoLayout, emitter) {
  214. this.VideoLayout = VideoLayout;
  215. this.eventEmitter = emitter;
  216. var self = this;
  217. // Listen for large video size updates
  218. var largeVideo = $('#largeVideo')[0];
  219. var onplaying = function (arg1, arg2, arg3) {
  220. // re-select
  221. if (RTCBrowserType.isTemasysPluginUsed())
  222. largeVideo = $('#largeVideo')[0];
  223. currentVideoWidth = largeVideo.videoWidth;
  224. currentVideoHeight = largeVideo.videoHeight;
  225. self.position(currentVideoWidth, currentVideoHeight);
  226. };
  227. largeVideo.onplaying = onplaying;
  228. },
  229. /**
  230. * Indicates if the large video is currently visible.
  231. *
  232. * @return <tt>true</tt> if visible, <tt>false</tt> - otherwise
  233. */
  234. isLargeVideoVisible: function() {
  235. return $('#largeVideo').is(':visible');
  236. },
  237. /**
  238. * Updates the large video with the given new video source.
  239. */
  240. updateLargeVideo: function(resourceJid, forceUpdate) {
  241. var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid);
  242. console.log('hover in ' + resourceJid + ', video: ', newSmallVideo);
  243. if ((currentSmallVideo && currentSmallVideo.resourceJid !== resourceJid)
  244. || forceUpdate) {
  245. $('#activeSpeaker').css('visibility', 'hidden');
  246. if(currentSmallVideo) {
  247. oldSmallVideo = currentSmallVideo;
  248. } else {
  249. oldSmallVideo = null;
  250. }
  251. currentSmallVideo = newSmallVideo;
  252. var oldJid = null;
  253. if(oldSmallVideo)
  254. oldJid = oldSmallVideo.peerJid;
  255. if (oldJid !== resourceJid) {
  256. // we want the notification to trigger even if userJid is undefined,
  257. // or null.
  258. this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT,
  259. resourceJid);
  260. }
  261. $('#largeVideo').fadeOut(300,
  262. changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible()));
  263. } else {
  264. if(currentSmallVideo) {
  265. currentSmallVideo.showAvatar();
  266. }
  267. }
  268. },
  269. /**
  270. * Shows/hides the large video.
  271. */
  272. setLargeVideoVisible: function(isVisible) {
  273. if (isVisible) {
  274. $('#largeVideo').css({visibility: 'visible'});
  275. $('.watermark').css({visibility: 'visible'});
  276. if(currentSmallVideo)
  277. currentSmallVideo.enableDominantSpeaker(true);
  278. }
  279. else {
  280. $('#largeVideo').css({visibility: 'hidden'});
  281. $('#activeSpeaker').css('visibility', 'hidden');
  282. $('.watermark').css({visibility: 'hidden'});
  283. if(currentSmallVideo)
  284. currentSmallVideo.enableDominantSpeaker(false);
  285. }
  286. },
  287. onVideoTypeChanged: function (jid) {
  288. if(jid && currentSmallVideo && jid === currentSmallVideo.peerJid)
  289. {
  290. var isDesktop = APP.RTC.isVideoSrcDesktop(jid);
  291. getVideoSize = isDesktop
  292. ? getDesktopVideoSize
  293. : getCameraVideoSize;
  294. getVideoPosition = isDesktop
  295. ? getDesktopVideoPosition
  296. : getCameraVideoPosition;
  297. this.position(null, null);
  298. }
  299. },
  300. /**
  301. * Positions the large video.
  302. *
  303. * @param videoWidth the stream video width
  304. * @param videoHeight the stream video height
  305. */
  306. position: function (videoWidth, videoHeight,
  307. videoSpaceWidth, videoSpaceHeight, animate) {
  308. if(!videoSpaceWidth)
  309. videoSpaceWidth = $('#videospace').width();
  310. if(!videoSpaceHeight)
  311. videoSpaceHeight = window.innerHeight;
  312. var videoSize = getVideoSize(videoWidth,
  313. videoHeight,
  314. videoSpaceWidth,
  315. videoSpaceHeight);
  316. var largeVideoWidth = videoSize[0];
  317. var largeVideoHeight = videoSize[1];
  318. var videoPosition = getVideoPosition(largeVideoWidth,
  319. largeVideoHeight,
  320. videoSpaceWidth,
  321. videoSpaceHeight);
  322. var horizontalIndent = videoPosition[0];
  323. var verticalIndent = videoPosition[1];
  324. positionVideo($('#largeVideo'),
  325. largeVideoWidth,
  326. largeVideoHeight,
  327. horizontalIndent, verticalIndent, animate);
  328. },
  329. isLargeVideoOnTop: function () {
  330. var Etherpad = require("../etherpad/Etherpad");
  331. var Prezi = require("../prezi/Prezi");
  332. return !Prezi.isPresentationVisible() && !Etherpad.isVisible();
  333. },
  334. resize: function (animate, isVisible, completeFunction) {
  335. var availableHeight = window.innerHeight;
  336. var availableWidth = UIUtil.getAvailableVideoWidth(isVisible);
  337. if (availableWidth < 0 || availableHeight < 0) return;
  338. var avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
  339. var top = availableHeight / 2 - avatarSize / 4 * 3;
  340. $('#activeSpeaker').css('top', top);
  341. if(animate)
  342. {
  343. $('#videospace').animate({
  344. right: window.innerWidth - availableWidth,
  345. width: availableWidth,
  346. height: availableHeight
  347. },
  348. {
  349. queue: false,
  350. duration: 500,
  351. complete: completeFunction
  352. });
  353. $('#largeVideoContainer').animate({
  354. width: availableWidth,
  355. height: availableHeight
  356. },
  357. {
  358. queue: false,
  359. duration: 500
  360. });
  361. }
  362. else
  363. {
  364. $('#videospace').width(availableWidth);
  365. $('#videospace').height(availableHeight);
  366. $('#largeVideoContainer').width(availableWidth);
  367. $('#largeVideoContainer').height(availableHeight);
  368. }
  369. return [availableWidth, availableHeight];
  370. },
  371. resizeVideoAreaAnimated: function (isVisible, completeFunction) {
  372. var size = this.resize(true, isVisible, completeFunction);
  373. this.position(null, null, size[0], size[1], true);
  374. },
  375. getResourceJid: function () {
  376. if(!currentSmallVideo)
  377. return null;
  378. return currentSmallVideo.resourceJid;
  379. },
  380. updateAvatar: function (resourceJid) {
  381. if (resourceJid === this.getResourceJid()) {
  382. updateActiveSpeakerAvatarSrc();
  383. }
  384. },
  385. showAvatar: function (resourceJid, show) {
  386. if(this.getResourceJid() === resourceJid
  387. && LargeVideo.isLargeVideoOnTop())
  388. {
  389. $("#largeVideo").css("visibility", show ? "hidden" : "visible");
  390. $('#activeSpeaker').css("visibility", show ? "visible" : "hidden");
  391. return true;
  392. }
  393. return false;
  394. }
  395. }
  396. module.exports = LargeVideo;