From 09124ad7e9e934974853f04a0ffc4aa5c14ac5de Mon Sep 17 00:00:00 2001 From: Jaya Allamsetty Date: Tue, 22 Sep 2020 15:45:51 -0400 Subject: [PATCH] fix(iframe): Use largeVideo video element for screenshot. Get the existing HTMLVideoElement for large video instead of creating a new video element for capturing the screenshot. This should prevent the video player from getting displayed on mobile Safari. --- modules/API/external/external_api.js | 4 +- react/features/large-video/actions.any.js | 52 ----------------------- react/features/large-video/actions.web.js | 47 ++++++++++++++++++++ 3 files changed, 49 insertions(+), 54 deletions(-) diff --git a/modules/API/external/external_api.js b/modules/API/external/external_api.js index cac9525ab..a26e708fb 100644 --- a/modules/API/external/external_api.js +++ b/modules/API/external/external_api.js @@ -639,8 +639,8 @@ export default class JitsiMeetExternalAPI extends EventEmitter { /** * Captures the screenshot of the large video. * - * @returns {dataURL} - Base64 encoded image data of the screenshot if large - * video is detected, an error otherwise. + * @returns {Promise} - Resolves with a base64 encoded image data of the screenshot + * if large video is detected, an error otherwise. */ captureLargeVideoScreenshot() { return this._transport.sendRequest({ diff --git a/react/features/large-video/actions.any.js b/react/features/large-video/actions.any.js index 3f787329c..f003f5547 100644 --- a/react/features/large-video/actions.any.js +++ b/react/features/large-video/actions.any.js @@ -9,7 +9,6 @@ import { import { _handleParticipantError } from '../base/conference'; import { MEDIA_TYPE } from '../base/media'; import { getParticipants } from '../base/participants'; -import { getTrackByMediaTypeAndParticipant } from '../base/tracks'; import { reportError } from '../base/util'; import { shouldDisplayTileView } from '../video-layout'; @@ -18,57 +17,6 @@ import { UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION } from './actionTypes'; -/** -* Captures a screenshot of the video displayed on the large video. -* -* @returns {Function} -*/ -export function captureLargeVideoScreenshot() { - return (dispatch: Dispatch, getState: Function): Promise => { - const state = getState(); - const largeVideo = state['features/large-video']; - - if (!largeVideo) { - return Promise.resolve(); - } - const tracks = state['features/base/tracks']; - const { jitsiTrack } = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, largeVideo.participantId); - const videoStream = jitsiTrack.getOriginalStream(); - - // Create a HTML canvas and draw video from the track on to the canvas. - const [ track ] = videoStream.getVideoTracks(); - const { height, width } = track.getSettings() ?? track.getConstraints(); - const canvasElement = document.createElement('canvas'); - const ctx = canvasElement.getContext('2d'); - const videoElement = document.createElement('video'); - - videoElement.height = parseInt(height, 10); - videoElement.width = parseInt(width, 10); - videoElement.autoplay = true; - videoElement.srcObject = videoStream; - canvasElement.height = videoElement.height; - canvasElement.width = videoElement.width; - - // Wait for the video to load before drawing on to the canvas. - const promise = new Promise(resolve => { - videoElement.onloadeddata = () => resolve(); - }); - - return promise.then(() => { - ctx.drawImage(videoElement, 0, 0, videoElement.width, videoElement.height); - const dataURL = canvasElement.toDataURL('image/png', 1.0); - - // Cleanup. - ctx.clearRect(0, 0, videoElement.width, videoElement.height); - videoElement.srcObject = null; - canvasElement.remove(); - videoElement.remove(); - - return Promise.resolve(dataURL); - }); - }; -} - /** * Signals conference to select a participant. * diff --git a/react/features/large-video/actions.web.js b/react/features/large-video/actions.web.js index bf3634021..75bd727fa 100644 --- a/react/features/large-video/actions.web.js +++ b/react/features/large-video/actions.web.js @@ -3,9 +3,56 @@ import type { Dispatch } from 'redux'; import VideoLayout from '../../../modules/UI/videolayout/VideoLayout'; +import { MEDIA_TYPE } from '../base/media'; +import { getTrackByMediaTypeAndParticipant } from '../base/tracks'; export * from './actions.any'; +/** +* Captures a screenshot of the video displayed on the large video. +* +* @returns {Function} +*/ +export function captureLargeVideoScreenshot() { + return (dispatch: Dispatch, getState: Function): Promise => { + const state = getState(); + const largeVideo = state['features/large-video']; + + if (!largeVideo) { + return Promise.resolve(); + } + const tracks = state['features/base/tracks']; + const { jitsiTrack } = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, largeVideo.participantId); + const videoStream = jitsiTrack.getOriginalStream(); + + // Get the video element for the large video, cast HTMLElement to HTMLVideoElement to make flow happy. + /* eslint-disable-next-line no-extra-parens*/ + const videoElement = ((document.getElementById('largeVideo'): any): HTMLVideoElement); + + if (!videoElement) { + return Promise.resolve(); + } + + // Create a HTML canvas and draw video on to the canvas. + const [ track ] = videoStream.getVideoTracks(); + const { height, width } = track.getSettings() ?? track.getConstraints(); + const canvasElement = document.createElement('canvas'); + const ctx = canvasElement.getContext('2d'); + + canvasElement.style.display = 'none'; + canvasElement.height = parseInt(height, 10); + canvasElement.width = parseInt(width, 10); + ctx.drawImage(videoElement, 0, 0); + const dataURL = canvasElement.toDataURL('image/png', 1.0); + + // Cleanup. + ctx.clearRect(0, 0, canvasElement.width, canvasElement.height); + canvasElement.remove(); + + return Promise.resolve(dataURL); + }; +} + /** * Resizes the large video container based on the dimensions provided. *