web-dev-qa-db-ja.com

react jsコンポーネントを使用してWebページでWebカメラフィードをストリーミングする方法は?

私はreactJSを初めて使用し、コンポーネントを構築しようとしています。

ライブWebカメラフィードを表示するビデオコンポーネントがあり、フィードのスナップをキャプチャしてディスクに保存するためのキャプチャボタンがある基本的なユーティリティを構築したいと思います。単一のコンポーネント(ビデオフィード+キャプチャボタン)に入れたい

このコードはブラウザでフィードをストリーミングしますが、コンポーネントにフィードが必要です。

<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関数の定義の前に使用しようとしています。私はこれをどのように機能させるか混乱しています。

ありがとうございました!

6

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>;
    }
});
7
Jordan Burnett

handleVideoメソッドはvideoを参照しますが、handleVideoが表示できる場所にはその変数を定義しません。代わりに、renderで定義します。

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

だからあなたの最初の問題がありますが、それはあなたの本当の問題ではありません。あなたの本当の問題は、React-landではdocument.anythinggetElementByIdgetByTagAndClassNamequerySelectorなど)を避けたいということです。これは、Reactが仮想DOMを使用するためです。注意しないと、実際のDOM要素を参照すると、それらの参照がすぐに悪くなる可能性があります(renderの後に仮想DOMが実際の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>;

つまり、render関数で返す要素にref="something"属性を追加すると、this.refs.somethingを参照することで、(React)コードの他の場所でその要素を参照できます。

refsを使用する他の方法もあります(たとえば、関数を渡すことができます)が、それはこの質問の範囲外なので、refsのReactドキュメントを読むことをお勧めします。

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

[〜#〜]編集[〜#〜]

ジョーダン・バーネットの回答が強調しているように、Reactと通常のJSDOM作業の間には他にも重要な違いがあります。繰り返しますが、それらすべてを説明することはこの質問の範囲外ですが、以下を読むことを強くお勧めします。

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

dOM相互作用作業を行うためにオーバーライドする適切なメソッドを学習する(たとえば、非Reactコンポーネントでのイベントハンドラーのバインド)。

2
machineghost

回答に投稿されたコードの一部は非推奨になり、すべてのブラウザで機能しなくなる可能性があります。
新しい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の記事です:
ウェブカメラをストリーミングするためのReactアプリケーションの作成方法|基本、初心者

それが助けになったなら、拍手することを忘れないでください:)

最後に、実際の例のgithubリポジトリは次のとおりです: Basic-WebCam-Streamer

0
Ahmed Ghrib