diff --git a/react/features/shared-video/actions.any.ts b/react/features/shared-video/actions.any.ts index d2a2fcafed..6498814068 100644 --- a/react/features/shared-video/actions.any.ts +++ b/react/features/shared-video/actions.any.ts @@ -11,7 +11,7 @@ import { } from './actionTypes'; import { ShareVideoConfirmDialog, SharedVideoDialog } from './components'; import { PLAYBACK_START, PLAYBACK_STATUSES } from './constants'; -import { isSharedVideoEnabled } from './functions'; +import { isSharedVideoEnabled, sendShareVideoCommand } from './functions'; /** @@ -120,12 +120,15 @@ export function playSharedVideo(videoUrl: string) { if (conference) { const localParticipant = getLocalParticipant(getState()); - dispatch(setSharedVideoStatus({ - videoUrl, + // we will send the command and will create local video fake participant + // and start playing once we receive ourselves the command + sendShareVideoCommand({ + conference, + id: videoUrl, + localParticipantId: localParticipant?.id, status: PLAYBACK_START, - time: 0, - ownerId: localParticipant?.id - })); + time: 0 + }); } }; } diff --git a/react/features/shared-video/functions.ts b/react/features/shared-video/functions.ts index f29d47f1a8..597198c54a 100644 --- a/react/features/shared-video/functions.ts +++ b/react/features/shared-video/functions.ts @@ -1,4 +1,5 @@ import { IStateful } from '../base/app/types'; +import { IJitsiConference } from '../base/conference/reducer'; import { getFakeParticipants } from '../base/participants/functions'; import { toState } from '../base/redux/functions'; @@ -6,6 +7,7 @@ import { ALLOW_ALL_URL_DOMAINS, PLAYBACK_START, PLAYBACK_STATUSES, + SHARED_VIDEO, VIDEO_PLAYER_PARTICIPANT_NAME, YOUTUBE_PLAYER_PARTICIPANT_NAME, YOUTUBE_URL_DOMAIN @@ -146,3 +148,29 @@ export function isURLAllowedForSharedVideo(url: string, return false; } + +/** + * Sends SHARED_VIDEO command. + * + * @param {string} id - The id of the video. + * @param {string} status - The status of the shared video. + * @param {JitsiConference} conference - The current conference. + * @param {string} localParticipantId - The id of the local participant. + * @param {string} time - The seek position of the video. + * @returns {void} + */ +export function sendShareVideoCommand({ id, status, conference, localParticipantId = '', time, muted, volume }: { + conference?: IJitsiConference; id: string; localParticipantId?: string; muted?: boolean; + status: string; time: number; volume?: number; +}) { + conference?.sendCommandOnce(SHARED_VIDEO, { + value: id, + attributes: { + from: localParticipantId, + muted, + state: status, + time, + volume + } + }); +} diff --git a/react/features/shared-video/middleware.any.ts b/react/features/shared-video/middleware.any.ts index e9358102dc..6e1662452b 100644 --- a/react/features/shared-video/middleware.any.ts +++ b/react/features/shared-video/middleware.any.ts @@ -30,7 +30,7 @@ import { SHARED_VIDEO, VIDEO_PLAYER_PARTICIPANT_NAME } from './constants'; -import { isSharedVideoEnabled, isSharingStatus, isURLAllowedForSharedVideo } from './functions'; +import { isSharedVideoEnabled, isSharingStatus, isURLAllowedForSharedVideo, sendShareVideoCommand } from './functions'; import logger from './logger'; @@ -155,6 +155,14 @@ MiddlewareRegistry.register(store => next => action => { APP.API.notifyAudioOrVideoSharingToggled(MEDIA_TYPE.VIDEO, status, ownerId); } + // when setting status we need to send the command for that, but not do it for the start command + // as we are sending the command in playSharedVideo and setting the start status once + // we receive the response, this way we will start the video at the same time when remote participants + // start it, on receiving the command + if (status === 'start') { + break; + } + if (localParticipantId === ownerId) { sendShareVideoCommand({ conference, @@ -224,12 +232,15 @@ function handleSharingVideoStatus(store: IStore, videoUrl: string, if (oldVideoUrl && oldVideoUrl !== videoUrl) { logger.warn( - `User with id: ${localParticipantId} sent videoUrl: ${videoUrl} while we are playing: ${oldVideoUrl}`); + `User with id: ${from} sent videoUrl: ${videoUrl} while we are playing: ${oldVideoUrl}`); return; } - if (state === PLAYBACK_START && !isSharingStatus(oldStatus)) { + // If the video was not started (no participant) we want to create the participant + // this can be triggered by start, but also by paused or playing + // commands (joining late) and getting the current state + if (state === PLAYBACK_START || !isSharingStatus(oldStatus)) { const youtubeId = videoUrl.match(/http/) ? false : videoUrl; const avatarURL = youtubeId ? `https://img.youtube.com/vi/${youtubeId}/0.jpg` : ''; @@ -242,6 +253,15 @@ function handleSharingVideoStatus(store: IStore, videoUrl: string, })); dispatch(pinParticipant(videoUrl)); + + if (localParticipantId === from) { + dispatch(setSharedVideoStatus({ + videoUrl, + status: state, + time: Number(time), + ownerId: localParticipantId + })); + } } if (localParticipantId !== from) { @@ -254,31 +274,3 @@ function handleSharingVideoStatus(store: IStore, videoUrl: string, })); } } - -/* eslint-disable max-params */ - -/** - * Sends SHARED_VIDEO command. - * - * @param {string} id - The id of the video. - * @param {string} status - The status of the shared video. - * @param {JitsiConference} conference - The current conference. - * @param {string} localParticipantId - The id of the local participant. - * @param {string} time - The seek position of the video. - * @returns {void} - */ -function sendShareVideoCommand({ id, status, conference, localParticipantId = '', time, muted, volume }: { - conference?: IJitsiConference; id: string; localParticipantId?: string; muted: boolean; - status: string; time: number; volume: number; -}) { - conference?.sendCommandOnce(SHARED_VIDEO, { - value: id, - attributes: { - from: localParticipantId, - muted, - state: status, - time, - volume - } - }); -}