2017-10-12 10:00:28 +08:00
|
|
|
import Screenshare from '/imports/api/screenshare';
|
2017-11-11 11:41:37 +08:00
|
|
|
import KurentoBridge from '/imports/api/screenshare/client/bridge';
|
2020-05-15 00:36:03 +08:00
|
|
|
import BridgeService from '/imports/api/screenshare/client/bridge/service';
|
2019-05-29 04:46:29 +08:00
|
|
|
import Settings from '/imports/ui/services/settings';
|
2019-11-15 00:35:56 +08:00
|
|
|
import logger from '/imports/startup/client/logger';
|
2019-12-19 05:40:04 +08:00
|
|
|
import { stopWatching } from '/imports/ui/components/external-video-player/service';
|
|
|
|
import Meetings from '/imports/api/meetings';
|
|
|
|
import Auth from '/imports/ui/services/auth';
|
2020-09-10 22:11:18 +08:00
|
|
|
import UserListService from '/imports/ui/components/user-list/service';
|
Correctly set audio input/output devices
When refusing ("thumbs down" button) echo test, user is able to select a different input device. This should work fine for chrome, firefox and safari (once user grants permission when asked by html5client).
For output devices, we depend on setSinkId function, which is enabled by default on current chrome release (2020) but not in Firefox (user needs to enable "setSinkId in about:config page). This implementation is listed as (?) in MDN.
In other words, output device selection should work out of the box for chrome, only.
When selecting an outputDevice, all alert sounds (hangup, screenshare , polling, etc) also goes to the same output device.
This solves #10592
2020-10-07 07:37:55 +08:00
|
|
|
import AudioService from '/imports/ui/components/audio/service';
|
2021-04-03 11:06:39 +08:00
|
|
|
import { Meteor } from "meteor/meteor";
|
|
|
|
import MediaStreamUtils from '/imports/utils/media-stream-utils';
|
2017-07-25 03:29:34 +08:00
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
const SCREENSHARE_MEDIA_ELEMENT_NAME = 'screenshareVideo';
|
|
|
|
|
2021-08-23 23:36:01 +08:00
|
|
|
/**
|
|
|
|
* Screenshare status to be filtered in getStats()
|
|
|
|
*/
|
|
|
|
const FILTER_SCREENSHARE_STATS = [
|
|
|
|
'outbound-rtp',
|
|
|
|
'inbound-rtp',
|
|
|
|
];
|
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
let _isSharingScreen = false;
|
|
|
|
const _sharingScreenDep = {
|
|
|
|
value: false,
|
|
|
|
tracker: new Tracker.Dependency(),
|
|
|
|
};
|
|
|
|
|
|
|
|
const isSharingScreen = () => {
|
|
|
|
_sharingScreenDep.tracker.depend();
|
|
|
|
return _sharingScreenDep.value;
|
|
|
|
};
|
|
|
|
|
|
|
|
const setSharingScreen = (isSharingScreen) => {
|
|
|
|
if (_sharingScreenDep.value !== isSharingScreen) {
|
|
|
|
_sharingScreenDep.value = isSharingScreen;
|
|
|
|
_sharingScreenDep.tracker.changed();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-06 06:18:39 +08:00
|
|
|
// A simplified, trackable version of isVideoBroadcasting that DOES NOT
|
|
|
|
// account for the presenter's local sharing state.
|
|
|
|
// It reflects the GLOBAL screen sharing state (akka-apps)
|
|
|
|
const isGloballyBroadcasting = () => {
|
|
|
|
const screenshareEntry = Screenshare.findOne({ meetingId: Auth.meetingID },
|
|
|
|
{ fields: { 'screenshare.stream': 1 } });
|
|
|
|
|
|
|
|
return (!screenshareEntry ? false : !!screenshareEntry.screenshare.stream);
|
|
|
|
}
|
|
|
|
|
2017-07-25 03:29:34 +08:00
|
|
|
// 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
|
|
|
|
// and display it, or end the call and hide the video
|
2017-11-06 23:39:55 +08:00
|
|
|
const isVideoBroadcasting = () => {
|
2020-12-10 06:00:54 +08:00
|
|
|
const sharing = isSharingScreen();
|
2020-08-27 03:53:23 +08:00
|
|
|
const screenshareEntry = Screenshare.findOne({ meetingId: Auth.meetingID },
|
|
|
|
{ fields: { 'screenshare.stream': 1 } });
|
2020-12-10 06:00:54 +08:00
|
|
|
const screenIsShared = !screenshareEntry ? false : !!screenshareEntry.screenshare.stream;
|
2017-07-25 03:29:34 +08:00
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
if (screenIsShared && isSharingScreen) {
|
|
|
|
setSharingScreen(false);
|
2017-07-26 04:56:40 +08:00
|
|
|
}
|
2017-09-09 01:00:00 +08:00
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
return sharing || screenIsShared;
|
2019-03-09 03:41:19 +08:00
|
|
|
};
|
2017-07-25 03:29:34 +08:00
|
|
|
|
|
|
|
|
2020-08-27 03:03:30 +08:00
|
|
|
const screenshareHasAudio = () => {
|
2020-08-27 03:53:23 +08:00
|
|
|
const screenshareEntry = Screenshare.findOne({ meetingId: Auth.meetingID },
|
|
|
|
{ fields: { 'screenshare.hasAudio': 1 } });
|
|
|
|
|
|
|
|
if (!screenshareEntry) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !!screenshareEntry.screenshare.hasAudio;
|
2020-08-27 03:03:30 +08:00
|
|
|
}
|
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
const screenshareHasEnded = () => {
|
2021-03-12 09:31:46 +08:00
|
|
|
if (isSharingScreen()) {
|
2020-12-10 06:00:54 +08:00
|
|
|
setSharingScreen(false);
|
2020-08-27 07:30:53 +08:00
|
|
|
}
|
2020-12-10 06:00:54 +08:00
|
|
|
|
|
|
|
KurentoBridge.stop();
|
2020-08-27 07:30:53 +08:00
|
|
|
};
|
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
const getMediaElement = () => {
|
|
|
|
return document.getElementById(SCREENSHARE_MEDIA_ELEMENT_NAME);
|
|
|
|
}
|
|
|
|
|
|
|
|
const attachLocalPreviewStream = (mediaElement) => {
|
|
|
|
const stream = KurentoBridge.gdmStream;
|
|
|
|
if (stream && mediaElement) {
|
|
|
|
// Always muted, presenter preview.
|
|
|
|
BridgeService.screenshareLoadAndPlayMediaStream(stream, mediaElement, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const screenshareHasStarted = () => {
|
|
|
|
// Presenter's screen preview is local, so skip
|
2021-03-12 09:31:46 +08:00
|
|
|
if (!UserListService.amIPresenter()) {
|
2020-08-27 07:30:53 +08:00
|
|
|
viewScreenshare();
|
2020-12-10 06:00:54 +08:00
|
|
|
}
|
2019-03-09 03:41:19 +08:00
|
|
|
};
|
2017-07-25 03:29:34 +08:00
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
const shareScreen = async (onFail) => {
|
2019-12-19 05:40:04 +08:00
|
|
|
// stop external video share if running
|
|
|
|
const meeting = Meetings.findOne({ meetingId: Auth.meetingID });
|
2020-12-10 06:00:54 +08:00
|
|
|
|
2019-12-19 05:40:04 +08:00
|
|
|
if (meeting && meeting.externalVideoUrl) {
|
|
|
|
stopWatching();
|
|
|
|
}
|
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
try {
|
|
|
|
const stream = await BridgeService.getScreenStream();
|
2021-04-03 11:06:39 +08:00
|
|
|
if(!UserListService.isUserPresenter(Auth.userID)) return MediaStreamUtils.stopMediaStreamTracks(stream);
|
2020-12-10 06:00:54 +08:00
|
|
|
await KurentoBridge.share(stream, onFail);
|
|
|
|
setSharingScreen(true);
|
|
|
|
} catch (error) {
|
|
|
|
return onFail(error);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const viewScreenshare = () => {
|
|
|
|
const hasAudio = screenshareHasAudio();
|
|
|
|
KurentoBridge.view(hasAudio).catch((error) => {
|
|
|
|
logger.error({
|
|
|
|
logCode: 'screenshare_view_failed',
|
|
|
|
extraInfo: {
|
|
|
|
errorName: error.name,
|
|
|
|
errorMessage: error.message,
|
|
|
|
},
|
|
|
|
}, `Screenshare viewer failure`);
|
|
|
|
});
|
2019-03-09 03:41:19 +08:00
|
|
|
};
|
2017-09-13 04:47:06 +08:00
|
|
|
|
Correctly set audio input/output devices
When refusing ("thumbs down" button) echo test, user is able to select a different input device. This should work fine for chrome, firefox and safari (once user grants permission when asked by html5client).
For output devices, we depend on setSinkId function, which is enabled by default on current chrome release (2020) but not in Firefox (user needs to enable "setSinkId in about:config page). This implementation is listed as (?) in MDN.
In other words, output device selection should work out of the box for chrome, only.
When selecting an outputDevice, all alert sounds (hangup, screenshare , polling, etc) also goes to the same output device.
This solves #10592
2020-10-07 07:37:55 +08:00
|
|
|
const screenShareEndAlert = () => AudioService
|
|
|
|
.playAlertSound(`${Meteor.settings.public.app.cdn
|
2020-12-01 00:09:35 +08:00
|
|
|
+ Meteor.settings.public.app.basename
|
|
|
|
+ Meteor.settings.public.app.instanceId}`
|
Correctly set audio input/output devices
When refusing ("thumbs down" button) echo test, user is able to select a different input device. This should work fine for chrome, firefox and safari (once user grants permission when asked by html5client).
For output devices, we depend on setSinkId function, which is enabled by default on current chrome release (2020) but not in Firefox (user needs to enable "setSinkId in about:config page). This implementation is listed as (?) in MDN.
In other words, output device selection should work out of the box for chrome, only.
When selecting an outputDevice, all alert sounds (hangup, screenshare , polling, etc) also goes to the same output device.
This solves #10592
2020-10-07 07:37:55 +08:00
|
|
|
+ '/resources/sounds/ScreenshareOff.mp3');
|
2019-06-13 02:40:58 +08:00
|
|
|
|
2019-05-29 04:46:29 +08:00
|
|
|
const dataSavingSetting = () => Settings.dataSaving.viewScreenshare;
|
2019-04-16 05:39:07 +08:00
|
|
|
|
2021-08-23 23:36:01 +08:00
|
|
|
/**
|
|
|
|
* Get stats about all active screenshare peer.
|
|
|
|
* We filter the status based on FILTER_SCREENSHARE_STATS constant.
|
|
|
|
*
|
|
|
|
* For more information see:
|
|
|
|
* https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats
|
|
|
|
* and
|
|
|
|
* https://developer.mozilla.org/en-US/docs/Web/API/RTCStatsReport
|
|
|
|
* @returns An Object containing the information about each active peer
|
|
|
|
* (currently one, for screenshare). The returned format
|
|
|
|
* follows the format returned by video's service getStats, which
|
|
|
|
* considers more than one peer connection to be returned.
|
|
|
|
* The format is given by:
|
|
|
|
* {
|
|
|
|
* peerIdString: RTCStatsReport
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
const getStats = async () => {
|
|
|
|
const peer = KurentoBridge.getPeerConnection();
|
|
|
|
|
|
|
|
if (!peer) return null;
|
|
|
|
|
|
|
|
const peerStats = await peer.getStats();
|
|
|
|
|
|
|
|
const screenshareStats = {};
|
|
|
|
|
|
|
|
peerStats.forEach((stat) => {
|
|
|
|
if (FILTER_SCREENSHARE_STATS.includes(stat.type)) {
|
|
|
|
screenshareStats[stat.type] = stat;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return { screenshareStats };
|
|
|
|
};
|
|
|
|
|
2017-07-25 03:29:34 +08:00
|
|
|
export {
|
2020-12-10 06:00:54 +08:00
|
|
|
SCREENSHARE_MEDIA_ELEMENT_NAME,
|
2019-03-09 03:41:19 +08:00
|
|
|
isVideoBroadcasting,
|
2020-12-10 06:00:54 +08:00
|
|
|
screenshareHasEnded,
|
|
|
|
screenshareHasStarted,
|
2019-03-09 03:41:19 +08:00
|
|
|
shareScreen,
|
2019-04-16 05:39:07 +08:00
|
|
|
screenShareEndAlert,
|
2019-05-29 04:46:29 +08:00
|
|
|
dataSavingSetting,
|
2020-12-10 06:00:54 +08:00
|
|
|
isSharingScreen,
|
|
|
|
setSharingScreen,
|
|
|
|
getMediaElement,
|
|
|
|
attachLocalPreviewStream,
|
2021-02-06 06:18:39 +08:00
|
|
|
isGloballyBroadcasting,
|
2021-08-23 23:36:01 +08:00
|
|
|
getStats,
|
2017-07-25 03:29:34 +08:00
|
|
|
};
|