diff --git a/openvidu-browser/package.json b/openvidu-browser/package.json index b9be6b38..49caaf30 100644 --- a/openvidu-browser/package.json +++ b/openvidu-browser/package.json @@ -4,6 +4,7 @@ "freeice": "2.2.2", "hark": "1.2.3", "jsnlog": "2.30.0", + "mime-types": "2.1.34", "platform": "1.3.6", "semver": "7.3.5", "uuid": "8.3.2", @@ -13,6 +14,7 @@ "devDependencies": { "@types/node": "17.0.8", "@types/platform": "1.3.4", + "@types/mime-types": "2.1.1", "browserify": "17.0.0", "grunt": "1.4.1", "grunt-cli": "1.4.3", diff --git a/openvidu-browser/src/OpenVidu/LocalRecorder.ts b/openvidu-browser/src/OpenVidu/LocalRecorder.ts index 73260bdd..feb85073 100644 --- a/openvidu-browser/src/OpenVidu/LocalRecorder.ts +++ b/openvidu-browser/src/OpenVidu/LocalRecorder.ts @@ -19,12 +19,8 @@ import { Stream } from './Stream'; import { LocalRecorderState } from '../OpenViduInternal/Enums/LocalRecorderState'; import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; +import * as mime from 'mime-types'; - -/** - * @hidden - */ -declare var MediaRecorder: any; /** * @hidden */ @@ -38,17 +34,13 @@ let platform: PlatformUtils; /** * Easy recording of [[Stream]] objects straightaway from the browser. Initialized with [[OpenVidu.initLocalRecorder]] method - * - * > WARNINGS: - * - Performing browser local recording of **remote streams** may cause some troubles. A long waiting time may be required after calling _LocalRecorder.stop()_ in this case - * - Only Chrome and Firefox support local stream recording */ export class LocalRecorder { state: LocalRecorderState; private connectionId: string; - private mediaRecorder: any; + private mediaRecorder: MediaRecorder; private chunks: any[] = []; private blob?: Blob; private id: string; @@ -69,46 +61,54 @@ export class LocalRecorder { /** * Starts the recording of the Stream. [[state]] property must be `READY`. After method succeeds is set to `RECORDING` * - * @param mimeType The [MediaRecorder.mimeType](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/mimeType) to be used to record this Stream. - * Make sure the platform supports it or the promise will return an error. If this parameter is not provided, the MediaRecorder will use the default codecs available in the platform + * @param options The [MediaRecorder.options](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder#parameters) to be used to record this Stream. + * For example: + * + * ```javascript + * var OV = new OpenVidu(); + * var publisher = await OV.initPublisherAsync(); + * var localRecorder = OV.initLocalRecorder(publisher.stream); + * var options = { + * mimeType: 'video/webm;codecs=vp8', + * audioBitsPerSecond:128000, + * videoBitsPerSecond:2500000 + * }; + * localRecorder.record(options); + * ``` + * + * If not specified, the default options preferred by the platform will be used. * * @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording successfully started and rejected with an Error object if not */ - record(mimeType?: string): Promise { + record(options?: any): Promise { return new Promise((resolve, reject) => { try { if (typeof MediaRecorder === 'undefined') { - logger.error('MediaRecorder not supported on your browser. See compatibility in https://caniuse.com/#search=MediaRecorder'); - throw (Error('MediaRecorder not supported on your browser. See compatibility in https://caniuse.com/#search=MediaRecorder')); + logger.error('MediaRecorder not supported on your device. See compatibility in https://caniuse.com/#search=MediaRecorder'); + throw (Error('MediaRecorder not supported on your device. See compatibility in https://caniuse.com/#search=MediaRecorder')); } if (this.state !== LocalRecorderState.READY) { throw (Error('\'LocalRecord.record()\' needs \'LocalRecord.state\' to be \'READY\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.clean()\' or init a new LocalRecorder before')); } logger.log("Starting local recording of stream '" + this.stream.streamId + "' of connection '" + this.connectionId + "'"); - let options = {}; - if (typeof MediaRecorder.isTypeSupported === 'function') { - if (!!mimeType) { - if (!MediaRecorder.isTypeSupported(mimeType)) { - return reject(new Error('mimeType "' + mimeType + '" is not supported')); - } - options = { mimeType }; - } else { - logger.log('No mimeType parameter provided. Using default codecs'); - } - } else { - logger.warn('MediaRecorder#isTypeSupported is not supported. Using default codecs'); + if (!options) { + options = { mimeType: 'video/webm' }; + } else if (!options.mimeType) { + options.mimeType = 'video/webm'; } this.mediaRecorder = new MediaRecorder(this.stream.getMediaStream(), options); - this.mediaRecorder.start(10); + this.mediaRecorder.start(); } catch (err) { return reject(err); } this.mediaRecorder.ondataavailable = (e) => { - this.chunks.push(e.data); + if (e.data.size > 0) { + this.chunks.push(e.data); + } }; this.mediaRecorder.onerror = (e) => { @@ -131,10 +131,6 @@ export class LocalRecorder { logger.log('MediaRecorder resumed (state=' + this.mediaRecorder.state + ')'); }; - this.mediaRecorder.onwarning = (e) => { - logger.log('MediaRecorder warning: ' + e); - }; - this.state = LocalRecorderState.RECORDING; return resolve(); @@ -243,7 +239,6 @@ export class LocalRecorder { const f = () => { delete this.blob; this.chunks = []; - delete this.mediaRecorder; this.state = LocalRecorderState.READY; }; if (this.state === LocalRecorderState.RECORDING || this.state === LocalRecorderState.PAUSED) { @@ -267,7 +262,7 @@ export class LocalRecorder { const url = window.URL.createObjectURL(this.blob); a.href = url; - a.download = this.id + '.webm'; + a.download = this.id + '.' + mime.extension(this.blob!.type); a.click(); window.URL.revokeObjectURL(url); @@ -352,7 +347,7 @@ export class LocalRecorder { } const sendable = new FormData(); - sendable.append('file', this.blob!, this.id + '.webm'); + sendable.append('file', this.blob!, this.id + '.' + mime.extension(this.blob!.type)); http.onreadystatechange = () => { if (http.readyState === 4) { @@ -376,7 +371,7 @@ export class LocalRecorder { private onStopDefault(): void { logger.log('MediaRecorder stopped (state=' + this.mediaRecorder.state + ')'); - this.blob = new Blob(this.chunks, { type: 'video/webm' }); + this.blob = new Blob(this.chunks, { type: this.mediaRecorder.mimeType }); this.chunks = []; this.videoPreviewSrc = window.URL.createObjectURL(this.blob);