EN
React - play web camera on video element (custom WebCamera component)
3
points
In this short article, you can see how in React create custom web camera component using video element and stream video to it.
Example preview:
Practical example
In this example, we present how to create reusable WebCamera
component to which we can pass the following properties:
width
-WebCamera
component width inpx
(equals to video width),height
-WebCamera
component height inpx
(equals to video height),onReady
- function to be executed once theWebCamera
component is ready,onError
- function to be executed when some error occurs.
Note:
It is important to be sure the web camera is connected properly, permissions for camera are granted and it is not used by any other application.
// ONLINE-RUNNER:browser;
// import React from 'react';
// -- camera utils -----------------------------
const playStream = (video, stream, callback) => {
const handleLoaded = () => {
video.removeEventListener('loadedmetadata', handleLoaded);
video.play();
if (callback) {
callback(stream);
}
};
video.addEventListener('loadedmetadata', handleLoaded);
video.srcObject = stream;
};
const stopStream = (stream) => {
for (const track of stream.getTracks()) {
track.stop();
}
};
const playCamera = (video, preferedWidth, preferedHeight, onReady = null, onError = null, audioConstraints = null, videoConstraints = null) => {
let _stream = null;
let _destroyed = false;
const constraints = {
video: {
width: preferedWidth,
height: preferedHeight,
...videoConstraints
},
audio: audioConstraints
};
const promise = navigator.mediaDevices?.getUserMedia?.(constraints);
if (promise) {
promise
.then((stream) => {
if (_destroyed) {
stopStream(stream);
} else {
_stream = stream;
playStream(video, stream, onReady);
}
})
.catch((error) => {
if (onError) {
onError(`${error.name}: ${error.message}`);
}
});
} else {
if (onError) {
onError('Camera API is not supported.');
}
}
return () => {
if (_destroyed) {
return;
}
if (_stream) {
stopStream(_stream);
_stream = null;
}
_destroyed = true;
};
};
// -- react hooks ------------------------------
// Wraps action function with function that doesn't change its reference.
// It is useful when we want to pass a function to component as prop preventing unnecessary re-rendering on reference change.
//
const useProxy = (action) => {
const state = React.useMemo(
() => ({
wrapper: (...args) => {
if (state.action) {
return state.action(...args);
}
return undefined;
},
}),
[]
);
state.action = action;
return state.wrapper;
};
// -- react components -------------------------
const WebCamera = ({ width, height, audioConstraints, videoConstraints, onReady, onError }) => {
const ref = React.useRef();
const handleReady = useProxy(onReady); // by using proxy, onReady is wrapped in function which reference doesn't change
const handleError = useProxy(onError); // by using proxy, onError is wrapped in function which reference doesn't change
React.useEffect(
() => playCamera(ref.current, width, height, handleReady, handleError, audioConstraints, videoConstraints),
[width, height] // by using useProxy, we don't need to track onReady and onError functions
);
return <video ref={ref} width={width} height={height} />;
};
// Usage example:
const App = () => {
const handleReady = (stream) => {
console.log('Web camera is ready and working');
};
const handleError = (error) => {
console.error(error);
};
return (
<div>
<WebCamera width={640} height={480} onReady={handleReady} onError={handleError} />
</div>
);
};
const root = document.querySelector('#root');
ReactDOM.render(<App />, root);
It is possible to configure audio and video constraints manually by:
// https://developer.mozilla.org/en-US/docs/Web/API/Media_Capture_and_Streams_API/Constraints
// navigator.mediaDevices.getUserMedia({
const audioConstraints = { // audio: {
sampleSize: 16, // sampleSize: 16,
channelCount: 2 // channelCount: 2
}; // },
const videoConstraints = { // video: {
width: 1920, // width: 1920,
height: 1080, // height: 1080
}; // }
// });
return (
<WebCamera
width={640}
height={480}
audioConstraints={audioConstraints}
videoConstraints={videoConstraints}
onReady={handleReady}
onError={handleError}
/>
);
Giving premissions
When a web camera is accessed from React you should see notification to permit using it. Just let use it by desired website and remove permissions when it is not needed.