UNPKG

extendable-media-recorder

Version:

An extendable drop-in replacement for the native MediaRecorder.

121 lines 6.12 kB
export const createNativeMediaRecorderFactory = (createNotSupportedError) => (nativeMediaRecorderConstructor, stream, mediaRecorderOptions) => { const bufferedBlobEventListeners = new Map(); const dataAvailableListeners = new WeakMap(); const errorListeners = new WeakMap(); const flags = []; const nativeMediaRecorder = new nativeMediaRecorderConstructor(stream, mediaRecorderOptions); const stopListeners = new WeakMap(); nativeMediaRecorder.addEventListener('stop', ({ isTrusted }) => { if (isTrusted) { setTimeout(() => flags.shift()); } }); nativeMediaRecorder.addEventListener = ((addEventListener) => { return (type, listener, options) => { let patchedEventListener = listener; if (typeof listener === 'function') { if (type === 'dataavailable') { const bufferedBlobEvents = []; // Bug #20: Firefox dispatches multiple dataavailable events while being inactive. patchedEventListener = (event) => { const [[isSliced, isActive] = [false, false]] = flags; if (isSliced && !isActive) { bufferedBlobEvents.push(event); } else { listener.call(nativeMediaRecorder, event); } }; bufferedBlobEventListeners.set(listener, bufferedBlobEvents); dataAvailableListeners.set(listener, patchedEventListener); } else if (type === 'error') { // Bug #12 & #13: Firefox fires a regular event with an error property. patchedEventListener = (event) => { if (event instanceof ErrorEvent) { listener.call(nativeMediaRecorder, event); } else { listener.call(nativeMediaRecorder, new ErrorEvent('error', { error: event.error })); } }; errorListeners.set(listener, patchedEventListener); } else if (type === 'stop') { // Bug #20: Firefox dispatches multiple dataavailable events while being inactive. patchedEventListener = (event) => { for (const [dataAvailableListener, bufferedBlobEvents] of bufferedBlobEventListeners.entries()) { if (bufferedBlobEvents.length > 0) { const [blobEvent] = bufferedBlobEvents; if (bufferedBlobEvents.length > 1) { Object.defineProperty(blobEvent, 'data', { value: new Blob(bufferedBlobEvents.map(({ data }) => data), { type: blobEvent.data.type }) }); } bufferedBlobEvents.length = 0; dataAvailableListener.call(nativeMediaRecorder, blobEvent); } } listener.call(nativeMediaRecorder, event); }; stopListeners.set(listener, patchedEventListener); } } return addEventListener.call(nativeMediaRecorder, type, patchedEventListener, options); }; })(nativeMediaRecorder.addEventListener); nativeMediaRecorder.removeEventListener = ((removeEventListener) => { return (type, listener, options) => { let patchedEventListener = listener; if (typeof listener === 'function') { if (type === 'dataavailable') { bufferedBlobEventListeners.delete(listener); const dataAvailableListener = dataAvailableListeners.get(listener); if (dataAvailableListener !== undefined) { patchedEventListener = dataAvailableListener; } } else if (type === 'error') { const errorListener = errorListeners.get(listener); if (errorListener !== undefined) { patchedEventListener = errorListener; } } else if (type === 'stop') { const stopListener = stopListeners.get(listener); if (stopListener !== undefined) { patchedEventListener = stopListener; } } } return removeEventListener.call(nativeMediaRecorder, type, patchedEventListener, options); }; })(nativeMediaRecorder.removeEventListener); nativeMediaRecorder.start = ((start) => { return (timeslice) => { /* * Bug #6: Safari will emit a blob without any data when asked to encode a MediaStream with a video track into an audio * codec. */ if (mediaRecorderOptions.mimeType !== undefined && mediaRecorderOptions.mimeType.startsWith('audio/') && stream.getVideoTracks().length > 0) { throw createNotSupportedError(); } if (nativeMediaRecorder.state === 'inactive') { flags.push([timeslice !== undefined, true]); } return timeslice === undefined ? start.call(nativeMediaRecorder) : start.call(nativeMediaRecorder, timeslice); }; })(nativeMediaRecorder.start); nativeMediaRecorder.stop = ((stop) => { return () => { if (nativeMediaRecorder.state !== 'inactive') { flags[0][1] = false; } stop.call(nativeMediaRecorder); }; })(nativeMediaRecorder.stop); return nativeMediaRecorder; }; //# sourceMappingURL=native-media-recorder-factory.js.map