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.

desktopsharing.js 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /**
  2. * Indicates that desktop stream is currently in use(for toggle purpose).
  3. * @type {boolean}
  4. */
  5. var isUsingScreenStream = false;
  6. /**
  7. * Indicates that switch stream operation is in progress and prevent from triggering new events.
  8. * @type {boolean}
  9. */
  10. var switchInProgress = false;
  11. /**
  12. * Method used to get screen sharing stream.
  13. *
  14. * @type {function(stream_callback, failure_callback}
  15. */
  16. var obtainDesktopStream = null;
  17. /**
  18. * Flag used to cache desktop sharing enabled state. Do not use directly as it can be <tt>null</tt>.
  19. * @type {null|boolean}
  20. */
  21. var _desktopSharingEnabled = null;
  22. /**
  23. * @returns {boolean} <tt>true</tt> if desktop sharing feature is available and enabled.
  24. */
  25. function isDesktopSharingEnabled() {
  26. if(_desktopSharingEnabled === null){
  27. if(obtainDesktopStream === obtainScreenFromExtension) {
  28. // Parse chrome version
  29. var userAgent = navigator.userAgent.toLowerCase();
  30. // We can assume that user agent is chrome, because it's enforced when 'ext' streaming method is set
  31. var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
  32. console.log("Chrome version" + userAgent, ver);
  33. _desktopSharingEnabled = ver >= 35;
  34. } else {
  35. _desktopSharingEnabled = obtainDesktopStream === obtainWebRTCScreen;
  36. }
  37. }
  38. return _desktopSharingEnabled;
  39. }
  40. /**
  41. * Call this method to toggle desktop sharing feature.
  42. * @param method pass "ext" to use chrome extension for desktop capture(chrome extension required),
  43. * pass "webrtc" to use WebRTC "screen" desktop source('chrome://flags/#enable-usermedia-screen-capture'
  44. * must be enabled), pass any other string or nothing in order to disable this feature completely.
  45. */
  46. function setDesktopSharing(method) {
  47. // Check if we are running chrome
  48. if(!navigator.webkitGetUserMedia){
  49. obtainDesktopStream = null;
  50. console.info("Desktop sharing disabled");
  51. } else if(method == "ext") {
  52. obtainDesktopStream = obtainScreenFromExtension;
  53. console.info("Using Chrome extension for desktop sharing");
  54. } else if(method == "webrtc") {
  55. obtainDesktopStream = obtainWebRTCScreen;
  56. console.info("Using Chrome WebRTC for desktop sharing");
  57. }
  58. // Reset enabled cache
  59. _desktopSharingEnabled = null;
  60. showDesktopSharingButton();
  61. }
  62. function showDesktopSharingButton() {
  63. if(isDesktopSharingEnabled()) {
  64. $('#desktopsharing').css( {display:"inline"} );
  65. } else {
  66. $('#desktopsharing').css( {display:"none"} );
  67. }
  68. }
  69. /**
  70. * Initializes <link rel=chrome-webstore-item /> with extension id set in config.js to support inline installs.
  71. * Host site must be selected as main website of published extension.
  72. */
  73. function initInlineInstalls()
  74. {
  75. $("link[rel=chrome-webstore-item]").attr("href", getWebStoreInstallUrl());
  76. }
  77. /**
  78. * Constructs inline install URL for Chrome desktop streaming extension.
  79. * The 'chromeExtensionId' must be defined in config.js.
  80. * @returns {string}
  81. */
  82. function getWebStoreInstallUrl()
  83. {
  84. return "https://chrome.google.com/webstore/detail/" + config.chromeExtensionId;
  85. }
  86. /*
  87. * Toggles screen sharing.
  88. */
  89. function toggleScreenSharing() {
  90. if (switchInProgress || !obtainDesktopStream) {
  91. console.warn("Switch in progress or no method defined");
  92. return;
  93. }
  94. switchInProgress = true;
  95. // Only the focus is able to set a shared key.
  96. if(!isUsingScreenStream)
  97. {
  98. obtainDesktopStream(
  99. function(stream) {
  100. // We now use screen stream
  101. isUsingScreenStream = true;
  102. // Hook 'ended' event to restore camera when screen stream stops
  103. stream.addEventListener('ended',
  104. function(e) {
  105. if(!switchInProgress && isUsingScreenStream) {
  106. toggleScreenSharing();
  107. }
  108. }
  109. );
  110. newStreamCreated(stream);
  111. },
  112. getSwitchStreamFailed );
  113. } else {
  114. // Disable screen stream
  115. getUserMediaWithConstraints(
  116. ['video'],
  117. function(stream) {
  118. // We are now using camera stream
  119. isUsingScreenStream = false;
  120. newStreamCreated(stream);
  121. },
  122. getSwitchStreamFailed, config.resolution || '360'
  123. );
  124. }
  125. }
  126. function getSwitchStreamFailed(error) {
  127. console.error("Failed to obtain the stream to switch to", error);
  128. switchInProgress = false;
  129. }
  130. function newStreamCreated(stream) {
  131. var oldStream = connection.jingle.localVideo;
  132. change_local_video(stream, !isUsingScreenStream);
  133. var conferenceHandler = getConferenceHandler();
  134. if(conferenceHandler) {
  135. // FIXME: will block switchInProgress on true value in case of exception
  136. conferenceHandler.switchStreams(stream, oldStream, streamSwitchDone);
  137. } else {
  138. // We are done immediately
  139. console.error("No conference handler");
  140. streamSwitchDone();
  141. }
  142. }
  143. function streamSwitchDone() {
  144. //window.setTimeout(
  145. // function() {
  146. switchInProgress = false;
  147. // }, 100
  148. //);
  149. }
  150. /**
  151. * Method obtains desktop stream from WebRTC 'screen' source.
  152. * Flag 'chrome://flags/#enable-usermedia-screen-capture' must be enabled.
  153. */
  154. function obtainWebRTCScreen(streamCallback, failCallback) {
  155. getUserMediaWithConstraints(
  156. ['screen'],
  157. streamCallback,
  158. failCallback
  159. );
  160. }
  161. /**
  162. * Asks Chrome extension to call chooseDesktopMedia and gets chrome 'desktop' stream for returned stream token.
  163. */
  164. function obtainScreenFromExtension(streamCallback, failCallback) {
  165. checkExtInstalled(
  166. function(isInstalled) {
  167. if(isInstalled) {
  168. doGetStreamFromExtension(streamCallback, failCallback);
  169. } else {
  170. chrome.webstore.install(
  171. getWebStoreInstallUrl(),
  172. function(arg) {
  173. console.log("Extension installed successfully", arg);
  174. // We need to reload the page in order to get the access to chrome.runtime
  175. window.location.reload(false);
  176. },
  177. function(arg) {
  178. console.log("Failed to install the extension", arg);
  179. failCallback(arg);
  180. }
  181. );
  182. }
  183. }
  184. );
  185. }
  186. function checkExtInstalled(isInstalledCallback) {
  187. if(!chrome.runtime) {
  188. // No API, so no extension for sure
  189. isInstalledCallback(false);
  190. }
  191. chrome.runtime.sendMessage(
  192. config.chromeExtensionId,
  193. { getVersion: true },
  194. function(response){
  195. if(!response || !response.version) {
  196. // Communication failure - assume that no endpoint exists
  197. console.warn("Extension not installed?: "+chrome.runtime.lastError);
  198. isInstalledCallback(false);
  199. } else {
  200. // Check installed extension version
  201. var extVersion = response.version;
  202. console.log('Extension version is: '+extVersion);
  203. var updateRequired = isUpdateRequired(config.minChromeExtVersion, extVersion);
  204. if(updateRequired) {
  205. alert(
  206. 'Jitsi Desktop Streamer requires update. ' +
  207. 'Changes will take effect after next Chrome restart.' );
  208. }
  209. isInstalledCallback(!updateRequired);
  210. }
  211. }
  212. );
  213. }
  214. /**
  215. * Checks whether extension update is required.
  216. * @param minVersion minimal required version
  217. * @param extVersion current extension version
  218. * @returns {boolean}
  219. */
  220. function isUpdateRequired(minVersion, extVersion)
  221. {
  222. try
  223. {
  224. var s1 = minVersion.split('.');
  225. var s2 = extVersion.split('.');
  226. var len = Math.max(s1.length, s2.length);
  227. for(var i = 0; i < len; i++)
  228. {
  229. var n1=0,n2=0;
  230. if(i < s1.length)
  231. n1 = parseInt(s1[i]);
  232. if(i < s2.length)
  233. n2 = parseInt(s2[i]);
  234. if(isNaN(n1) || isNaN(n2))
  235. {
  236. return true;
  237. }
  238. else if(n1 !== n2)
  239. {
  240. return n1 > n2;
  241. }
  242. }
  243. // will happen if boths version has identical numbers in
  244. // their components (even if one of them is longer, has more components)
  245. return false;
  246. }
  247. catch(e)
  248. {
  249. console.error("Failed to parse extension version", e);
  250. return true;
  251. }
  252. }
  253. function doGetStreamFromExtension(streamCallback, failCallback) {
  254. // Sends 'getStream' msg to the extension. Extension id must be defined in the config.
  255. chrome.runtime.sendMessage(
  256. config.chromeExtensionId,
  257. { getStream: true},
  258. function(response) {
  259. if(!response) {
  260. failCallback(chrome.runtime.lastError);
  261. return;
  262. }
  263. console.log("Response from extension: "+response);
  264. if(response.streamId) {
  265. getUserMediaWithConstraints(
  266. ['desktop'],
  267. function(stream) {
  268. streamCallback(stream);
  269. },
  270. failCallback,
  271. null, null, null,
  272. response.streamId);
  273. } else {
  274. failCallback("Extension failed to get the stream");
  275. }
  276. }
  277. );
  278. }