feat(iFrame): Add a method for capturing screenshot of the large video (#7717)

This commit is contained in:
Jaya Allamsetty 2020-09-18 11:53:27 -04:00 committed by GitHub
parent 119b79fd84
commit 1d5decc14f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 3 deletions

View File

@ -21,7 +21,11 @@ import {
import { isEnabled as isDropboxEnabled } from '../../react/features/dropbox';
import { toggleE2EE } from '../../react/features/e2ee/actions';
import { invite } from '../../react/features/invite';
import { resizeLargeVideo, selectParticipantInLargeVideo } from '../../react/features/large-video/actions';
import {
captureLargeVideoScreenshot,
resizeLargeVideo,
selectParticipantInLargeVideo
} from '../../react/features/large-video/actions';
import { toggleLobbyMode } from '../../react/features/lobby/actions.web';
import { RECORDING_TYPES } from '../../react/features/recording/constants';
import { getActiveSession } from '../../react/features/recording/functions';
@ -339,6 +343,21 @@ function initCommands() {
const { name } = request;
switch (name) {
case 'capture-largevideo-screenshot' :
APP.store.dispatch(captureLargeVideoScreenshot())
.then(dataURL => {
let error;
if (!dataURL) {
error = new Error('No large video found!');
}
callback({
error,
dataURL
});
});
break;
case 'invite': {
const { invitees } = request;

View File

@ -636,6 +636,18 @@ 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.
*/
captureLargeVideoScreenshot() {
return this._transport.sendRequest({
name: 'capture-largevideo-screenshot'
});
}
/**
* Removes the listeners and removes the Jitsi Meet frame.
*

View File

@ -10,6 +10,7 @@ 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';
@ -20,12 +21,63 @@ import {
declare var APP: Object;
/**
* Captures a screenshot of the video displayed on the large video.
*
* @returns {Function}
*/
export function captureLargeVideoScreenshot() {
return (dispatch: Dispatch<any>, getState: Function): Promise<Object> => {
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);
});
};
}
/**
* Resizes the large video container based on the dimensions provided.
*
* @param {number} width - Width that needs to be applied on the large video container.
* @param {number} height - Height that needs to be applied on the large video container.
* @returns {void}
* @returns {Function}
*/
export function resizeLargeVideo(width: number, height: number) {
return (dispatch: Dispatch<any>, getState: Function) => {
@ -72,7 +124,7 @@ export function selectParticipant() {
/**
* Action to select the participant to be displayed in LargeVideo based on the
* participant id provided. If a partcipant id is not provided, the LargeVideo
* participant id provided. If a participant id is not provided, the LargeVideo
* participant will be selected based on a variety of factors: If there is a
* dominant or pinned speaker, or if there are remote tracks, etc.
*