AutorÃa | Ultima modificación | Ver Log |
// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** Tiny Record RTC - Screen recorder configuration.** @module tiny_recordrtc/screen_recorder* @copyright 2024 The Open University* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/import BaseClass from './base_recorder';import Modal from 'tiny_recordrtc/modal';import {component} from 'tiny_recordrtc/common';import {getString} from 'core/str';export default class Screen extends BaseClass {configurePlayer() {return this.modalRoot.querySelector('video');}getSupportedTypes() {return [// Support webm as a preference.// This container supports both vp9, and vp8.// It does not support AVC1/h264 at all.// It is supported by Chromium, and Firefox browsers, but not Safari.'video/webm;codecs=vp9,opus','video/webm;codecs=vp8,opus',// Fall back to mp4 if webm is not available.// The mp4 container supports v9, and h264 but neither of these are supported for recording on other// browsers.// In addition to this, we can record in v9, but VideoJS does not support a mp4 container with v9 codec// for playback. We leave it as a final option as a just-in-case.'video/mp4;codecs=h264,opus','video/mp4;codecs=h264,wav','video/mp4;codecs=v9,opus',];}getRecordingOptions() {return {videoBitsPerSecond: parseInt(this.config.screenbitrate),videoWidth: parseInt(this.config.videoscreenwidth),videoHeight: parseInt(this.config.videoscreenheight),};}getMediaConstraints() {return {audio: true,systemAudio: 'exclude',video: {displaySurface: 'monitor',frameRate: {ideal: 24},width: {max: parseInt(this.config.videoscreenwidth),},height: {max: parseInt(this.config.videoscreenheight),},},};}playOnCapture() {// Play the recording back on capture.return true;}getRecordingType() {return 'screen';}getTimeLimit() {return this.config.screentimelimit;}getEmbedTemplateName() {return 'tiny_recordrtc/embed_screen';}getFileName(prefix) {return `${prefix}-video.${this.getFileExtension()}`;}getFileExtension() {if (window.MediaRecorder.isTypeSupported('audio/webm')) {return 'webm';} else if (window.MediaRecorder.isTypeSupported('audio/mp4')) {return 'mp4';}window.console.warn(`Unknown file type for MediaRecorder API`);return '';}async captureUserMedia() {// Screen recording requires both audio and the screen, and we need to get them both together.const audioPromise = navigator.mediaDevices.getUserMedia({audio: true});const screenPromise = navigator.mediaDevices.getDisplayMedia(this.getMediaConstraints());// If the audioPromise is "rejected" (indicating that the user does not want to share their voice),// we will proceed to record their screen without audio.// Therefore, we will use Promise.allSettled instead of Promise.all.await Promise.allSettled([audioPromise, screenPromise]).then(this.combineAudioAndScreenRecording.bind(this));}/*** For starting screen recording, once we have both audio and video, combine them.** @param {Object[]} results from the above Promise.allSettled call.*/combineAudioAndScreenRecording(results) {const [audioData, screenData] = results;if (screenData.status !== 'fulfilled') {// If the user does not grant screen permission show warning popup.this.handleCaptureFailure(screenData.reason);return;}const screenStream = screenData.value;// Prepare to handle if the user clicks the browser's "Stop Sharing Screen" button.screenStream.getVideoTracks()[0].addEventListener('ended', this.handleStopScreenSharing.bind(this));// Handle microphone.if (audioData.status !== 'fulfilled') {// We could not get audio. In this case, we just continue without audio.this.handleCaptureSuccess(screenStream);return;}const audioStream = audioData.value;// Merge the video track from the media stream with the audio track from the microphone stream// and stop any unnecessary tracks to ensure that the recorded video has microphone sound.const composedStream = new MediaStream();screenStream.getTracks().forEach(function(track) {if (track.kind === 'video') {composedStream.addTrack(track);} else {track.stop();}});audioStream.getAudioTracks().forEach(function(micTrack) {composedStream.addTrack(micTrack);});this.handleCaptureSuccess(composedStream);}/*** Callback that is called by the user clicking Stop screen sharing on the browser.*/handleStopScreenSharing() {if (this.isRecording() || this.isPaused()) {this.requestRecordingStop();this.cleanupStream();} else {this.setRecordButtonState(false);this.displayAlert(getString('screensharingstopped_title', component),getString('screensharingstopped', component));}}handleRecordingStartStopRequested() {if (this.isRecording() || this.isPaused()) {this.requestRecordingStop();this.cleanupStream();} else {this.startRecording();}}static getModalClass() {return class extends Modal {static TYPE = `${component}/screen_recorder`;static TEMPLATE = `${component}/screen_recorder`;};}}