feat(iFrame): Add a method for capturing screenshot of the large video (#7717)
This commit is contained in:
parent
119b79fd84
commit
1d5decc14f
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue