2024-05-24 22:44:36 +08:00
|
|
|
import { makeVar, useReactiveVar } from '@apollo/client';
|
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-11-15 00:35:56 +08:00
|
|
|
import logger from '/imports/startup/client/logger';
|
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 MediaStreamUtils from '/imports/utils/media-stream-utils';
|
2022-05-09 09:59:25 +08:00
|
|
|
import ConnectionStatusService from '/imports/ui/components/connection-status/service';
|
2022-02-17 08:51:39 +08:00
|
|
|
import browserInfo from '/imports/utils/browserInfo';
|
2024-06-13 19:59:11 +08:00
|
|
|
import createUseSubscription from '/imports/ui/core/hooks/createUseSubscription';
|
|
|
|
import { SCREENSHARE_SUBSCRIPTION } from './queries';
|
2017-07-25 03:29:34 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const SCREENSHARE_MEDIA_ELEMENT_NAME = 'screenshareVideo';
|
2020-12-10 06:00:54 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const DEFAULT_SCREENSHARE_STATS_TYPES = [
|
2021-08-23 23:36:01 +08:00
|
|
|
'outbound-rtp',
|
|
|
|
'inbound-rtp',
|
|
|
|
];
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const CONTENT_TYPE_CAMERA = 'camera';
|
|
|
|
export const CONTENT_TYPE_SCREENSHARE = 'screenshare';
|
2023-03-24 20:23:17 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const isSharingVar = makeVar(false);
|
|
|
|
export const sharingContentTypeVar = makeVar(false);
|
|
|
|
export const cameraAsContentDeviceIdTypeVar = makeVar('');
|
2020-12-10 06:00:54 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const useScreenshare = createUseSubscription(SCREENSHARE_SUBSCRIPTION, {}, true);
|
2024-06-13 19:59:11 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const useIsSharing = () => useReactiveVar(isSharingVar);
|
|
|
|
export const useSharingContentType = () => useReactiveVar(sharingContentTypeVar);
|
|
|
|
export const useCameraAsContentDeviceIdType = () => useReactiveVar(cameraAsContentDeviceIdTypeVar);
|
2020-12-10 06:00:54 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const isSharing = () => isSharingVar();
|
2020-12-10 06:00:54 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const setIsSharing = (sharing) => {
|
2024-05-24 22:44:36 +08:00
|
|
|
if (isSharing() !== sharing) {
|
|
|
|
isSharingVar(sharing);
|
2023-03-28 05:40:08 +08:00
|
|
|
}
|
2020-12-10 06:00:54 +08:00
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const getSharingContentType = () => sharingContentTypeVar();
|
2024-05-24 22:44:36 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const setSharingContentType = (contentType) => {
|
2024-05-24 22:44:36 +08:00
|
|
|
if (getSharingContentType() !== contentType) {
|
|
|
|
sharingContentTypeVar(contentType);
|
2020-12-10 06:00:54 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const getCameraAsContentDeviceId = () => cameraAsContentDeviceIdTypeVar();
|
2023-03-28 05:46:42 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const setCameraAsContentDeviceId = (deviceId) => {
|
2024-05-24 22:44:36 +08:00
|
|
|
if (getCameraAsContentDeviceId() !== deviceId) {
|
|
|
|
cameraAsContentDeviceIdTypeVar(deviceId);
|
2020-12-10 06:00:54 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const _trackStreamTermination = (stream, handler) => {
|
2022-05-08 00:25:31 +08:00
|
|
|
if (typeof stream !== 'object' || typeof handler !== 'function') {
|
|
|
|
throw new TypeError('Invalid trackStreamTermination arguments');
|
|
|
|
}
|
2023-08-23 21:45:44 +08:00
|
|
|
let _handler = handler;
|
|
|
|
|
|
|
|
// Dirty, but effective way of checking whether the browser supports the 'inactive'
|
|
|
|
// event. If the oninactive interface is null, it can be overridden === supported.
|
|
|
|
// If undefined, it's not; so we fallback to the track 'ended' event.
|
|
|
|
// The track ended listener should probably be reviewed once we create
|
|
|
|
// thin wrapper classes for MediaStreamTracks as well, because we'll want a single
|
|
|
|
// media stream holding multiple tracks in the future
|
|
|
|
if (stream.oninactive !== undefined) {
|
|
|
|
if (typeof stream.oninactive === 'function') {
|
|
|
|
const oldHandler = stream.oninactive;
|
|
|
|
_handler = () => {
|
|
|
|
oldHandler();
|
|
|
|
handler();
|
|
|
|
};
|
|
|
|
}
|
2022-05-08 00:25:31 +08:00
|
|
|
stream.addEventListener('inactive', handler, { once: true });
|
|
|
|
} else {
|
|
|
|
const track = MediaStreamUtils.getVideoTracks(stream)[0];
|
|
|
|
if (track) {
|
|
|
|
track.addEventListener('ended', handler, { once: true });
|
2023-08-23 21:45:44 +08:00
|
|
|
if (typeof track.onended === 'function') {
|
|
|
|
const oldHandler = track.onended;
|
|
|
|
_handler = () => {
|
|
|
|
oldHandler();
|
|
|
|
handler();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
track.onended = _handler;
|
2022-05-08 00:25:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const _isStreamActive = (stream) => {
|
2024-05-24 22:44:36 +08:00
|
|
|
const tracksAreActive = !stream.getTracks().some((track) => track.readyState === 'ended');
|
2022-05-08 00:25:31 +08:00
|
|
|
|
|
|
|
return tracksAreActive && stream.active;
|
2024-05-24 22:44:36 +08:00
|
|
|
};
|
2022-05-08 00:25:31 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const _handleStreamTermination = () => {
|
2022-05-08 00:25:31 +08:00
|
|
|
screenshareHasEnded();
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const useIsScreenGloballyBroadcasting = () => {
|
2024-07-31 02:53:49 +08:00
|
|
|
const { data, loading } = useScreenshare();
|
|
|
|
|
|
|
|
return {
|
|
|
|
screenIsShared: Boolean(
|
|
|
|
data
|
2024-06-13 20:45:28 +08:00
|
|
|
&& data[0]
|
|
|
|
&& data[0].contentType === CONTENT_TYPE_SCREENSHARE
|
|
|
|
&& data[0].stream,
|
2024-07-31 02:53:49 +08:00
|
|
|
),
|
|
|
|
loading,
|
|
|
|
};
|
2024-06-13 19:59:11 +08:00
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const useIsCameraAsContentGloballyBroadcasting = () => {
|
2024-06-13 19:59:11 +08:00
|
|
|
const { data } = useScreenshare();
|
|
|
|
|
|
|
|
return Boolean(data && data[0] && data[0].contentType === CONTENT_TYPE_CAMERA && data[0].stream);
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const useIsScreenBroadcasting = () => {
|
2024-06-13 20:45:28 +08:00
|
|
|
const active = useIsSharing();
|
|
|
|
const sharingContentType = useSharingContentType();
|
2024-07-31 02:53:49 +08:00
|
|
|
const { screenIsShared } = useIsScreenGloballyBroadcasting();
|
2024-06-13 20:45:28 +08:00
|
|
|
const sharing = active && sharingContentType === CONTENT_TYPE_SCREENSHARE;
|
|
|
|
|
|
|
|
return sharing || screenIsShared;
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const useIsCameraAsContentBroadcasting = () => {
|
2024-06-14 22:35:23 +08:00
|
|
|
const active = useIsSharing();
|
|
|
|
const sharingContentType = useSharingContentType();
|
2024-05-24 22:44:36 +08:00
|
|
|
const sharing = active && sharingContentType === CONTENT_TYPE_CAMERA;
|
2024-06-14 22:35:23 +08:00
|
|
|
const cameraAsContentIsShared = useIsCameraAsContentGloballyBroadcasting();
|
2023-03-28 05:40:08 +08:00
|
|
|
|
|
|
|
return sharing || cameraAsContentIsShared;
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const useScreenshareHasAudio = () => {
|
2024-06-13 19:59:11 +08:00
|
|
|
const { data } = useScreenshare();
|
|
|
|
|
|
|
|
return Boolean(data && data[0] && data[0].hasAudio);
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const useBroadcastContentType = () => {
|
2024-06-14 22:35:23 +08:00
|
|
|
const { data } = useScreenshare();
|
2023-05-31 22:06:49 +08:00
|
|
|
|
2024-06-14 22:35:23 +08:00
|
|
|
if (!data || !data[0]) {
|
2023-03-28 05:40:08 +08:00
|
|
|
// defaults to contentType: "camera"
|
|
|
|
return CONTENT_TYPE_CAMERA;
|
|
|
|
}
|
|
|
|
|
2024-06-14 22:35:23 +08:00
|
|
|
return data[0].contentType;
|
2024-05-24 22:44:36 +08:00
|
|
|
};
|
2023-03-28 05:40:08 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const screenshareHasEnded = () => {
|
2024-05-24 22:44:36 +08:00
|
|
|
if (isSharingVar()) {
|
2023-03-28 05:40:08 +08:00
|
|
|
setIsSharing(false);
|
2020-08-27 07:30:53 +08:00
|
|
|
}
|
2023-03-28 05:46:42 +08:00
|
|
|
if (getSharingContentType() === CONTENT_TYPE_CAMERA) {
|
|
|
|
setCameraAsContentDeviceId('');
|
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
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const getMediaElement = () => document.getElementById(SCREENSHARE_MEDIA_ELEMENT_NAME);
|
2020-12-10 06:00:54 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const getMediaElementDimensions = () => {
|
2022-07-22 07:25:15 +08:00
|
|
|
const element = getMediaElement();
|
|
|
|
return {
|
|
|
|
width: element?.videoWidth ?? 0,
|
|
|
|
height: element?.videoHeight ?? 0,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const setVolume = (volume) => {
|
2021-11-12 01:51:21 +08:00
|
|
|
KurentoBridge.setVolume(volume);
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const getVolume = () => KurentoBridge.getVolume();
|
2021-12-09 00:24:26 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const useShouldEnableVolumeControl = () => {
|
2024-06-13 19:59:11 +08:00
|
|
|
const SCREENSHARE_CONFIG = window.meetingClientSettings.public.kurento.screenshare;
|
|
|
|
const VOLUME_CONTROL_ENABLED = SCREENSHARE_CONFIG.enableVolumeControl;
|
|
|
|
const hasAudio = useScreenshareHasAudio();
|
|
|
|
|
|
|
|
return VOLUME_CONTROL_ENABLED && hasAudio;
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const attachLocalPreviewStream = (mediaElement) => {
|
2024-05-24 22:44:36 +08:00
|
|
|
const { isTabletApp } = browserInfo;
|
2022-11-09 09:50:16 +08:00
|
|
|
if (isTabletApp) {
|
2022-02-17 08:51:39 +08:00
|
|
|
// We don't show preview for mobile app, as the stream is only available in native code
|
|
|
|
return;
|
|
|
|
}
|
2020-12-10 06:00:54 +08:00
|
|
|
const stream = KurentoBridge.gdmStream;
|
|
|
|
if (stream && mediaElement) {
|
|
|
|
// Always muted, presenter preview.
|
|
|
|
BridgeService.screenshareLoadAndPlayMediaStream(stream, mediaElement, true);
|
|
|
|
}
|
2023-05-24 09:07:32 +08:00
|
|
|
};
|
2020-12-10 06:00:54 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const setOutputDeviceId = (outputDeviceId) => {
|
2023-05-24 09:07:32 +08:00
|
|
|
const screenShareElement = document.getElementById(SCREENSHARE_MEDIA_ELEMENT_NAME);
|
2023-05-13 04:06:23 +08:00
|
|
|
const sinkIdSupported = screenShareElement && typeof screenShareElement.setSinkId === 'function';
|
2023-05-24 09:07:32 +08:00
|
|
|
const srcStream = screenShareElement?.srcObject;
|
2023-05-13 04:06:23 +08:00
|
|
|
|
2023-05-24 09:07:32 +08:00
|
|
|
if (typeof outputDeviceId === 'string'
|
|
|
|
&& sinkIdSupported
|
|
|
|
&& screenShareElement.sinkId !== outputDeviceId
|
|
|
|
&& srcStream
|
|
|
|
&& srcStream.getAudioTracks().length > 0) {
|
2023-05-13 04:06:23 +08:00
|
|
|
try {
|
|
|
|
screenShareElement.setSinkId(outputDeviceId);
|
|
|
|
logger.debug({
|
2023-05-24 09:07:32 +08:00
|
|
|
logCode: 'screenshare_output_device_change',
|
2023-05-13 04:06:23 +08:00
|
|
|
extraInfo: {
|
|
|
|
newDeviceId: outputDeviceId,
|
|
|
|
},
|
2023-05-24 09:07:32 +08:00
|
|
|
}, `Screenshare output device changed: to ${outputDeviceId || 'default'}`);
|
2023-05-13 04:06:23 +08:00
|
|
|
} catch (error) {
|
|
|
|
logger.error({
|
2023-05-24 09:07:32 +08:00
|
|
|
logCode: 'screenshare_output_device_change_failure',
|
2023-05-13 04:06:23 +08:00
|
|
|
extraInfo: {
|
|
|
|
errorName: error.name,
|
|
|
|
errorMessage: error.message,
|
|
|
|
newDeviceId: outputDeviceId,
|
|
|
|
},
|
2023-05-24 09:07:32 +08:00
|
|
|
}, `Error changing screenshare output device - {${error.name}: ${error.message}}`);
|
2023-05-13 04:06:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const screenshareHasStarted = (hasAudio, isPresenter, options = {}) => {
|
2020-12-10 06:00:54 +08:00
|
|
|
// Presenter's screen preview is local, so skip
|
2021-11-24 00:40:14 +08:00
|
|
|
if (!isPresenter) {
|
2024-06-14 22:35:23 +08:00
|
|
|
viewScreenshare({ outputDeviceId: options.outputDeviceId }, hasAudio);
|
2020-12-10 06:00:54 +08:00
|
|
|
}
|
2019-03-09 03:41:19 +08:00
|
|
|
};
|
2017-07-25 03:29:34 +08:00
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const shareScreen = async (
|
2024-06-14 22:35:23 +08:00
|
|
|
isCameraAsContentBroadcasting,
|
|
|
|
stopWatching,
|
|
|
|
isPresenter,
|
|
|
|
onFail,
|
|
|
|
options = {},
|
|
|
|
) => {
|
|
|
|
if (isCameraAsContentBroadcasting) {
|
2023-03-29 21:36:22 +08:00
|
|
|
screenshareHasEnded();
|
|
|
|
}
|
2019-12-19 05:40:04 +08:00
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
try {
|
2023-04-27 00:09:02 +08:00
|
|
|
let stream;
|
2023-03-24 20:23:17 +08:00
|
|
|
let contentType = CONTENT_TYPE_SCREENSHARE;
|
2023-03-11 01:25:24 +08:00
|
|
|
if (options.stream == null) {
|
2023-04-27 00:09:02 +08:00
|
|
|
stream = await BridgeService.getScreenStream();
|
|
|
|
} else {
|
2023-03-24 20:23:17 +08:00
|
|
|
contentType = CONTENT_TYPE_CAMERA;
|
2023-04-27 00:09:02 +08:00
|
|
|
stream = options.stream;
|
|
|
|
}
|
2022-05-08 00:25:31 +08:00
|
|
|
_trackStreamTermination(stream, _handleStreamTermination);
|
|
|
|
|
|
|
|
if (!isPresenter) {
|
|
|
|
MediaStreamUtils.stopMediaStreamTracks(stream);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-24 20:23:17 +08:00
|
|
|
await KurentoBridge.share(stream, onFail, contentType);
|
2022-05-08 00:25:31 +08:00
|
|
|
|
|
|
|
// Stream might have been disabled in the meantime. I love badly designed
|
|
|
|
// async components like this screen sharing bridge :) - prlanzarin 09 May 22
|
|
|
|
if (!_isStreamActive(stream)) {
|
|
|
|
_handleStreamTermination();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-01-20 02:47:10 +08:00
|
|
|
// stop external video share if running
|
|
|
|
stopWatching();
|
2022-10-24 21:11:28 +08:00
|
|
|
|
2023-03-28 05:40:08 +08:00
|
|
|
setSharingContentType(contentType);
|
|
|
|
setIsSharing(true);
|
2020-12-10 06:00:54 +08:00
|
|
|
} catch (error) {
|
2022-05-08 00:25:31 +08:00
|
|
|
onFail(error);
|
2020-12-10 06:00:54 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const viewScreenshare = (options = {}, hasAudio) => {
|
2023-05-24 09:07:32 +08:00
|
|
|
KurentoBridge.view({ hasAudio, outputDeviceId: options.outputDeviceId })
|
|
|
|
.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
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export const screenShareEndAlert = () => AudioService
|
2024-03-07 01:28:18 +08:00
|
|
|
.playAlertSound(`${window.meetingClientSettings.public.app.cdn
|
2024-08-03 02:10:39 +08:00
|
|
|
+ window.meetingClientSettings.public.app.basename}`
|
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
|
|
|
|
2021-08-23 23:36:01 +08:00
|
|
|
/**
|
2022-05-09 09:59:25 +08:00
|
|
|
* Get stats about all active screenshare peers.
|
2021-08-23 23:36:01 +08:00
|
|
|
*
|
|
|
|
* For more information see:
|
2022-05-09 09:59:25 +08:00
|
|
|
* - https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats
|
|
|
|
* - https://developer.mozilla.org/en-US/docs/Web/API/RTCStatsReport
|
|
|
|
|
|
|
|
* @param {Array[String]} statsType - An array containing valid RTCStatsType
|
|
|
|
* values to include in the return object
|
|
|
|
*
|
|
|
|
* @returns {Object} The information about each active screen sharing peer.
|
|
|
|
* The returned format follows the format returned by video's service
|
|
|
|
* getStats, which considers more than one peer connection to be returned.
|
2021-08-23 23:36:01 +08:00
|
|
|
* The format is given by:
|
|
|
|
* {
|
|
|
|
* peerIdString: RTCStatsReport
|
|
|
|
* }
|
|
|
|
*/
|
2024-08-10 01:58:44 +08:00
|
|
|
export const getStats = async (statsTypes = DEFAULT_SCREENSHARE_STATS_TYPES) => {
|
2022-05-09 09:59:25 +08:00
|
|
|
const screenshareStats = {};
|
2021-08-23 23:36:01 +08:00
|
|
|
const peer = KurentoBridge.getPeerConnection();
|
|
|
|
|
|
|
|
if (!peer) return null;
|
|
|
|
|
|
|
|
const peerStats = await peer.getStats();
|
|
|
|
|
|
|
|
peerStats.forEach((stat) => {
|
2022-05-09 09:59:25 +08:00
|
|
|
if (statsTypes.includes(stat.type)) {
|
2021-08-23 23:36:01 +08:00
|
|
|
screenshareStats[stat.type] = stat;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return { screenshareStats };
|
|
|
|
};
|
|
|
|
|
2022-05-09 09:59:25 +08:00
|
|
|
// This method may throw errors
|
2024-08-10 01:58:44 +08:00
|
|
|
export const isMediaFlowing = (previousStats, currentStats) => {
|
2022-05-09 09:59:25 +08:00
|
|
|
const bpsData = ConnectionStatusService.calculateBitsPerSecond(
|
2023-02-16 07:01:05 +08:00
|
|
|
currentStats?.screenshareStats,
|
|
|
|
previousStats?.screenshareStats,
|
2022-05-09 09:59:25 +08:00
|
|
|
);
|
|
|
|
const bpsDataAggr = Object.values(bpsData)
|
|
|
|
.reduce((sum, partialBpsData = 0) => sum + parseFloat(partialBpsData), 0);
|
|
|
|
|
|
|
|
return bpsDataAggr > 0;
|
|
|
|
};
|
|
|
|
|
2024-08-10 01:58:44 +08:00
|
|
|
export default {
|
2020-12-10 06:00:54 +08:00
|
|
|
SCREENSHARE_MEDIA_ELEMENT_NAME,
|
2022-05-09 09:59:25 +08:00
|
|
|
isMediaFlowing,
|
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,
|
2023-03-28 05:40:08 +08:00
|
|
|
isSharing,
|
|
|
|
setIsSharing,
|
|
|
|
setSharingContentType,
|
|
|
|
getSharingContentType,
|
2020-12-10 06:00:54 +08:00
|
|
|
getMediaElement,
|
2022-07-22 07:25:15 +08:00
|
|
|
getMediaElementDimensions,
|
2020-12-10 06:00:54 +08:00
|
|
|
attachLocalPreviewStream,
|
2021-08-23 23:36:01 +08:00
|
|
|
getStats,
|
2021-11-12 01:51:21 +08:00
|
|
|
setVolume,
|
|
|
|
getVolume,
|
2023-03-28 05:46:42 +08:00
|
|
|
setCameraAsContentDeviceId,
|
|
|
|
getCameraAsContentDeviceId,
|
2023-05-24 09:07:32 +08:00
|
|
|
setOutputDeviceId,
|
2024-05-24 22:44:36 +08:00
|
|
|
useCameraAsContentDeviceIdType,
|
|
|
|
useIsSharing,
|
|
|
|
useSharingContentType,
|
2024-06-13 19:59:11 +08:00
|
|
|
useIsScreenGloballyBroadcasting,
|
|
|
|
useIsCameraAsContentGloballyBroadcasting,
|
|
|
|
useShouldEnableVolumeControl,
|
2024-06-13 20:45:28 +08:00
|
|
|
useIsScreenBroadcasting,
|
2024-06-14 22:35:23 +08:00
|
|
|
useIsCameraAsContentBroadcasting,
|
|
|
|
useScreenshareHasAudio,
|
|
|
|
useBroadcastContentType,
|
2017-07-25 03:29:34 +08:00
|
|
|
};
|