【问题标题】:How to stream webcam feed on web page using react js component?如何使用 React js 组件在网页上流式传输网络摄像头提要?
【发布时间】:2017-04-27 23:01:48
【问题描述】:

我是 reactJS 的新手,正在尝试构建一个组件。

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

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

<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函数中的定义之前使用。 我很困惑如何完成这项工作。

谢谢!

【问题讨论】:

  • 我没有在我的答案中包含这个,因为它实际上不是答案的一部分,但从你的代码看来,你正在将你的 React 代码与你的 HTML 保持一致。 JSX 旨在用于 Javascript 文件,虽然您可以将其放入 HTML,但将组件代码保存在单独的文件中可能会更成功。您的 IDE 不会被同一个文件中的两种类似 HTML 的语法混淆,使用 Babel 等工具将 JSX 转换为浏览器友好的 JS 等会更容易。
  • 谢谢,我计划将反应代码放在另一个文件中,但我认为我可以先运行 cod,然后使用单独的文件构建进一步的功能。我只是通过使用 Youtube 视频教程来学习,这就是我开始的方式,通过增强同一文件中的代码。 :)
  • 重要提示:Google Chrome 不推荐使用 createObjectURL 函数。改用这个: function handleVideo(stream) { video.srcObject=stream; }

标签: javascript html reactjs video-streaming webcam


【解决方案1】:

关于 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>;
    }
});

【讨论】:

  • 嗨@jordan,感谢您的回复,“componentDidMount”是我正在寻找的东西。我同意渲染函数应该保持纯净:) 我尝试了您在上面建议的代码。我在控制台中没有看到任何错误,但页面中缺少视频组件。它正在访问网络摄像头,(灯亮)但视频组件丢失:(想法?
  • 我添加了"id=videoElement",视频组件又回来了。现在它也有了 src,但不知何故,盒子是空的。大声笑
  • 您是否在调试控制台中看到任何错误?有关我测试过的版本,请参阅我编辑的答案。您需要确保 videoError 函数仍然存在。 autoplay 也变为 autoPlay
  • 乔丹·伯内特,我爱你:P 自动播放到自动播放使提要上线 :) 谢谢 :)
【解决方案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>
    );
  }
}

这是我关于它的 Medium 文章:
How to create a React application to stream your webcam | Basic, Beginner

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

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

【讨论】:

  • 嗨@Ahmed github 链接已损坏,您是否更改了存储库名称?谢谢。
【解决方案3】:

您的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 函数中返回的任何元素,您可以通过引用this.refs.something 在您的(React) 代码中的任何其他位置引用该元素。

还有其他使用refs 的方法(例如,你可以传递一个函数),但这超出了这个问题的范围,所以我建议你阅读refs 的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 组件上绑定事件处理程序)。

【讨论】:

  • 感谢@machineghost 的回复。我将浏览您上面提到的文档。我相信它会对我有所帮助。
  • 目前,我已经做出了您建议使用“ref”的更改 现在,来自控制台的错误消息消失了,但我在浏览器中看不到提要。它只是一个空盒子。但是,网络摄像头的灯亮着。想法?
  • 听起来可能视频的src 没有设置好(我认为没有src&lt;video&gt; 看起来像一个空框)。这可能与navigator.getUserMedia 没有调用handleVideo 有关,但我不熟悉navigator.getUserMedia 的工作原理(这是一个已弃用的API)。如果我是你,我会在handleVideo 中抛出console.logdebugger 以查看它是否被调用。如果是,请检查 this.refs.video 是否存在以及 window.URL.createObjectURL(stream) 是否正在返回您所期望的。如果它没有被调用......那是你的问题:)
  • 嗯,handleVideo 正在被调用。 inside handleVideo blob:null/ffdeec81-e718-1244-8ba8-02500ab37d1c(这是window.URL.createObjectURL(stream)的值) blob:null/c75a5290-cdde-1e4d-859c-6eea07aa7b22(这是this.refs.video.src) 值在那里,我用了“检查元素”;视频标签中缺少 SRC 属性。
  • 似乎没有调用handleVideo。这很可能是时间的(单独)问题。在您的 React render 方法中,您构建了“虚拟 DOM”,而不是真正的 DOM。只有在渲染发生之后,虚拟 DOM 才会成为真实的 DOM。因此,您还应该将navigator.getUserMedia 代码从render 移出并移入componentDidMount(发生在第一个render 之后)和/或componentDidUpdate(每隔一个render 发生)方法(参见我的答案中的第二个链接以了解有关它们的更多信息)。
【解决方案4】:

您可以使用 react-webcam。按照下面的链接,它已经有一个“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>
  );
};    

乐于助人!

【讨论】:

    【解决方案5】:

    这是 AG_HIHI 写成功能组件的解决方案。我删除了开始按钮,而是在安装后立即开始。将 AppStreamCam 组件放在您的 parant react 组件中,它会显示您的网络摄像头提要。对我来说就像一个魅力。

    import React, {useEffect} from 'react'
    
    const AppStreamCam = () => {
    
      const 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.
      }
    
      useEffect(()=>{
        streamCamVideo()
      },[])
    
      return (
        <div>
            <video autoPlay={true} id="videoElement" ></video>
        </div>
      );
    }
    
    export default AppStreamCam
    

    【讨论】:

      猜你喜欢
      • 2011-10-31
      • 2020-03-22
      • 1970-01-01
      • 2013-05-14
      • 2014-06-18
      • 1970-01-01
      • 1970-01-01
      • 2016-10-23
      • 2012-10-08
      相关资源
      最近更新 更多