diff --git a/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js b/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js index 761e383ede9eafae27c72990146aaadfc41a26e2..93dc3185fd9e9c9235fcd9ccc32e654019f5ef87 100755 --- a/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js +++ b/bigbluebutton-html5/imports/api/screenshare/client/bridge/kurento.js @@ -63,15 +63,80 @@ export default class KurentoScreenshareBridge { return normalizedError; } - async kurentoWatchVideo() { + static playElement(screenshareMediaElement) { + const mediaTagPlayed = () => { + logger.info({ + logCode: 'screenshare_media_play_success', + }, 'Screenshare media played successfully'); + }; + + if (screenshareMediaElement.paused) { + // Tag isn't playing yet. Play it. + screenshareMediaElement.play() + .then(mediaTagPlayed) + .catch((error) => { + // NotAllowedError equals autoplay issues, fire autoplay handling event. + // This will be handled in the screenshare react component. + if (error.name === 'NotAllowedError') { + logger.error({ + logCode: 'screenshare_error_autoplay', + extraInfo: { errorName: error.name }, + }, 'Screenshare play failed due to autoplay error'); + const tagFailedEvent = new CustomEvent('screensharePlayFailed', + { detail: { mediaElement: screenshareMediaElement } }); + window.dispatchEvent(tagFailedEvent); + } else { + // Tag failed for reasons other than autoplay. Log the error and + // try playing again a few times until it works or fails for good + const played = playAndRetry(screenshareMediaElement); + if (!played) { + logger.error({ + logCode: 'screenshare_error_media_play_failed', + extraInfo: { errorName: error.name }, + }, `Screenshare media play failed due to ${error.name}`); + } else { + mediaTagPlayed(); + } + } + }); + } else { + // Media tag is already playing, so log a success. This is really a + // logging fallback for a case that shouldn't happen. But if it does + // (ie someone re-enables the autoPlay prop in the element), then it + // means the stream is playing properly and it'll be logged. + mediaTagPlayed(); + } + }; + + static screenshareElementLoadAndPlay(stream, element, muted) { + element.muted = muted; + element.pause(); + element.srcObject = stream; + KurentoScreenshareBridge.playElement(element); + } + + kurentoViewLocalPreview() { + const screenshareMediaElement = document.getElementById(SCREENSHARE_VIDEO_TAG); + const { webRtcPeer } = window.kurentoManager.kurentoScreenshare; + + if (webRtcPeer) { + const stream = webRtcPeer.getLocalStream(); + KurentoScreenshareBridge.screenshareElementLoadAndPlay(stream, screenshareMediaElement, true); + } + } + + async kurentoViewScreen() { + const screenshareMediaElement = document.getElementById(SCREENSHARE_VIDEO_TAG); let iceServers = []; let started = false; try { iceServers = await fetchWebRTCMappedStunTurnServers(getSessionToken()); } catch (error) { - logger.error({ logCode: 'screenshare_viwer_fetchstunturninfo_error', extraInfo: { error } }, - 'Screenshare bridge failed to fetch STUN/TURN info, using default'); + logger.error({ + logCode: 'screenshare_viewer_fetchstunturninfo_error', + extraInfo: { error } + }, 'Screenshare bridge failed to fetch STUN/TURN info, using default'); iceServers = getMappedFallbackStun(); } finally { const options = { @@ -81,52 +146,6 @@ export default class KurentoScreenshareBridge { userName: getUsername(), }; - const screenshareTag = document.getElementById(SCREENSHARE_VIDEO_TAG); - - const playElement = () => { - const mediaTagPlayed = () => { - logger.info({ - logCode: 'screenshare_viewer_media_play_success', - }, 'Screenshare viewer media played successfully'); - }; - if (screenshareTag.paused) { - // Tag isn't playing yet. Play it. - screenshareTag.play() - .then(mediaTagPlayed) - .catch((error) => { - // NotAllowedError equals autoplay issues, fire autoplay handling event. - // This will be handled in the screenshare react component. - if (error.name === 'NotAllowedError') { - logger.error({ - logCode: 'screenshare_viewer_error_autoplay', - extraInfo: { errorName: error.name }, - }, 'Screenshare viewer play failed due to autoplay error'); - const tagFailedEvent = new CustomEvent('screensharePlayFailed', - { detail: { mediaElement: screenshareTag } }); - window.dispatchEvent(tagFailedEvent); - } else { - // Tag failed for reasons other than autoplay. Log the error and - // try playing again a few times until it works or fails for good - const played = playAndRetry(screenshareTag); - if (!played) { - logger.error({ - logCode: 'screenshare_viewer_error_media_play_failed', - extraInfo: { errorName: error.name }, - }, `Screenshare viewer media play failed due to ${error.name}`); - } else { - mediaTagPlayed(); - } - } - }); - } else { - // Media tag is already playing, so log a success. This is really a - // logging fallback for a case that shouldn't happen. But if it does - // (ie someone re-enables the autoPlay prop in the element), then it - // means the stream is playing properly and it'll be logged. - mediaTagPlayed(); - } - }; - const onFail = (error) => { KurentoScreenshareBridge.handleViewerFailure(error, started); }; @@ -139,10 +158,11 @@ export default class KurentoScreenshareBridge { const { webRtcPeer } = window.kurentoManager.kurentoVideo; if (webRtcPeer) { const stream = webRtcPeer.getRemoteStream(); - screenshareTag.muted = true; - screenshareTag.pause(); - screenshareTag.srcObject = stream; - playElement(); + KurentoScreenshareBridge.screenshareElementLoadAndPlay( + stream, + screenshareMediaElement, + true, + ); } }; diff --git a/bigbluebutton-html5/imports/ui/components/screenshare/service.js b/bigbluebutton-html5/imports/ui/components/screenshare/service.js index 51340cba8a3800d399a99b7b618e6fccb54f4a13..33c839df91e293df73f74d8629e34fed06479234 100644 --- a/bigbluebutton-html5/imports/ui/components/screenshare/service.js +++ b/bigbluebutton-html5/imports/ui/components/screenshare/service.js @@ -7,6 +7,7 @@ import { tryGenerateIceCandidates } from '/imports/utils/safari-webrtc'; import { stopWatching } from '/imports/ui/components/external-video-player/service'; import Meetings from '/imports/api/meetings'; import Auth from '/imports/ui/services/auth'; +import UserListService from '/imports/ui/components/user-list/service'; // when the meeting information has been updated check to see if it was // screensharing. If it has changed either trigger a call to receive video @@ -29,15 +30,21 @@ const presenterScreenshareHasEnded = () => { KurentoBridge.kurentoExitVideo(); }; +const viewScreenshare = () => { + const amIPresenter = UserListService.isUserPresenter(Auth.userID); + if (!amIPresenter) { + KurentoBridge.kurentoViewScreen(); + } else { + KurentoBridge.kurentoViewLocalPreview(); + } +}; + // if remote screenshare has been started connect and display the video stream const presenterScreenshareHasStarted = () => { - // KurentoBridge.kurentoWatchVideo: references a function in the global - // namespace inside kurento-extension.js that we load dynamically - // WebRTC restrictions may need a capture device permission to release // useful ICE candidates on recvonly/no-gUM peers tryGenerateIceCandidates().then(() => { - KurentoBridge.kurentoWatchVideo(); + viewScreenshare(); }).catch((error) => { logger.error({ logCode: 'screenshare_no_valid_candidate_gum_failure', @@ -47,7 +54,7 @@ const presenterScreenshareHasStarted = () => { }, }, `Forced gUM to release additional ICE candidates failed due to ${error.name}.`); // The fallback gUM failed. Try it anyways and hope for the best. - KurentoBridge.kurentoWatchVideo(); + viewScreenshare(); }); }; diff --git a/bigbluebutton-html5/imports/ui/components/user-list/service.js b/bigbluebutton-html5/imports/ui/components/user-list/service.js index 9782e2a15fdbcf55b10cfcfa214e9dc116d9d096..11b31ee10d6fd2077701cc21103c14cabd562da1 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/service.js +++ b/bigbluebutton-html5/imports/ui/components/user-list/service.js @@ -506,6 +506,12 @@ const sortUsersByLastName = (a, b) => { return 0; }; +const isUserPresenter = (userId) => { + const user = Users.findOne({ userId }, + { fields: { presenter: 1 } }); + return user ? user.presenter : false; +}; + export const getUserNamesLink = (docTitle, fnSortedLabel, lnSortedLabel) => { const mimeType = 'text/plain'; const userNamesObj = getUsers() @@ -567,4 +573,5 @@ export default { hasPrivateChatBetweenUsers, toggleUserLock, requestUserInformation, + isUserPresenter, }; diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/service.js b/bigbluebutton-html5/imports/ui/components/video-provider/service.js index b7815a52a053636e948084d60cec6d27d3dc9648..3bc3985e45ddfb9b9edebba997299935d8dc9284 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/service.js +++ b/bigbluebutton-html5/imports/ui/components/video-provider/service.js @@ -34,17 +34,11 @@ const { const TOKEN = '_'; class VideoService { - static isUserPresenter(userId) { - const user = Users.findOne({ userId }, - { fields: { presenter: 1 } }); - return user ? user.presenter : false; - } - // Paginated streams: sort with following priority: local -> presenter -> alphabetic static sortPaginatedStreams(s1, s2) { - if (VideoService.isUserPresenter(s1.userId) && !VideoService.isUserPresenter(s2.userId)) { + if (UserListService.isUserPresenter(s1.userId) && !UserListService.isUserPresenter(s2.userId)) { return -1; - } else if (VideoService.isUserPresenter(s2.userId) && !VideoService.isUserPresenter(s1.userId)) { + } else if (UserListService.isUserPresenter(s2.userId) && !UserListService.isUserPresenter(s1.userId)) { return 1; } else { return UserListService.sortUsersByName(s1, s2);