无法使用 webrtc MediaDevices 在反应应用程序中切换摄像头(从前到后)

Posted

技术标签:

【中文标题】无法使用 webrtc MediaDevices 在反应应用程序中切换摄像头(从前到后)【英文标题】:unable to switch camera (front to rear) in react application using webrtc MediaDevices 【发布时间】:2019-07-26 19:04:36 【问题描述】:

这是我打算做的示例演示。 如果有人对此修复有任何想法以使其工作或任何新逻辑,请分享。

这个演示是通过使用 mediaStream API 和 使用 react-webcam 库,它实际上提供了在名为 videoConstraints=faceMode: 'user' or 'environment' 的道具的帮助下管理相机视图的选项,但它似乎不起作用。 当我单击相机开关时,ICON 屏幕只是挂起,没有任何显示,而且有时它会意外工作所以最终我不得不跳转到这个原生 API 解决方案,它显示了下面的代码。 衷心感谢您的期待。

    start() 
       if (window.stream) 
         console.log('found stream and clearing that', window.stream)
         window.stream.getTracks().forEach(function(track) 
           track.stop()
         )
       
    
       const constraints = 
         video: true,
         audio: false
       
    
       return navigator.mediaDevices
         .getUserMedia(constraints)
         .then(this.gotStream)
         .then(this.gotDevices)
         .catch(this.handleError);
     
    
     gotStream(stream) 
       window.stream = stream // make stream available to console
       // video.srcObject = stream;
       // Refresh button list in case labels have become available
       console.log('enumerating media devices ')
       return navigator.mediaDevices.enumerateDevices()
     
    
     gotDevices(mediaDevices) 
       const  availableVideoInputs, videoConstraints  = this.state
       mediaDevices.forEach(mediaDevice => 
         // console.log(mediaDevice)
    
         if (mediaDevice.kind === 'videoinput') 
           console.log('found new video input ', mediaDevice)
           availableVideoInputs.push(
             deviceId: mediaDevice.deviceId,
             label: mediaDevice.label
           )
           // availableVideoInputs.push('mediaDevice.deviceId.availableVideoInputs.push(mediaDevice.deviceId)')
         
       )
    
       console.log('aggregated availableVideoInputs new ', availableVideoInputs)
    
       if (availableVideoInputs.length > 0) 
         // there are accessible webcam
         // setting first device as default to open
         const tempVideoConstraint = ...videoConstraints
    
         if (availableVideoInputs[0].deviceId) 
           console.log('availableVideoInputs[0] = ', availableVideoInputs[0])
           tempVideoConstraint.deviceId = availableVideoInputs[0].deviceId
         
    
         // console.log('putting tempVideoConstraint.facingMode ', tempVideoConstraint)
         // if (availableVideoInputs[0].label.includes('back')) 
         //   tempVideoConstraint.facingMode =  exact: 'environment'
         //  else 
         //   // it is now turn to set front active
         //   tempVideoConstraint.facingMode = 'user'
         // 
    
         console.log('setting new video constrains ', tempVideoConstraint)
    
         // this.setState(
         //   availableVideoInputs,
         //   // activeVideoInputID: availableVideoInputs[0].deviceId,
         //   // videoConstraints: tempVideoConstraint
         // )
    
         this.updateAvailableVideoStream(availableVideoInputs)
    
         return Promise.resolve('done setting updateAvailableVideoStream')
        else 
         // no webcam is available or accessible
         console.error('ERR::VIDEO_STREAM_NOT_AVAILABLE')
       
     
    
     updateAvailableVideoStream(availableVideoInputs) 
       this.setState( availableVideoInputs )
     
    
    componentDidMount() 
    this.start()
         .then(data => 
           console.log('data ', data)
           console.log('update state ', this.state)
           this.setState(
             videoConstraints: 
               ...this.state.videoConstraints,
               facingMode: 'user'
             
           )
         )
    
    
    handleCameraSwitch() 
         const  videoConstraints, availableVideoInputs, activeVideoInputID  = this.state
       console.log('current video constraints ', videoConstraints)
       const tempVideoConstraint =  ...videoConstraints 
    
       // now check if it is possible to change camera view
       // means check for another webcam
    
       console.log( availableVideoInputs )
       console.log( activeVideoInputID )
       console.log( remainingVideoStreams )
    
       if (availableVideoInputs.length === 1) 
         // cannot change the webcam as there is only 1 webcam available
         console.error('ERR - cannot change camera view [Available Video Inputs: 1]')
    
         return
       
    
       // now change the view to another camera
       // get the current active video input device id and filter then from available video stream
    
       const remainingVideoStreams = availableVideoInputs.filter(videoStream => videoStream.deviceId !== activeVideoInputID)
    
       // now check if in remainingVideoStreams there is more than 1 stream available to switch
       // if available then show the Stream Selection List to user
       // else change the stream to remainingVideoStreams[0]
       console.log( availableVideoInputs )
       console.log( activeVideoInputID )
       console.log( remainingVideoStreams )
    
       if (remainingVideoStreams && remainingVideoStreams.length === 1) 
         tempVideoConstraint.deviceId = remainingVideoStreams[0].deviceId
         console.log('new video constraints ', ...tempVideoConstraint)
         console.log('webcam ref ', this.webCamRef.current)
    
         // if (remainingVideoStreams[0].label.includes('back') || tempVideoConstraint.facingMode === 'user') 
         //   tempVideoConstraint.facingMode =  exact: 'environment' 
         //  else 
         //   // it is now turn to set front active
         //   tempVideoConstraint.facingMode = 'user'
         // 
         console.log('new video constraints with facing mode', tempVideoConstraint)
    
         // const constraints = 
         //   video: tempVideoConstraint
         // 
         // navigator.mediaDevices.getUserMedia(constraints)
         //   .then((stream) => 
         //     console.log('stream -> ', stream)
         //   )
         //   .catch((error) => 
         //     console.error('Some error occured while changing the camera view ', error)
         //     console.log(error)
         //   )
    
         this.setState( videoConstraints: tempVideoConstraint, activeVideoInputID: remainingVideoStreams[0].deviceId )
        else 
         // show the remaining stream list to user
       
    
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

