Reputation: 301
I have React class with two main elements. Canvas and video. I get video stream and render it at 30fps to canvas.
class GetImage extends Component {
constructor() {
super();
this.constraints = {
video: {
width: { ideal: 2048 },
height: { ideal: 1080 },
facingMode: {
exact: 'environment'
}
}
}
}
componentDidMount() {
setVideo(this.video, this.constraints, this.readyToPlayVideo)
}
capture = () => {
const { video } = this
let canvas = document.createElement('canvas')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
let context = canvas.getContext('2d')
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(this.video, 0, 0, canvas.width, canvas.height)
this.setState({ capture: canvas.toDataURL('image/jpeg') })
stopVideo()
}
readyToPlayVideo = () => {
const { canvas, video } = this
const { offsetHeight, offsetWidth } = video
const ctx = canvas.getContext('2d')
ctx.canvas.setAttribute('height', offsetHeight)
ctx.canvas.setAttribute('width', offsetWidth)
const timeout = 1000 / 30 // drawing at 30fps
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
let _listener = () => {
(function loop() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(video, 0, 0)
setTimeout(loop, timeout)
})()
}
_listener();
}
retake = () =>
this.setState({ capture: null },
() => {
setVideo(this.video, this.constraints, this.readyToPlayVideo, this.handleError)
}
)
render() {
return (
<div>
<video
style={{ visibility: 'hidden'}}
ref={video => (this.video = video)}
playsInline
autoPlay
/>
<canvas ref={canvas => (this.canvas = canvas)}/>
</div>
)
}
}
So far so good. But I faced a problem with mobile Safari. Looks like it keep every Canvas object ever created in memory.
After several pictures was taken Safari crashes with "out of memory". I already do clearRect before rendering a new image, but it doesn't help.
Upvotes: 5
Views: 491
Reputation: 28573
There are a couple of issues to address here. Firstly, you seem to have a circular reference in your loop
function; you are calling the function within the function.
Because of this, the autoplay the video (when rendering) won't stop .. causing the 'out of memory' error.
Also , I think best practice would be to create a componentDidUnmount
function that destroys the video (when no longer needed). Use .dispose
to destroy the video.
Hope this helps
Upvotes: 2
Reputation: 2000
Not sure this will 100% solve your problem, however, this may help. Instead of using setInterval
for your animations, use window.requestAnimationFrame
.
Here's an example
var requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ) {
window.setTimeout(callback, 1000 / 30);
};
})();
function loop(){
requestAnimFrame(loop);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(video, 0, 0);
setTimeout(loop, timeout);
}
loop();
Once again, I'm not 100% sure this will solve your issue, however, it may help. Let me know if you have any more questions.
I hope this helps!
Upvotes: 2