This component prompts the user for camera access, displays a video preview, and allows the user to watch it again with video controls such as downloading or navigating to specific moments. However, there is an issue where the recorded video seems to be missing duration information, causing the <video>
element not to know the length of the video. How can we prevent this problem? Any suggestions for improving the code are welcome. Thank you!
export default function RecordVideo() {
const [state, setState] = createSignal<State>("idle");
const [blob, setBlob] = createSignal<Blob | null>(null);
const [count, setCount] = createSignal<number>(0);
const [message, setMessage] = createSignal<string>("start recording");
let preview: { srcObject: MediaStream };
let recorder: MediaRecorder;
let interval: NodeJS.Timer;
const startRecording = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true,
});
setState("recording");
preview.srcObject = stream;
recorder = new MediaRecorder(stream);
const chunks: Blob[] = [];
recorder.ondataavailable = ({ data }) => chunks.push(data);
recorder.onstop = () => {
setBlob(new Blob(chunks, { type: "video/mp4" }));
};
recorder.start();
interval = setInterval(() => setCount(prev => prev + 1), 1000);
} catch ({ message }: any) {
setMessage(message);
}
};
const stopRecording = () => {
preview.srcObject.getTracks().forEach(track => track.stop());
recorder.stop();
clearInterval(interval);
setState("stopped");
};
const deleteRecording = () => {
setBlob(null);
setCount(0);
setState("idle");
};
const downloadRecording = () =>
Object.assign(document.createElement("a"), {
href: URL.createObjectURL(blob()),
download: "a.mp4",
type: "video/mp4",
}).click();
onCleanup(() => {
clearInterval(interval);
preview.srcObject?.getTracks().forEach(track => track.stop());
});
return (
<section>
<Switch>
<Match when={state() === "idle"}>
<div onClick={startRecording}>
{message()}
</div>
</Match>
<Match when={state() === "recording"}>
<div>
<video
ref={preview}
autoplay
muted
/>
<div>
<span>{formatCount(count())}</span>
<button onClick={stopRecording}>
stop
</button>
</div>
</div>
</Match>
<Match when={state() === "stopped" && blob()}>
<div>
<video
src={URL.createObjectURL(blob())}
autoplay
controls
/>
<div>
<button onClick={deleteRecording}>delete</button>
<button onClick={downloadRecording}>
download
</button>
</div>
</div>
</Match>
</Switch>
</section>
);
}