Browse Source

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

j8
Jaya Allamsetty 4 years ago
parent
commit
1d5decc14f
No account linked to committer's email address
3 changed files with 86 additions and 3 deletions
  1. 20
    1
      modules/API/API.js
  2. 12
    0
      modules/API/external/external_api.js
  3. 54
    2
      react/features/large-video/actions.js

+ 20
- 1
modules/API/API.js View File

21
 import { isEnabled as isDropboxEnabled } from '../../react/features/dropbox';
21
 import { isEnabled as isDropboxEnabled } from '../../react/features/dropbox';
22
 import { toggleE2EE } from '../../react/features/e2ee/actions';
22
 import { toggleE2EE } from '../../react/features/e2ee/actions';
23
 import { invite } from '../../react/features/invite';
23
 import { invite } from '../../react/features/invite';
24
-import { resizeLargeVideo, selectParticipantInLargeVideo } from '../../react/features/large-video/actions';
24
+import {
25
+    captureLargeVideoScreenshot,
26
+    resizeLargeVideo,
27
+    selectParticipantInLargeVideo
28
+} from '../../react/features/large-video/actions';
25
 import { toggleLobbyMode } from '../../react/features/lobby/actions.web';
29
 import { toggleLobbyMode } from '../../react/features/lobby/actions.web';
26
 import { RECORDING_TYPES } from '../../react/features/recording/constants';
30
 import { RECORDING_TYPES } from '../../react/features/recording/constants';
27
 import { getActiveSession } from '../../react/features/recording/functions';
31
 import { getActiveSession } from '../../react/features/recording/functions';
339
         const { name } = request;
343
         const { name } = request;
340
 
344
 
341
         switch (name) {
345
         switch (name) {
346
+        case 'capture-largevideo-screenshot' :
347
+            APP.store.dispatch(captureLargeVideoScreenshot())
348
+                .then(dataURL => {
349
+                    let error;
350
+
351
+                    if (!dataURL) {
352
+                        error = new Error('No large video found!');
353
+                    }
354
+
355
+                    callback({
356
+                        error,
357
+                        dataURL
358
+                    });
359
+                });
360
+            break;
342
         case 'invite': {
361
         case 'invite': {
343
             const { invitees } = request;
362
             const { invitees } = request;
344
 
363
 

+ 12
- 0
modules/API/external/external_api.js View File

636
         }
636
         }
637
     }
637
     }
638
 
638
 
639
+    /**
640
+     * Captures the screenshot of the large video.
641
+     *
642
+     * @returns {dataURL} - Base64 encoded image data of the screenshot if large
643
+     * video is detected, an error otherwise.
644
+     */
645
+    captureLargeVideoScreenshot() {
646
+        return this._transport.sendRequest({
647
+            name: 'capture-largevideo-screenshot'
648
+        });
649
+    }
650
+
639
     /**
651
     /**
640
      * Removes the listeners and removes the Jitsi Meet frame.
652
      * Removes the listeners and removes the Jitsi Meet frame.
641
      *
653
      *

+ 54
- 2
react/features/large-video/actions.js View File

10
 import { _handleParticipantError } from '../base/conference';
10
 import { _handleParticipantError } from '../base/conference';
11
 import { MEDIA_TYPE } from '../base/media';
11
 import { MEDIA_TYPE } from '../base/media';
12
 import { getParticipants } from '../base/participants';
12
 import { getParticipants } from '../base/participants';
13
+import { getTrackByMediaTypeAndParticipant } from '../base/tracks';
13
 import { reportError } from '../base/util';
14
 import { reportError } from '../base/util';
14
 import { shouldDisplayTileView } from '../video-layout';
15
 import { shouldDisplayTileView } from '../video-layout';
15
 
16
 
20
 
21
 
21
 declare var APP: Object;
22
 declare var APP: Object;
22
 
23
 
24
+/**
25
+* Captures a screenshot of the video displayed on the large video.
26
+*
27
+* @returns {Function}
28
+*/
29
+export function captureLargeVideoScreenshot() {
30
+    return (dispatch: Dispatch<any>, getState: Function): Promise<Object> => {
31
+        const state = getState();
32
+        const largeVideo = state['features/large-video'];
33
+
34
+        if (!largeVideo) {
35
+            return Promise.resolve();
36
+        }
37
+        const tracks = state['features/base/tracks'];
38
+        const { jitsiTrack } = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, largeVideo.participantId);
39
+        const videoStream = jitsiTrack.getOriginalStream();
40
+
41
+        // Create a HTML canvas and draw video from the track on to the canvas.
42
+        const [ track ] = videoStream.getVideoTracks();
43
+        const { height, width } = track.getSettings() ?? track.getConstraints();
44
+        const canvasElement = document.createElement('canvas');
45
+        const ctx = canvasElement.getContext('2d');
46
+        const videoElement = document.createElement('video');
47
+
48
+        videoElement.height = parseInt(height, 10);
49
+        videoElement.width = parseInt(width, 10);
50
+        videoElement.autoplay = true;
51
+        videoElement.srcObject = videoStream;
52
+        canvasElement.height = videoElement.height;
53
+        canvasElement.width = videoElement.width;
54
+
55
+        // Wait for the video to load before drawing on to the canvas.
56
+        const promise = new Promise(resolve => {
57
+            videoElement.onloadeddata = () => resolve();
58
+        });
59
+
60
+        return promise.then(() => {
61
+            ctx.drawImage(videoElement, 0, 0, videoElement.width, videoElement.height);
62
+            const dataURL = canvasElement.toDataURL('image/png', 1.0);
63
+
64
+            // Cleanup.
65
+            ctx.clearRect(0, 0, videoElement.width, videoElement.height);
66
+            videoElement.srcObject = null;
67
+            canvasElement.remove();
68
+            videoElement.remove();
69
+
70
+            return Promise.resolve(dataURL);
71
+        });
72
+    };
73
+}
74
+
23
 /**
75
 /**
24
  * Resizes the large video container based on the dimensions provided.
76
  * Resizes the large video container based on the dimensions provided.
25
  *
77
  *
26
  * @param {number} width - Width that needs to be applied on the large video container.
78
  * @param {number} width - Width that needs to be applied on the large video container.
27
  * @param {number} height - Height that needs to be applied on the large video container.
79
  * @param {number} height - Height that needs to be applied on the large video container.
28
- * @returns {void}
80
+ * @returns {Function}
29
  */
81
  */
30
 export function resizeLargeVideo(width: number, height: number) {
82
 export function resizeLargeVideo(width: number, height: number) {
31
     return (dispatch: Dispatch<any>, getState: Function) => {
83
     return (dispatch: Dispatch<any>, getState: Function) => {
72
 
124
 
73
 /**
125
 /**
74
  * Action to select the participant to be displayed in LargeVideo based on the
126
  * Action to select the participant to be displayed in LargeVideo based on the
75
- * participant id provided. If a partcipant id is not provided, the LargeVideo
127
+ * participant id provided. If a participant id is not provided, the LargeVideo
76
  * participant will be selected based on a variety of factors: If there is a
128
  * participant will be selected based on a variety of factors: If there is a
77
  * dominant or pinned speaker, or if there are remote tracks, etc.
129
  * dominant or pinned speaker, or if there are remote tracks, etc.
78
  *
130
  *

Loading…
Cancel
Save