【问题讨论】:

【参考方案1】:

这是您的实现的小变化。 但这将完全符合您的期望。

请参阅下面的切换相机前/后的实现。 我还添加了错误验证,例如:

    如果没有可用的视频流,则会抛出错误。 如果尝试访问后置摄像头时只有 1 个视频流可用,则会引发错误。

如果您有任何其他方法或想要更多说明,请点赞并回复评论

componentDidMount() 
    const gotDevices = (mediaDevices) =>
      new Promise((resolve, reject) => 
        const availableVideoInputs = []
        mediaDevices.forEach(mediaDevice => 
          if (mediaDevice.kind === 'videoinput') 
            availableVideoInputs.push(
              deviceId: mediaDevice.deviceId,
              label: mediaDevice.label
            )
          
        )

        if (availableVideoInputs.length > 0) 
          resolve(availableVideoInputs)
         else 
          reject(new Error('ERR::NO_MEDIA_TO_STREAM'))
        
      )


    navigator.mediaDevices.enumerateDevices().then(gotDevices)
      .then((availableVideoInputs) => this.setState( availableVideoInputs ))
      .catch((err) => this.setState( hasError: err ))

  
  
  updateFileUploadView(newActiveView) 
    this.setState( activeFileUploadView: newActiveView )

    const  hasError  = this.state
    if (newActiveView === 'clickFromWebcam' && hasError) 
      return console.error(hasError)
    

    if (newActiveView === '') 
      // means no view is active and clear the selected image
      this.setState( captureImageBase64: '', videoConstraints: defaultVideoConstraints  )
    
  
  
  changeCameraView() 
    const  availableVideoInputs  = this.state
    if (availableVideoInputs.length === 1) 
      return console.error('ERR::AVAILABLE_MEDIA_STREAMS_IS_1')
    

    this.setState( resetCameraView: true )

    setTimeout(() => 
      const  videoConstraints:  facingMode   = this.state
      const newFacingMode = facingMode === 'user' ?  exact: 'environment'  : 'user'

      this.setState(
        videoConstraints:  facingMode: newFacingMode ,
        resetCameraView: false
      )
    , 100)
  
  
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
                !resetCameraView ?
                  <Webcam
                    audio=false
                    height='100%'
                    ref=this.webCamRef
                    screenshotFormat="image/png"
                    minScreenshotWidth=screenShotWidth
                    minScreenshotHeight=screenShotHeight
                    screenshotQuality=1
                    width='100%'
                    videoConstraints=videoConstraints
                  />
                  : 'Loading...'

如您所见,此实现使用 react-webcam 库

componentDidMount中,您将首先检查类型视频输入的可用媒体流,然后在其他方法中,例如更改cameraView,即将相机切换到前/后。

我仅卸载网络摄像头 100 毫秒,然后使用新的 videoConstraints 将其安装回来,无论是 facesMode: 'user' 还是 facesMode: exact: 'environment'

这种方法将为您的代码提供一个良好的开端,您可以玩转代码并从中获得乐趣。 谢谢!

【讨论】:

以上是关于无法使用 webrtc MediaDevices 在反应应用程序中切换摄像头(从前到后)的主要内容,如果未能解决你的问题,请参考以下文章

navigator.mediaDevices 未定义

如何使用webrtc 一

如何强制录制webrtc音频的时间限制

webRTc实现视频直播

web技术分享| 实现WebRTC多个对等连接

web技术分享| 实现WebRTC多个对等连接