|
@@ -10,6 +10,7 @@ import {
|
10
|
10
|
import { _handleParticipantError } from '../base/conference';
|
11
|
11
|
import { MEDIA_TYPE } from '../base/media';
|
12
|
12
|
import { getParticipants } from '../base/participants';
|
|
13
|
+import { getTrackByMediaTypeAndParticipant } from '../base/tracks';
|
13
|
14
|
import { reportError } from '../base/util';
|
14
|
15
|
import { shouldDisplayTileView } from '../video-layout';
|
15
|
16
|
|
|
@@ -20,12 +21,63 @@ import {
|
20
|
21
|
|
21
|
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
|
76
|
* Resizes the large video container based on the dimensions provided.
|
25
|
77
|
*
|
26
|
78
|
* @param {number} width - Width that needs to be applied on the large video container.
|
27
|
79
|
* @param {number} height - Height that needs to be applied on the large video container.
|
28
|
|
- * @returns {void}
|
|
80
|
+ * @returns {Function}
|
29
|
81
|
*/
|
30
|
82
|
export function resizeLargeVideo(width: number, height: number) {
|
31
|
83
|
return (dispatch: Dispatch<any>, getState: Function) => {
|
|
@@ -72,7 +124,7 @@ export function selectParticipant() {
|
72
|
124
|
|
73
|
125
|
/**
|
74
|
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
|
128
|
* participant will be selected based on a variety of factors: If there is a
|
77
|
129
|
* dominant or pinned speaker, or if there are remote tracks, etc.
|
78
|
130
|
*
|