Home > Software design >  Cant draw video on canvas in ReactJs
Cant draw video on canvas in ReactJs

Time:10-22

I am trying to draw the video frames on canvas in react basically something which is already done in vanilla java script into react little differently.

vanilla js code:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div>
    <video id="video" width="800" src="./media/video.mp4" autoplay muted loop></video>
    <canvas id="output-canvas" width="800" height="450"></canvas>
  </div>
  <script>
    let video, c1, ctx1, c_tmp, ctx_tmp;
    function init() {
      video = document.getElementById('video');

      c1 = document.getElementById('output-canvas');
      ctx1 = c1.getContext('2d');

      c_tmp = document.createElement('canvas');
      c_tmp.setAttribute('width', 800);
      c_tmp.setAttribute('height', 450);
      ctx_tmp = c_tmp.getContext('2d');

      video.addEventListener('play', computeFrame);
    }
    function computeFrame() {

      ctx_tmp.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
      let frame = ctx_tmp.getImageData(0, 0, video.videoWidth, video.videoHeight);

      ctx1.putImageData(frame, 0, 0);
      setTimeout(computeFrame, 0);
    }
    document.addEventListener("DOMContentLoaded", () => {
      init();
    });
  </script>
</body>

</html>

Now my same try for implementation in react is below

import logo from './logo.svg';
import './App.css';
import React from 'react';
import {useRef,useState, useEffect} from 'react';
import gvideo from './assets/videos/gvid.mp4'

function App() {
  const video = useRef(null);
  const c1= useRef(null);
  const c2= useRef(null);
  const [playerState, setPlayerState] = useState({
    isPlaying: false,
    progress: 0,
    speed: 1,
    isMuted: false,
  });
  let width= video.clientWidth;
  let height= video.clientHeight;
  let ctx= c2.current.getContext('2d');

  function drawVid(){
    
    ctx.drawImage(video,0,0,width,height);

      let frame= ctx.getImageData(0,0,width,height);
      for(let i=0; i<frame.data.length; i =4){
        let r= frame.data[i];
        let g= frame.data[i 1];
        let b= frame.data[i 2];

      }
      requestAnimationFrame(drawVid);
  }

  useEffect(() => {
    // drawVid()

  });
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
          <video  ref={video} id="my_video" src={gvideo} height="300" width="600" autoPlay loop muted ></video>
          <canvas style={{display:"none"}} ref={c1} id="c1" width="160" height="96"></canvas>
          <canvas ref={c2} id="c2" width="300" height="400"></canvas>
        <p>
          Edit {playerState.isPlaying} <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

kindly someone guide me , I have to implement something similar to this i.e. chroma keying

CodePudding user response:

Bascially you have done right, but pay attention:

  1. You should use video.current instead of video in your function drawVid, and in the width/height inititaion. Check Hooks Reference
  2. let ctx= c2.current.getContext('2d'); may cause error at first, because the c2.current will be null until the canvas really mounted. Try put it inside function drawId and make some protection like
if(!c2.current){
   // check if video dom is ready
   requestNextAnimationFrame(drawId)
}

And it's better to do same protection for video

if(!video.current){
   // check if video dom is ready
   requestNextAnimationFrame(drawId)
}
  • Related