'React-Webcam Recording, Replay, Re-recording, Storing

Just making a video recording component that you can replay afterwards to see if you like it, if not you just re-record, and finally it will store it in the database. I'm using react-webcam for this, with some functionality I've found online.

I have a handleDownload function which sets the video tag source with the blob I just recorded. Originally it downloaded the video file when clicking the button, but I want the video to be replayable as soon as I stop a recording. Ideally, I want to use the same react-webcam component, but not sure I can do that, so for now this will do.

It works when I set the function to onClick listener to the button, however, it doesn't work when I call the function inside of handleStopCaptureClick so I tried to implement a useEffect which causes handleDownload to run after we stop capturing. This doesn't work either - thoughts? Thanks!

import React, {useEffect} from "react";
import Webcam from "react-webcam";

export const WebcamStreamCapture = () => {
    const webcamRef = React.useRef(null);
    const mediaRecorderRef = React.useRef(null);
    const [capturing, setCapturing] = React.useState(false);
    const [recordedChunks, setRecordedChunks] = React.useState([]);
    const isInitialMount = React.useRef(true);

    useEffect(() => {
      if (isInitialMount.current) {
        isInitialMount.current = false;
      } else {
        if (!capturing) {
          console.log('running handleDownload')
          handleDownload();
        }
      }
    }, [capturing])

    const handleStartCaptureClick = React.useCallback(() => {
      setCapturing(true);
      mediaRecorderRef.current = new MediaRecorder(webcamRef.current.stream, {
        mimeType: "video/webm"
      });
      mediaRecorderRef.current.addEventListener(
        "dataavailable",
        handleDataAvailable
      );
      mediaRecorderRef.current.start();
    }, [webcamRef, setCapturing, mediaRecorderRef]);
  
    const handleDataAvailable = React.useCallback(
      ({ data }) => {
        if (data.size > 0) {
          setRecordedChunks((prev) => prev.concat(data));
        }
      },
      [setRecordedChunks]
    );
  
    const handleStopCaptureClick = React.useCallback(() => {
      mediaRecorderRef.current.stop();
      setCapturing(false);
    }, [mediaRecorderRef, webcamRef, setCapturing]);

        const handleDownload = React.useCallback(() => {
      if (recordedChunks.length) {
        const blob = new Blob(recordedChunks, {
          type: "video/webm"
        });
        const url = URL.createObjectURL(blob);
        const video = document.getElementById("video-replay");
        video.src = url
      }
    }, [recordedChunks]);
  
    return (
      <div className="d-flex flex-column align-items-center">
        <Webcam audio={false} ref={webcamRef} height={400} width={500}/>
        <video id="video-replay" height="400" width="500" controls></video>
        {capturing ? (
          <button className="btn btn-danger" onClick={handleStopCaptureClick}>Stop Capture</button>
        ) : (
          <button className="btn btn-danger" onClick={handleStartCaptureClick}>Start Capture</button>
        )}
        {recordedChunks.length > 0 && (
          <div>
            <button onClick={handleDownload}>Download</button>
          </div>
        )}
      </div>
    );
};

Possible Solution So I caught myself thinking, if the chunks aren't appearing/working during the useEffect either, it must mean that when capturing stops in handleStopCaptureClick it takes the state a while to update, including chunks I suppose. By changing the dependency from 'capturing' to 'recordedChunks' in useEffect, I was successful in making the video appear right after you stop recording.



Solution 1:[1]

Solution: By changing the dependency from 'capturing' to 'recordedChunks' in useEffect, I was successful in making the video appear right after you stop recording.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Fernando Rafael Narbona