extendable-media-recorder
Version:
An extendable drop-in replacement for the native MediaRecorder.
238 lines (186 loc) • 8.35 kB
text/typescript
import { IMediaRecorder, IMediaRecorderEventMap, IMediaRecorderOptions } from '../interfaces';
import {
TBlobEventHandler,
TErrorEventHandler,
TEventHandler,
TMediaRecorderConstructorFactory,
TNativeEventTarget,
TRecordingState
} from '../types';
export const createMediaRecorderConstructor: TMediaRecorderConstructorFactory = (
createNativeMediaRecorder,
createNotSupportedError,
createWebAudioMediaRecorder,
createWebmPcmMediaRecorder,
encoderRegexes,
eventTargetConstructor,
nativeMediaRecorderConstructor
) => {
return class MediaRecorder extends eventTargetConstructor<IMediaRecorderEventMap> implements IMediaRecorder {
private _internalMediaRecorder: Omit<
IMediaRecorder,
'ondataavailable' | 'onerror' | 'onpause' | 'onresume' | 'onstart' | 'onstop' | keyof TNativeEventTarget
>;
private _ondataavailable: null | [TBlobEventHandler<this>, TBlobEventHandler<this>];
private _onerror: null | [TErrorEventHandler<this>, TErrorEventHandler<this>];
private _onpause: null | [TEventHandler<this>, TEventHandler<this>];
private _onresume: null | [TEventHandler<this>, TEventHandler<this>];
private _onstart: null | [TEventHandler<this>, TEventHandler<this>];
private _onstop: null | [TEventHandler<this>, TEventHandler<this>];
constructor(stream: MediaStream, options: IMediaRecorderOptions = {}) {
const { mimeType } = options;
if (
nativeMediaRecorderConstructor !== null &&
// Bug #10: Safari does not yet implement the isTypeSupported() method.
(mimeType === undefined ||
(nativeMediaRecorderConstructor.isTypeSupported !== undefined &&
nativeMediaRecorderConstructor.isTypeSupported(mimeType)))
) {
const internalMediaRecorder = createNativeMediaRecorder(nativeMediaRecorderConstructor, stream, options);
super(internalMediaRecorder);
this._internalMediaRecorder = internalMediaRecorder;
} else if (mimeType !== undefined && encoderRegexes.some((regex) => regex.test(mimeType))) {
super();
// Bug #10: Safari does not yet implement the isTypeSupported() method.
if (
nativeMediaRecorderConstructor !== null &&
nativeMediaRecorderConstructor.isTypeSupported !== undefined &&
nativeMediaRecorderConstructor.isTypeSupported('audio/webm;codecs=pcm')
) {
this._internalMediaRecorder = createWebmPcmMediaRecorder(this, nativeMediaRecorderConstructor, stream, mimeType);
} else {
this._internalMediaRecorder = createWebAudioMediaRecorder(this, stream, mimeType);
}
} else {
// This is creating a native MediaRecorder just to provoke it to throw an error.
if (nativeMediaRecorderConstructor !== null) {
createNativeMediaRecorder(nativeMediaRecorderConstructor, stream, options);
}
throw createNotSupportedError();
}
this._ondataavailable = null;
this._onerror = null;
this._onpause = null;
this._onresume = null;
this._onstart = null;
this._onstop = null;
}
get mimeType(): string {
return this._internalMediaRecorder.mimeType;
}
get ondataavailable(): null | TBlobEventHandler<this> {
return this._ondataavailable === null ? this._ondataavailable : this._ondataavailable[0];
}
set ondataavailable(value) {
if (this._ondataavailable !== null) {
this.removeEventListener('dataavailable', this._ondataavailable[1]);
}
if (typeof value === 'function') {
const boundListener = value.bind(this);
this.addEventListener('dataavailable', boundListener);
this._ondataavailable = [value, boundListener];
} else {
this._ondataavailable = null;
}
}
get onerror(): null | TErrorEventHandler<this> {
return this._onerror === null ? this._onerror : this._onerror[0];
}
set onerror(value) {
if (this._onerror !== null) {
this.removeEventListener('error', this._onerror[1]);
}
if (typeof value === 'function') {
const boundListener = value.bind(this);
this.addEventListener('error', boundListener);
this._onerror = [value, boundListener];
} else {
this._onerror = null;
}
}
get onpause(): null | TEventHandler<this> {
return this._onpause === null ? this._onpause : this._onpause[0];
}
set onpause(value) {
if (this._onpause !== null) {
this.removeEventListener('pause', this._onpause[1]);
}
if (typeof value === 'function') {
const boundListener = value.bind(this);
this.addEventListener('pause', boundListener);
this._onpause = [value, boundListener];
} else {
this._onpause = null;
}
}
get onresume(): null | TEventHandler<this> {
return this._onresume === null ? this._onresume : this._onresume[0];
}
set onresume(value) {
if (this._onresume !== null) {
this.removeEventListener('resume', this._onresume[1]);
}
if (typeof value === 'function') {
const boundListener = value.bind(this);
this.addEventListener('resume', boundListener);
this._onresume = [value, boundListener];
} else {
this._onresume = null;
}
}
get onstart(): null | TEventHandler<this> {
return this._onstart === null ? this._onstart : this._onstart[0];
}
set onstart(value) {
if (this._onstart !== null) {
this.removeEventListener('start', this._onstart[1]);
}
if (typeof value === 'function') {
const boundListener = value.bind(this);
this.addEventListener('start', boundListener);
this._onstart = [value, boundListener];
} else {
this._onstart = null;
}
}
get onstop(): null | TEventHandler<this> {
return this._onstop === null ? this._onstop : this._onstop[0];
}
set onstop(value) {
if (this._onstop !== null) {
this.removeEventListener('stop', this._onstop[1]);
}
if (typeof value === 'function') {
const boundListener = value.bind(this);
this.addEventListener('stop', boundListener);
this._onstop = [value, boundListener];
} else {
this._onstop = null;
}
}
get state(): TRecordingState {
return this._internalMediaRecorder.state;
}
public pause(): void {
return this._internalMediaRecorder.pause();
}
public resume(): void {
return this._internalMediaRecorder.resume();
}
public start(timeslice?: number): void {
return this._internalMediaRecorder.start(timeslice);
}
public stop(): void {
return this._internalMediaRecorder.stop();
}
public static isTypeSupported(mimeType: string): boolean {
return (
(nativeMediaRecorderConstructor !== null &&
// Bug #10: Safari does not yet implement the isTypeSupported() method.
nativeMediaRecorderConstructor.isTypeSupported !== undefined &&
nativeMediaRecorderConstructor.isTypeSupported(mimeType)) ||
encoderRegexes.some((regex) => regex.test(mimeType))
);
}
};
};