Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mobile safari out of memory during video capturing

Tags:

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.

like image 735
lasfin Avatar asked Aug 14 '19 08:08

lasfin


1 Answers

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

like image 59
Rachel Gallen Avatar answered Oct 19 '22 00:10

Rachel Gallen