@orbisk/vue-use-media-recorder
Version:
🪝 Vue Composable around MediaRecorder API
113 lines (112 loc) • 3.19 kB
JavaScript
import { computedWithControl, useSupported } from "@vueuse/core";
import { tryOnScopeDispose } from "@vueuse/shared";
import { defu } from "defu";
import { computed, ref, shallowRef, toValue } from "vue";
const defaultOptions = {
constraints: { audio: false, video: false },
mediaRecorderOptions: {},
onStart: () => {
},
onPause: () => {
},
onResume: () => {
},
onStop: () => {
},
onError: () => {
}
};
export function useMediaRecorder(options = {}) {
const data = ref([]);
const mediaRecorder = shallowRef();
const stream = shallowRef();
const isMimeTypeSupported = computed(() => {
return toValue(options.mediaRecorderOptions)?.mimeType ? MediaRecorder.isTypeSupported(toValue(options.mediaRecorderOptions)?.mimeType ?? "") : true;
});
const isSupported = useSupported(() => {
return !!navigator?.mediaDevices?.getUserMedia && isMimeTypeSupported.value;
});
const state = computedWithControl(() => mediaRecorder.value, () => {
return mediaRecorder.value?.state;
});
const mimeType = computedWithControl(() => mediaRecorder.value, () => {
return mediaRecorder.value?.mimeType;
});
const {
mediaRecorderOptions,
constraints
} = defu(options, defaultOptions);
const setupMediaRecorder = (newMediaRecorder) => {
newMediaRecorder.ondataavailable = (e) => {
mimeType.trigger();
data.value.push(e.data);
};
newMediaRecorder.onstop = (...args) => {
stream.value?.getTracks().forEach((t) => t.stop());
state.trigger();
mimeType.trigger();
options.onStop?.(...args);
};
newMediaRecorder.onpause = (...args) => {
state.trigger();
mimeType.trigger();
options.onPause?.(...args);
};
newMediaRecorder.onresume = (...args) => {
state.trigger();
mimeType.trigger();
options.onResume?.(...args);
};
newMediaRecorder.onstart = (...args) => {
state.trigger();
mimeType.trigger();
options.onStart?.(...args);
};
newMediaRecorder.onerror = (...args) => {
state.trigger();
mimeType.trigger();
options.onError?.(...args);
};
};
const start = async (timeslice = void 0) => {
if (state.value === "recording")
return;
data.value = [];
stream.value = await navigator.mediaDevices.getUserMedia(toValue(constraints));
const mr = new MediaRecorder(stream.value, toValue(mediaRecorderOptions));
setupMediaRecorder(mr);
mediaRecorder.value = mr;
mediaRecorder.value?.start(timeslice);
};
const stop = () => {
if (!state.value || state.value === "inactive")
return;
mediaRecorder.value?.stop();
};
const pause = () => {
if (state.value !== "recording")
return;
mediaRecorder.value?.pause();
};
const resume = () => {
if (state.value !== "paused")
return;
mediaRecorder.value?.resume();
};
tryOnScopeDispose(() => {
mediaRecorder.value?.stop();
});
return {
start,
pause,
resume,
stop,
data,
stream,
state,
isSupported,
isMimeTypeSupported,
mimeType: computed(() => mimeType.value),
mediaRecorder: computed(() => mediaRecorder.value)
};
}