2017-09-09 01:00:00 +08:00
|
|
|
import Auth from '/imports/ui/services/auth';
|
|
|
|
import BridgeService from './service';
|
2020-05-21 12:20:46 +08:00
|
|
|
import { fetchWebRTCMappedStunTurnServers, getMappedFallbackStun } from '/imports/utils/fetchStunTurnServers';
|
2019-09-07 04:54:48 +08:00
|
|
|
import playAndRetry from '/imports/utils/mediaElementPlayRetry';
|
2018-07-27 02:26:56 +08:00
|
|
|
import logger from '/imports/startup/client/logger';
|
2017-09-09 01:00:00 +08:00
|
|
|
|
2018-07-10 05:29:27 +08:00
|
|
|
const SFU_CONFIG = Meteor.settings.public.kurento;
|
|
|
|
const SFU_URL = SFU_CONFIG.wsUrl;
|
|
|
|
const CHROME_DEFAULT_EXTENSION_KEY = SFU_CONFIG.chromeDefaultExtensionKey;
|
|
|
|
const CHROME_CUSTOM_EXTENSION_KEY = SFU_CONFIG.chromeExtensionKey;
|
2020-05-15 00:36:03 +08:00
|
|
|
const CHROME_SCREENSHARE_SOURCES = SFU_CONFIG.screenshare.chromeScreenshareSources;
|
|
|
|
const FIREFOX_SCREENSHARE_SOURCE = SFU_CONFIG.screenshare.firefoxScreenshareSource;
|
2018-07-10 05:29:27 +08:00
|
|
|
const SCREENSHARE_VIDEO_TAG = 'screenshareVideo';
|
2017-11-18 02:55:59 +08:00
|
|
|
|
2018-04-27 06:52:46 +08:00
|
|
|
const CHROME_EXTENSION_KEY = CHROME_CUSTOM_EXTENSION_KEY === 'KEY' ? CHROME_DEFAULT_EXTENSION_KEY : CHROME_CUSTOM_EXTENSION_KEY;
|
2017-09-09 01:00:00 +08:00
|
|
|
|
2018-04-27 06:52:46 +08:00
|
|
|
const getUserId = () => Auth.userID;
|
2017-09-09 01:00:00 +08:00
|
|
|
|
2018-04-27 06:52:46 +08:00
|
|
|
const getMeetingId = () => Auth.meetingID;
|
|
|
|
|
2019-09-11 21:17:23 +08:00
|
|
|
const getUsername = () => Auth.fullname;
|
|
|
|
|
2018-07-10 05:29:27 +08:00
|
|
|
const getSessionToken = () => Auth.sessionToken;
|
|
|
|
|
2017-09-09 01:00:00 +08:00
|
|
|
export default class KurentoScreenshareBridge {
|
2019-09-07 02:58:22 +08:00
|
|
|
static normalizeError(error = {}) {
|
2019-09-11 00:20:40 +08:00
|
|
|
const errorMessage = error.name || error.message || error.reason || 'Unknown error';
|
|
|
|
const errorCode = error.code || undefined;
|
2019-09-07 02:58:22 +08:00
|
|
|
const errorReason = error.reason || error.id || 'Undefined reason';
|
|
|
|
|
|
|
|
return { errorMessage, errorCode, errorReason };
|
|
|
|
}
|
|
|
|
|
2019-09-11 00:20:40 +08:00
|
|
|
static handlePresenterFailure(error, started = false) {
|
2019-09-07 02:58:22 +08:00
|
|
|
const normalizedError = KurentoScreenshareBridge.normalizeError(error);
|
2019-09-11 00:20:40 +08:00
|
|
|
if (!started) {
|
|
|
|
logger.error({
|
|
|
|
logCode: 'screenshare_presenter_error_failed_to_connect',
|
|
|
|
extraInfo: { ...normalizedError },
|
|
|
|
}, `Screenshare presenter failed when trying to start due to ${normalizedError.errorMessage}`);
|
|
|
|
} else {
|
|
|
|
logger.error({
|
|
|
|
logCode: 'screenshare_presenter_error_failed_after_success',
|
|
|
|
extraInfo: { ...normalizedError },
|
|
|
|
}, `Screenshare presenter failed during working session due to ${normalizedError.errorMessage}`);
|
|
|
|
}
|
2019-09-07 02:58:22 +08:00
|
|
|
return normalizedError;
|
|
|
|
}
|
|
|
|
|
2019-09-11 00:20:40 +08:00
|
|
|
static handleViewerFailure(error, started = false) {
|
2019-09-07 02:58:22 +08:00
|
|
|
const normalizedError = KurentoScreenshareBridge.normalizeError(error);
|
2019-09-11 00:20:40 +08:00
|
|
|
if (!started) {
|
|
|
|
logger.error({
|
|
|
|
logCode: 'screenshare_viewer_error_failed_to_connect',
|
|
|
|
extraInfo: { ...normalizedError },
|
|
|
|
}, `Screenshare viewer failed when trying to start due to ${normalizedError.errorMessage}`);
|
|
|
|
} else {
|
|
|
|
logger.error({
|
|
|
|
logCode: 'screenshare_viewer_error_failed_after_success',
|
|
|
|
extraInfo: { ...normalizedError },
|
|
|
|
}, `Screenshare viewer failed during working session due to ${normalizedError.errorMessage}`);
|
|
|
|
}
|
2019-09-07 02:58:22 +08:00
|
|
|
return normalizedError;
|
|
|
|
}
|
|
|
|
|
2020-08-27 07:30:53 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-20 08:36:13 +08:00
|
|
|
async kurentoViewScreen(hasAudio) {
|
2020-08-27 07:30:53 +08:00
|
|
|
const screenshareMediaElement = document.getElementById(SCREENSHARE_VIDEO_TAG);
|
2018-07-10 05:29:27 +08:00
|
|
|
let iceServers = [];
|
2019-09-11 00:20:40 +08:00
|
|
|
let started = false;
|
2018-07-10 05:29:27 +08:00
|
|
|
|
|
|
|
try {
|
|
|
|
iceServers = await fetchWebRTCMappedStunTurnServers(getSessionToken());
|
|
|
|
} catch (error) {
|
2020-08-27 07:37:12 +08:00
|
|
|
logger.error({
|
|
|
|
logCode: 'screenshare_viewer_fetchstunturninfo_error',
|
|
|
|
extraInfo: { error }
|
|
|
|
}, 'Screenshare bridge failed to fetch STUN/TURN info, using default');
|
2020-05-21 12:20:46 +08:00
|
|
|
iceServers = getMappedFallbackStun();
|
2018-07-10 05:29:27 +08:00
|
|
|
} finally {
|
|
|
|
const options = {
|
2019-01-08 00:54:37 +08:00
|
|
|
wsUrl: Auth.authenticateURL(SFU_URL),
|
2018-07-10 05:29:27 +08:00
|
|
|
iceServers,
|
2019-07-16 04:59:00 +08:00
|
|
|
logger,
|
2019-03-28 06:18:40 +08:00
|
|
|
userName: getUsername(),
|
2018-07-10 05:29:27 +08:00
|
|
|
};
|
|
|
|
|
2019-09-07 02:58:22 +08:00
|
|
|
const onFail = (error) => {
|
2019-09-11 00:20:40 +08:00
|
|
|
KurentoScreenshareBridge.handleViewerFailure(error, started);
|
2019-09-07 02:58:22 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// Callback for the kurento-extension.js script. It's called when the whole
|
|
|
|
// negotiation with SFU is successful. This will load the stream into the
|
|
|
|
// screenshare media element and play it manually.
|
2019-08-03 05:32:42 +08:00
|
|
|
const onSuccess = () => {
|
2019-09-11 00:20:40 +08:00
|
|
|
started = true;
|
2019-08-03 05:32:42 +08:00
|
|
|
const { webRtcPeer } = window.kurentoManager.kurentoVideo;
|
|
|
|
if (webRtcPeer) {
|
|
|
|
const stream = webRtcPeer.getRemoteStream();
|
2020-08-27 07:30:53 +08:00
|
|
|
KurentoScreenshareBridge.screenshareElementLoadAndPlay(
|
|
|
|
stream,
|
|
|
|
screenshareMediaElement,
|
2020-08-20 08:36:13 +08:00
|
|
|
hasAudio,
|
2020-08-27 07:30:53 +08:00
|
|
|
);
|
2019-08-03 05:32:42 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-07-10 05:29:27 +08:00
|
|
|
window.kurentoWatchVideo(
|
|
|
|
SCREENSHARE_VIDEO_TAG,
|
|
|
|
BridgeService.getConferenceBridge(),
|
|
|
|
getUserId(),
|
|
|
|
getMeetingId(),
|
2019-09-07 02:58:22 +08:00
|
|
|
onFail,
|
2019-08-03 05:32:42 +08:00
|
|
|
onSuccess,
|
2018-08-23 23:49:16 +08:00
|
|
|
options,
|
2020-08-20 08:36:13 +08:00
|
|
|
hasAudio,
|
2018-07-10 05:29:27 +08:00
|
|
|
);
|
2018-08-23 23:49:16 +08:00
|
|
|
}
|
2017-09-09 01:00:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
kurentoExitVideo() {
|
|
|
|
window.kurentoExitVideo();
|
|
|
|
}
|
2017-09-13 04:47:06 +08:00
|
|
|
|
2020-05-15 00:36:03 +08:00
|
|
|
async kurentoShareScreen(onFail, stream) {
|
2018-07-10 05:29:27 +08:00
|
|
|
let iceServers = [];
|
|
|
|
try {
|
|
|
|
iceServers = await fetchWebRTCMappedStunTurnServers(getSessionToken());
|
|
|
|
} catch (error) {
|
2019-09-07 02:58:22 +08:00
|
|
|
logger.error({ logCode: 'screenshare_presenter_fetchstunturninfo_error' },
|
2020-05-15 00:36:03 +08:00
|
|
|
|
2019-07-16 04:59:00 +08:00
|
|
|
'Screenshare bridge failed to fetch STUN/TURN info, using default');
|
2020-05-21 12:20:46 +08:00
|
|
|
iceServers = getMappedFallbackStun();
|
2018-07-10 05:29:27 +08:00
|
|
|
} finally {
|
|
|
|
const options = {
|
2019-01-08 00:54:37 +08:00
|
|
|
wsUrl: Auth.authenticateURL(SFU_URL),
|
2018-07-10 05:29:27 +08:00
|
|
|
chromeExtension: CHROME_EXTENSION_KEY,
|
|
|
|
chromeScreenshareSources: CHROME_SCREENSHARE_SOURCES,
|
|
|
|
firefoxScreenshareSource: FIREFOX_SCREENSHARE_SOURCE,
|
|
|
|
iceServers,
|
2019-07-16 04:59:00 +08:00
|
|
|
logger,
|
2019-03-28 06:18:40 +08:00
|
|
|
userName: getUsername(),
|
2018-07-10 05:29:27 +08:00
|
|
|
};
|
2019-09-07 02:58:22 +08:00
|
|
|
|
2019-09-11 00:20:40 +08:00
|
|
|
let started = false;
|
|
|
|
|
2019-09-07 02:58:22 +08:00
|
|
|
const failureCallback = (error) => {
|
2019-09-11 00:20:40 +08:00
|
|
|
const normalizedError = KurentoScreenshareBridge.handlePresenterFailure(error, started);
|
2019-09-07 02:58:22 +08:00
|
|
|
onFail(normalizedError);
|
|
|
|
};
|
|
|
|
|
|
|
|
const successCallback = () => {
|
2019-09-11 00:20:40 +08:00
|
|
|
started = true;
|
2019-09-07 02:58:22 +08:00
|
|
|
logger.info({
|
|
|
|
logCode: 'screenshare_presenter_start_success',
|
|
|
|
}, 'Screenshare presenter started succesfully');
|
|
|
|
};
|
|
|
|
|
2020-05-15 00:36:03 +08:00
|
|
|
options.stream = stream || undefined;
|
|
|
|
|
2018-07-10 05:29:27 +08:00
|
|
|
window.kurentoShareScreen(
|
|
|
|
SCREENSHARE_VIDEO_TAG,
|
|
|
|
BridgeService.getConferenceBridge(),
|
|
|
|
getUserId(),
|
|
|
|
getMeetingId(),
|
2019-09-07 02:58:22 +08:00
|
|
|
failureCallback,
|
|
|
|
successCallback,
|
2018-08-23 23:49:16 +08:00
|
|
|
options,
|
2018-07-10 05:29:27 +08:00
|
|
|
);
|
|
|
|
}
|
2017-09-13 04:47:06 +08:00
|
|
|
}
|
2017-11-06 23:39:55 +08:00
|
|
|
|
|
|
|
kurentoExitScreenShare() {
|
|
|
|
window.kurentoExitScreenShare();
|
|
|
|
}
|
2017-09-09 01:00:00 +08:00
|
|
|
}
|