Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
{"version":3,"file":"screen_recorder.min.js","sources":["../src/screen_recorder.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Record RTC - Screen recorder configuration.\n *\n * @module      tiny_recordrtc/screen_recorder\n * @copyright   2024 The Open University\n * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseClass from './base_recorder';\nimport Modal from 'tiny_recordrtc/modal';\nimport {component} from 'tiny_recordrtc/common';\nimport {getString} from 'core/str';\n\nexport default class Screen extends BaseClass {\n    configurePlayer() {\n        return this.modalRoot.querySelector('video');\n    }\n\n    getSupportedTypes() {\n        return [\n            // Support webm as a preference.\n            // This container supports both vp9, and vp8.\n            // It does not support AVC1/h264 at all.\n            // It is supported by Chromium, and Firefox browsers, but not Safari.\n            'video/webm;codecs=vp9,opus',\n            'video/webm;codecs=vp8,opus',\n\n            // Fall back to mp4 if webm is not available.\n            // The mp4 container supports v9, and h264 but neither of these are supported for recording on other\n            // browsers.\n            // In addition to this, we can record in v9, but VideoJS does not support a mp4 container with v9 codec\n            // for playback. We leave it as a final option as a just-in-case.\n            'video/mp4;codecs=h264,opus',\n            'video/mp4;codecs=h264,wav',\n            'video/mp4;codecs=v9,opus',\n        ];\n\n    }\n\n    getRecordingOptions() {\n        return {\n            videoBitsPerSecond: parseInt(this.config.screenbitrate),\n            videoWidth: parseInt(this.config.videoscreenwidth),\n            videoHeight: parseInt(this.config.videoscreenheight),\n        };\n    }\n\n    getMediaConstraints() {\n        return {\n            audio: true,\n            systemAudio: 'exclude',\n            video: {\n                displaySurface: 'monitor',\n                frameRate: {ideal: 24},\n                width: {\n                    max: parseInt(this.config.videoscreenwidth),\n                },\n                height: {\n                    max: parseInt(this.config.videoscreenheight),\n                },\n            },\n        };\n    }\n\n    playOnCapture() {\n        // Play the recording back on capture.\n        return true;\n    }\n\n    getRecordingType() {\n        return 'screen';\n    }\n\n    getTimeLimit() {\n        return this.config.screentimelimit;\n    }\n\n    getEmbedTemplateName() {\n        return 'tiny_recordrtc/embed_screen';\n    }\n\n    getFileName(prefix) {\n        return `${prefix}-video.${this.getFileExtension()}`;\n    }\n\n    getFileExtension() {\n        if (window.MediaRecorder.isTypeSupported('audio/webm')) {\n            return 'webm';\n        } else if (window.MediaRecorder.isTypeSupported('audio/mp4')) {\n            return 'mp4';\n        }\n\n        window.console.warn(`Unknown file type for MediaRecorder API`);\n        return '';\n    }\n\n    async captureUserMedia() {\n        // Screen recording requires both audio and the screen, and we need to get them both together.\n        const audioPromise = navigator.mediaDevices.getUserMedia({audio: true});\n        const screenPromise = navigator.mediaDevices.getDisplayMedia(this.getMediaConstraints());\n        // If the audioPromise is \"rejected\" (indicating that the user does not want to share their voice),\n        // we will proceed to record their screen without audio.\n        // Therefore, we will use Promise.allSettled instead of Promise.all.\n        await Promise.allSettled([audioPromise, screenPromise]).then(this.combineAudioAndScreenRecording.bind(this));\n    }\n\n    /**\n     * For starting screen recording, once we have both audio and video, combine them.\n     *\n     * @param {Object[]} results from the above Promise.allSettled call.\n     */\n    combineAudioAndScreenRecording(results) {\n        const [audioData, screenData] = results;\n        if (screenData.status !== 'fulfilled') {\n            // If the user does not grant screen permission show warning popup.\n            this.handleCaptureFailure(screenData.reason);\n            return;\n        }\n\n        const screenStream = screenData.value;\n        // Prepare to handle if the user clicks the browser's \"Stop Sharing Screen\" button.\n        screenStream.getVideoTracks()[0].addEventListener('ended', this.handleStopScreenSharing.bind(this));\n\n        // Handle microphone.\n        if (audioData.status !== 'fulfilled') {\n            // We could not get audio. In this case, we just continue without audio.\n            this.handleCaptureSuccess(screenStream);\n            return;\n        }\n        const audioStream = audioData.value;\n        // Merge the video track from the media stream with the audio track from the microphone stream\n        // and stop any unnecessary tracks to ensure that the recorded video has microphone sound.\n        const composedStream = new MediaStream();\n        screenStream.getTracks().forEach(function(track) {\n            if (track.kind === 'video') {\n                composedStream.addTrack(track);\n            } else {\n                track.stop();\n            }\n        });\n        audioStream.getAudioTracks().forEach(function(micTrack) {\n            composedStream.addTrack(micTrack);\n        });\n\n        this.handleCaptureSuccess(composedStream);\n    }\n\n    /**\n     * Callback that is called by the user clicking Stop screen sharing on the browser.\n     */\n    handleStopScreenSharing() {\n        if (this.isRecording() || this.isPaused()) {\n            this.requestRecordingStop();\n            this.cleanupStream();\n        } else {\n            this.setRecordButtonState(false);\n            this.displayAlert(\n                getString('screensharingstopped_title', component),\n                getString('screensharingstopped', component)\n            );\n        }\n    }\n\n    handleRecordingStartStopRequested() {\n        if (this.isRecording() || this.isPaused()) {\n            this.requestRecordingStop();\n            this.cleanupStream();\n        } else {\n            this.startRecording();\n        }\n    }\n\n    static getModalClass() {\n        return class extends Modal {\n            static TYPE = `${component}/screen_recorder`;\n            static TEMPLATE = `${component}/screen_recorder`;\n        };\n    }\n}\n"],"names":["Screen","BaseClass","configurePlayer","this","modalRoot","querySelector","getSupportedTypes","getRecordingOptions","videoBitsPerSecond","parseInt","config","screenbitrate","videoWidth","videoscreenwidth","videoHeight","videoscreenheight","getMediaConstraints","audio","systemAudio","video","displaySurface","frameRate","ideal","width","max","height","playOnCapture","getRecordingType","getTimeLimit","screentimelimit","getEmbedTemplateName","getFileName","prefix","getFileExtension","window","MediaRecorder","isTypeSupported","console","warn","audioPromise","navigator","mediaDevices","getUserMedia","screenPromise","getDisplayMedia","Promise","allSettled","then","combineAudioAndScreenRecording","bind","results","audioData","screenData","status","handleCaptureFailure","reason","screenStream","value","getVideoTracks","addEventListener","handleStopScreenSharing","handleCaptureSuccess","audioStream","composedStream","MediaStream","getTracks","forEach","track","kind","addTrack","stop","getAudioTracks","micTrack","isRecording","isPaused","requestRecordingStop","cleanupStream","setRecordButtonState","displayAlert","component","handleRecordingStartStopRequested","startRecording","Modal"],"mappings":"+lBA4BqBA,eAAeC,uBAChCC,yBACWC,KAAKC,UAAUC,cAAc,SAGxCC,0BACW,CAKH,6BACA,6BAOA,6BACA,4BACA,4BAKRC,4BACW,CACHC,mBAAoBC,SAASN,KAAKO,OAAOC,eACzCC,WAAYH,SAASN,KAAKO,OAAOG,kBACjCC,YAAaL,SAASN,KAAKO,OAAOK,oBAI1CC,4BACW,CACHC,OAAO,EACPC,YAAa,UACbC,MAAO,CACHC,eAAgB,UAChBC,UAAW,CAACC,MAAO,IACnBC,MAAO,CACHC,IAAKf,SAASN,KAAKO,OAAOG,mBAE9BY,OAAQ,CACJD,IAAKf,SAASN,KAAKO,OAAOK,sBAM1CW,uBAEW,EAGXC,yBACW,SAGXC,sBACWzB,KAAKO,OAAOmB,gBAGvBC,6BACW,8BAGXC,YAAYC,wBACEA,yBAAgB7B,KAAK8B,oBAGnCA,0BACQC,OAAOC,cAAcC,gBAAgB,cAC9B,OACAF,OAAOC,cAAcC,gBAAgB,aACrC,OAGXF,OAAOG,QAAQC,gDACR,mCAKDC,aAAeC,UAAUC,aAAaC,aAAa,CAACzB,OAAO,IAC3D0B,cAAgBH,UAAUC,aAAaG,gBAAgBzC,KAAKa,6BAI5D6B,QAAQC,WAAW,CAACP,aAAcI,gBAAgBI,KAAK5C,KAAK6C,+BAA+BC,KAAK9C,OAQ1G6C,+BAA+BE,eACpBC,UAAWC,YAAcF,WACN,cAAtBE,WAAWC,wBAENC,qBAAqBF,WAAWG,cAInCC,aAAeJ,WAAWK,SAEhCD,aAAaE,iBAAiB,GAAGC,iBAAiB,QAASxD,KAAKyD,wBAAwBX,KAAK9C,OAGpE,cAArBgD,UAAUE,wBAELQ,qBAAqBL,oBAGxBM,YAAcX,UAAUM,MAGxBM,eAAiB,IAAIC,YAC3BR,aAAaS,YAAYC,SAAQ,SAASC,OACnB,UAAfA,MAAMC,KACNL,eAAeM,SAASF,OAExBA,MAAMG,UAGdR,YAAYS,iBAAiBL,SAAQ,SAASM,UAC1CT,eAAeM,SAASG,kBAGvBX,qBAAqBE,gBAM9BH,0BACQzD,KAAKsE,eAAiBtE,KAAKuE,iBACtBC,4BACAC,uBAEAC,sBAAqB,QACrBC,cACD,kBAAU,6BAA8BC,oBACxC,kBAAU,uBAAwBA,qBAK9CC,oCACQ7E,KAAKsE,eAAiBtE,KAAKuE,iBACtBC,4BACAC,sBAEAK,iFAKF,cAAcC,kCACAH,mFACIA"}