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 { isEnabled as isDropboxEnabled } from '../../react/features/dropbox';
|
||||||
import { toggleE2EE } from '../../react/features/e2ee/actions';
|
import { toggleE2EE } from '../../react/features/e2ee/actions';
|
||||||
import { invite } from '../../react/features/invite';
|
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 { toggleLobbyMode } from '../../react/features/lobby/actions.web';
|
||||||
import { RECORDING_TYPES } from '../../react/features/recording/constants';
|
import { RECORDING_TYPES } from '../../react/features/recording/constants';
|
||||||
import { getActiveSession } from '../../react/features/recording/functions';
|
import { getActiveSession } from '../../react/features/recording/functions';
|
||||||
|
@ -339,6 +343,21 @@ function initCommands() {
|
||||||
const { name } = request;
|
const { name } = request;
|
||||||
|
|
||||||
switch (name) {
|
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': {
|
case 'invite': {
|
||||||
const { invitees } = request;
|
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.
|
* Removes the listeners and removes the Jitsi Meet frame.
|
||||||
*
|
*
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
import { _handleParticipantError } from '../base/conference';
|
import { _handleParticipantError } from '../base/conference';
|
||||||
import { MEDIA_TYPE } from '../base/media';
|
import { MEDIA_TYPE } from '../base/media';
|
||||||
import { getParticipants } from '../base/participants';
|
import { getParticipants } from '../base/participants';
|
||||||
|
import { getTrackByMediaTypeAndParticipant } from '../base/tracks';
|
||||||
import { reportError } from '../base/util';
|
import { reportError } from '../base/util';
|
||||||
import { shouldDisplayTileView } from '../video-layout';
|
import { shouldDisplayTileView } from '../video-layout';
|
||||||
|
|
||||||
|
@ -20,12 +21,63 @@ import {
|
||||||
|
|
||||||
declare var APP: Object;
|
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.
|
* 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} 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.
|
* @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) {
|
export function resizeLargeVideo(width: number, height: number) {
|
||||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
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
|
* 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
|
* 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.
|
* dominant or pinned speaker, or if there are remote tracks, etc.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue