如何使用react js组件在网页上流式传输网络摄像头?

时间:2016-12-13 00:05:27

标签: javascript html5 reactjs video-streaming webcam

我是reactJS的新手并试图建立一个组件。

我想构建一个基本实用程序,其中我有一个显示实时网络摄像头源的视频组件,并且会有一个捕获按钮,用于捕获并存储源到磁盘的快照。我希望它在一个组件中(视频输入+捕获按钮)

此代码在浏览器中流式传输Feed,但我希望它在一个组件中

<body>
<div id="container">
    <video autoplay="true" id="videoElement">

    </video>
</div>
<script>
 var video = document.querySelector("#videoElement");

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;

if (navigator.getUserMedia) {       
    navigator.getUserMedia({video: true}, handleVideo, videoError);
}

function handleVideo(stream) {
    video.src = window.URL.createObjectURL(stream);
}

function videoError(e) {}
</script>
</body>
</html>

这很好用,但不是它不是一个组件。

我尝试在reaact js中关注但是它没有工作:

<body>
    <div id="container">

    </div>
    <script type="text/jsx">
        var MyComponent = React.createClass({
            handleVideo: function(stream) {
                video.src = window.URL.createObjectURL(stream);
            },
            videoError: function() {

            },
            render: function() {

            var video = document.querySelector("#videoElement");

            navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;

            if (navigator.getUserMedia) {       
                navigator.getUserMedia({video: true}, this.handleVideo, this.videoError);
            }
                return <div>
                        <video autoplay="true" id="videoElement">
                        </video>
                       </div>;
            }
        });
    React.render( <MyComponent />, document.getElementById('container'));
    </script>
    </body>

错误在handleVideo()

ReferenceError:未定义视频。

我对错误的理解是,

由于视频标签是后来的部分(作为回报),它在handleVideo函数中的定义之前尝试使用。 我很困惑如何使这项工作。

谢谢!

4 个答案:

答案 0 :(得分:5)

关于React组件的工作方式,有几点需要了解。首先根据React docs

  

render()函数应该是纯的,这意味着它不会修改   组件状态,每次调用时返回相同的结果,   并且它不直接与浏览器交互。

您应该将视频元素初始化为componentDidMount之类的备用生命周期方法,以确保它仅初始化一次。

其次,您很少需要直接与DOM交互。在这种情况下,我们可以使用组件的内部状态来管理视频流的src属性,确保它只在流初始化后才更新。

这是一个可能有效的更新组件:

var MyComponent = React.createClass({
  getInitialState: function(){
    return { videoSrc: null }
  },
  componentDidMount: function(){
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
    if (navigator.getUserMedia) {
        navigator.getUserMedia({video: true}, this.handleVideo, this.videoError);
    }
  },
  handleVideo: function(stream) {
    // Update the state, triggering the component to re-render with the correct stream
    this.setState({ videoSrc: window.URL.createObjectURL(stream) });
  },
  videoError: function() {

  },
  render: function() {
    return <div>
      <video src={this.state.videoSrc} autoPlay="true" />
    </div>;
    }
});

答案 1 :(得分:2)

您的handleVideo方法引用了video,但您没有在handleVideo可以看到的任何地方定义该变量。相反,您可以在render中定义它:

var video = document.querySelector("#videoElement");

所以这是你的第一个问题,但这不是你真正的问题。你真正的问题是在React-land中你要避免document.anythinggetElementByIdgetByTagAndClassNamequerySelector等。这是因为React使用虚拟DOM,如果在引用实际DOM元素时不小心,那些引用很快就会变坏(因为虚拟DOM会替换render之后的实际DOM。)

解决方案是使用React自己的替代技术:refs。它们更容易通过示例进行解释,所以这里使用refs修复了代码:

handleVideo: function(stream) {
    // KEY CHANGE #1
    this.refs.video.src = window.URL.createObjectURL(stream);
},
render: function() {
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;

    if (navigator.getUserMedia) {       
        navigator.getUserMedia({video: true}, this.handleVideo, this.videoError);
    }
    return <div>
        {{/* KEY CHANGE #2 */}}
        <video autoplay="true" id="videoElement" ref="video">
        </video>
    </div>;

换句话说,如果您在ref="something"函数中返回的任何元素中添加render属性,则可以通过引用{{1}在您的(React)代码中的任何其他位置引用该元素}}

还有其他方法可以使用this.refs.something(例如,你可以传递一个函数),但这超出了这个问题的范围,所以我建议你阅读{{1的React文档}}:

https://facebook.github.io/react/docs/refs-and-the-dom.html

修改

正如Jordan Burnett的回答所强调的那样,React和普通的JS DOM工作之间还存在其他重要的区别。同样,解释所有这些问题都超出了本问题的范围,但我强烈建议您阅读:

https://facebook.github.io/react/docs/react-component.html

学习覆盖以执行DOM交互工作的适当方法(例如,在非React组件上绑定事件处理程序)。

答案 2 :(得分:2)

答案中张贴的某些代码已被弃用,可能无法在所有浏览器中使用。
我创建了一个使用最新语法的新React应用程序。 这非常简单明了。我希望它可以帮助某人:)

class AppStreamCam extends React.Component {
  constructor(props) {
    super(props);
    this.streamCamVideo= this.streamCamVideo.bind(this)
  }
  streamCamVideo() {
    var constraints = { audio: true, video: { width: 1280, height: 720 } };
    navigator.mediaDevices
      .getUserMedia(constraints)
      .then(function(mediaStream) {
        var video = document.querySelector("video");

        video.srcObject = mediaStream;
        video.onloadedmetadata = function(e) {
          video.play();
        };
      })
      .catch(function(err) {
        console.log(err.name + ": " + err.message);
      }); // always check for errors at the end.
  }
  render() {
    return (
      <div>
        <div id="container">
          <video autoPlay={true} id="videoElement" controls></video>
        </div>
        <br/>
        <button onClick={this.streamCamVideo}>Start streaming</button>
      </div>
    );
  }
}

这是我的中型文章:
How to create a React application to stream your webcam | Basic, Beginner

如果有帮助,别忘了鼓掌:)

最后,这是工作示例的github存储库: Basic-WebCam-Streamer

答案 3 :(得分:0)

您可以使用反应网络摄像头。按照下面的链接,它已经具有“ getScreenshot”功能,该功能可从网络摄像头返回当前帧的base64编码图片。

https://www.npmjs.com/package/react-webcam

const WebcamCapture = () => {
  const webcamRef = React.useRef(null);
  const [imgSrc, setimgSrc] = React.useState(null);

  const capture = React.useCallback(
    () => {
      const imageSrc = webcamRef.current.getScreenshot();
      setimgSrc(imageSrc);

    },
    [webcamRef,setimgSrc]
  );

  return (
    <div>
      <Webcam
        audio={false}
        height={720}
        ref={webcamRef}
        screenshotFormat="image/jpeg"
        width={1280}
      />
      <button onClick={capture}>Capture photo</button>
      {imgSrc && (<img src = {imgSrc}/>) }
    </div>
  );
};    

很乐意提供帮助!