I have a react application in which I am trying to let the user toggle between recording their face and their screen. I have it set up so that the video element correctly shows if I am using getDisplayMedia or getUserMedia. The problem is that the recorded video from the MediaRecorder only shows the video from getUserMedia even if I toggle. How do I get the recording of the getDisplayMedia content after clicking toggleRecording?
Here is a small demo of the problem: To replicate the problem follow these steps: 1) Click Record 2) Click Share Screen 3) Click Stop Recording 4) Play the recorded video at the bottom.
You will see that it is not capturing a recording of the screen.
Here is a codesandbox of the demo.
If needed here is the code from the codesandbox:
import React, { useState, useEffect, useRef } from "react";
export default function App() {
const [isActive, setIsActive] = useState(false);
const [mediaStream, setMediaStream] = useState(null);
const [userFacing, setuserFacing] = useState(false);
const [videoUrl, setvideoUrl] = useState("");
const [chunks, setchunks] = useState([]);
const videoRef = useRef();
const liveStreamRecorder = useRef(null);
let liveStream;
const CAPTURE_OPTIONS = {
audio: true,
video: true
};
if (mediaStream && videoRef.current && !videoRef.current.srcObject) {
videoRef.current.srcObject = mediaStream;
}
useEffect(() => {
if (!mediaStream) {
enableStream();
} else {
return function cleanup() {
mediaStream.getVideoTracks().forEach((track) => {
track.stop();
});
};
}
}, [mediaStream]);
async function enableStream() {
try {
let stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
setMediaStream(stream);
} catch (err) {
console.log(err);
}
}
// toggles the stream to active or inactive
const toggle = () => {
setIsActive(!isActive);
};
const startRecording = () => {
toggle();
recorderInit();
};
const recorderInit = () => {
liveStream = videoRef.current.captureStream(30); // 30 FPS
liveStreamRecorder.current = new MediaRecorder(liveStream, {
mimeType: "video/webm;codecs=h264",
videoBitsPerSecond: 3 * 1024 * 1024
});
liveStreamRecorder.current.ondataavailable = (e) => {
chunks.push(e.data);
console.log("send data", e.data);
};
liveStreamRecorder.current.start(1000);
};
const stopRecording = () => {
liveStreamRecorder.current.stop();
const recVideoBlob = new Blob(chunks, {
type: "video/webm;codecs=h264"
});
// console.log('recVideo', recVideoBlob)
const videoURL = window.URL.createObjectURL(recVideoBlob);
setvideoUrl(videoURL);
};
const toggleRecording = async () => {
let stream;
!userFacing
? (stream = await navigator.mediaDevices.getDisplayMedia(CAPTURE_OPTIONS))
: (stream = await navigator.mediaDevices.getUserMedia(CAPTURE_OPTIONS));
setMediaStream(stream);
videoRef.current.srcObject = stream;
setuserFacing(!userFacing);
};
return (
<div className="studio-container">
<div id="container">
<video
width="640px"
height="480px"
ref={videoRef}
autoPlay
playsInline
muted={true}
/>
{/* VIDEO WILL APPEAR OF RECORDING AFTER PRESSING STOP RECORDING */}
{videoUrl ? <video controls src={videoUrl} /> : null}
</div>
<div className="studio-bottom-button-container">
{/* PRESS BUTTON TO START RECORDING AND THEN TO STOP RECORDING */}
<button
style={{ padding: ".5rem" }}
onClick={!isActive ? startRecording : stopRecording}
>
{!isActive ? "Record" : "Stop Recording"}
</button>
{/* PRESS BUTTON TO SWITCH BETWEEN GETUSERMEDIA AND GETDISPLAYMEDIA */}
<button
style={{ marginLeft: ".5rem", padding: ".5rem" }}
onClick={toggleRecording}
>
{!userFacing ? "Share Screen" : "Stop Sharing"}
</button>
</div>
</div>
);
}