React /webrtc,视频暂停时音频停止

时间:2021-07-14 04:39:02

标签: reactjs socket.io webrtc simple-peer

我认为这与 webrtc 无关,而是响应状态更新和重新渲染,但上下文涉及 webrtc。

我一直在开发一个带有简单对等和套接字 io 的视频聊天应用程序,用于后端和多对等聊天。

我正在使用 redux 来处理我的状态。

const intialState = {
  unseenMessages: [],
  peers: [],
  peerStreams: [],
  currMessages: [],
  chatID: null,
  meet: null,
  isChatActive: false,
  isPeopleActive: false,
  userVideoStream: null,
  cameraStream: null,
  screenOn: false,
  deck_limit: 2,
  mediaState: {
    videoPaused: false,
    muted: false,
  },
  userDeckOn: false,
};


这是初始的 redux 套接字状态。

我的每个对等流对象看起来像这样

 {
 muted:false,
 user:{}
 videoPaused:false,
 peer:Peer object
 peerID:userID
 userOnDeck:boolean if user is on main deck
 isPinned: boolean if user is pinned,
 stream:MediaStream of peer
  }

我有一个 setmediaState redux 动作,当我停止我的视频时,它会发出一个套接字事件 将我的用户 ID 发送给会议内的所有同行,通知他们该用户已停止视频。 音频也是如此。


/**
 * handle change in audio or video state of the peer
 * if peerchanged is current user broacast set metdia state to whole meeting
 * change mediaValues of user in peers and peerStreams array
 * @param {*} userID  user to add
 */
export const setmediaState = (mediaState, peerID, socket = null, meetID = null) => {
  return async (dispatch, getState) => {
    try {
      console.log("updating media state of peer", peerID, socket, meetID);
      let peerStreams = getState().socket.peerStreams;
      let peerIndex = peerStreams.findIndex((p) => p.peerID == peerID);
      if (socket) {
        socket.emit("change_media_state", {
          mediaState,
          meetID,
        });
        dispatch({
          type: actionTypes.SET_MEDIA_STATE,
          payload: mediaState,
        });
      }
     if (peerIndex != -1) {
         peerStreams[peerIndex] = {
           ...peerStreams[peerIndex],
           ...mediaState,
         };
         dispatch({
           type: actionTypes.UPDATE_PEER_STREAMS,
           payload: peerStreams,
         });
       }
    } catch (err) {
      dispatch(
        setNotification(
          "Warning:you may be disconnected",
          err.message + "\nPlease try refreshing",
          "warning"
        )
      );
    }
  };
};

现在的问题是当我停止视频时,音频也以某种方式暂停。我说这可能是状态/重新渲染问题的原因是当我评论上述函数中的以下代码时。视频停止工作正常/我视频暂停时可以收听音频。

 if (peerIndex != -1) {
         peerStreams[peerIndex] = {
           ...peerStreams[peerIndex],
           ...mediaState,
         };
         dispatch({
           type: actionTypes.UPDATE_PEER_STREAMS,
           payload: peerStreams,
         });
       }

基本上当我没有在更改 mediaState 时更新同行时,视频停止工作 正确。

这是我的视频元素


import { Microphone16, MicrophoneFilled16, MicrophoneFilled20, MicrophoneOff16, MicrophoneOffFilled16, MicrophoneOffFilled20, Pin16, PinFilled16, PinFilled20, Undo16, Undo20, UserAvatar20, VideoChat20, VideoOffFilled20 } from '@carbon/icons-react';
import { Column } from 'carbon-components-react';
import React, { useContext, useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { SocketContext } from '../../context/GlobalSocketContext';
import { pinUser, unPinUser } from '../../store/actions/socket';
import "./_styles.css"

/**
 * This is ther user video compoent of meet page
 * @component
 * @param {*} peerObj
 */
function CallVideo({peerObj}) {
    const ref = useRef();
    const peerStream= useSelector(state => state.socket.peerStreams.find(p=>p.peerID==peerObj.peerID))
    const userID = useSelector(state=>state.auth.userID);
    const userDeckopen = useSelector(state=>state.socket.userDeckOn)

    useEffect(()=>{
        if(peerStream&&peerStream.stream)console.log(peerStream.stream.getTracks())
      if(peerStream&&ref.current)ref.current.srcObject=peerStream.stream;
    },[peerStream])

    /**
     * Check is video stream is valid or not
     * by active trakcs and mediaState object of peer
     */
    const checkVideo = ()=>{
        return (peerStream&&peerStream.stream&&
            peerStream.stream.active
            &&peerStream.stream.getVideoTracks()[0]?.enabled&&
            !(peerStream.videoPaused)
        )
    }

    /**
     * Check is audio stream is valid or not
     * by active trakcs and mediaState object of peer
     */
    const checkAudio = ()=>{
        
        return (peerStream&&peerStream.stream&&
            peerStream.stream.active
            &&peerStream.stream.getAudioTracks()[0]?.enabled&&
            (!peerStream.muted)
        )

    }
    const dispatch = useDispatch()
    /**
     * check if user has proper image or not
     */
    const checkUser = ()=>{
        return (
            peerObj&&peerObj.user&&peerObj.user.image
        );
    }

    /**
     * user gizmos :pin unpin user
     */
    const pinPeer = ()=>{
        dispatch(pinUser(peerObj.peerID))
    }
    const unPinPeer = ()=>{
        dispatch(unPinUser(peerObj.peerID))
    }

    const handlePin = ()=>{
        if(peerObj.isPinned)unPinPeer();
        else pinPeer()

    }
    /**
     * set scale of userVideo based on the status of
     * peer
     * depending on if userBar is open or not,or if user
     * is pinned or not
     */
    let colLg = ()=>{
        if(peerObj.isPinned&&userDeckopen)return 12;
        else if(peerObj.isPinned)return 14;
        else return 7;
    }
    let colmd= ()=>{
        if(peerObj.isPinned&&userDeckopen)return 6;
        else if(peerObj.isPinned)return 7;
        else return 6;
    }
    return (
        <Column sm={4} md={colmd()} lg={colLg()} key={peerObj.peerID} className="video__col">
            <p style={{color:"white"}}>{
                peerObj?.peerID==userID?"You":peerObj?.user?.username
            }</p>
            <div className="peer__video">
                    {checkVideo()?<video autoPlay 
                    playsInline ref={ref} className={"videoplayer "+
                    (peerObj.isPinned?"pinned":"")
                    +(peerObj.isPinned&&userDeckopen?" pinned_deck":"")
                }
                    muted={peerObj.peerID==userID}/>:
                     <div className={"user__tile " + 
                            (peerObj.isPinned?"pinned":"")
                        +(peerObj.isPinned&&userDeckopen?" pinned_deck":"")
                        }>
                            {
                                        checkUser()?(<img src={peerObj?.user?.image} className="user__avatar"/>):
                                        (<UserAvatar20 className="user__avatar"/>)
                            }
                        </div>
                    }
                    <div className="user_video_gizmo user__gizmos__active" onClick={()=>{
                        handlePin()
                    }}>
                        {peerObj.isPinned?<Undo20 />:<PinFilled20/>}
                    </div>
                    <div className="media__state">
                        {checkAudio()?<MicrophoneFilled20/>:
                        <MicrophoneOffFilled20 className="media__off"/>}
                        {checkVideo()?<VideoChat20/>:<VideoOffFilled20 className="media__off"/>}
                    </div>
            </div>
        </Column>
    );
}

export default CallVideo

视频控件

import { Chat20, EventsAlt20, FaceActivated20, FaceActivatedAdd20, MicrophoneFilled20, MicrophoneOffFilled20, PhoneBlockFilled20, Screen20, ScreenOff20, Share20,VideoFilled20, VideoOffFilled20 } from '@carbon/icons-react'
import React, { useContext, useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useHistory, useParams } from 'react-router-dom';
import { SocketContext } from '../../context/GlobalSocketContext';
import { setmediaState } from '../../store/actions/socket';
import * as actionTypes from "../../store/constants/socket"
import "./_style.css"
/**
 * VideoControls
 * Display user video chat gizmos inside meet
 * @component
 */

function VideoControls() {
    const mediaState = useSelector(state => state.socket.mediaState)
    const screenOn = useSelector(state=>state.socket.screenOn);    
    const {meetID}= useParams()
    const context = useContext(SocketContext)
    const userVideoStream = useSelector(state=>state.socket.userVideoStream);
    const dispatch = useDispatch()
    const userID = useSelector(state=>state.auth.userID);
    const history = useHistory()
    /**start video */
    const handleVideoOff = (e)=>{
        if(userVideoStream)console.log(userVideoStream.getVideoTracks()[0])
       if(userVideoStream)userVideoStream.getVideoTracks()[0].enabled=true;
       dispatch(setmediaState({videoPaused:false},userID,context.socket,meetID))
    }
    /**stop video */
    const handleVideoOn =()=>{
        if(userVideoStream)console.log(userVideoStream.getVideoTracks()[0])
        if(userVideoStream)userVideoStream.getVideoTracks()[0].enabled=false;
    dispatch(setmediaState({videoPaused:true},userID,context.socket,meetID))
    }
    /**stop audio */
    const handleAudioOn = (e)=>{
        if(userVideoStream&&
        userVideoStream.getAudioTracks()[0])userVideoStream.getAudioTracks()[0].enabled=false;  
    dispatch(setmediaState({muted:true},userID,context.socket,meetID))
    }
    /**start audio */
    const handleAudioOff = (e)=>{
        if(userVideoStream
          &&userVideoStream.getAudioTracks()[0])userVideoStream.getAudioTracks()[0].enabled=true;
    dispatch(setmediaState({muted:false},userID,context.socket,meetID))
    }
    /**stop screen */
    const handleScreenOn = (e)=>{
        context.stopScreenShare();
      
    }
   /**start screen */
    const handleScreenOff = (e)=>{
        context.startScreenShare()
    }
    /**share link to users */
    const shareLink = (e)=>{
         prompt(
    "Copy this link and send it to people you want to meet with",
    window.location.href
  );
    }
    /**open meetpeople sidebar */
    const openPeople = (e)=>{dispatch({
        type:actionTypes.PEOPLE_ACTIVE
    })}
   /**open chat sidebar */
    const openChat = (e)=>{dispatch({
        type:actionTypes.CHAT_ACTIVE
    })}
   /**exit meet */
    const leaveMeet = (e)=>{
       history.push("/dashboard")
    }

    return(
        <div className="video__controls">
            <div>
            
            {!mediaState.videoPaused?<VideoFilled20 onClick={e=>handleVideoOn(e)}/>:
            <VideoOffFilled20 onClick={e=>handleVideoOff(e)}/>}
            </div>
            <div>

            {!mediaState.muted?<MicrophoneFilled20 onClick={e=>handleAudioOn(e)}/>:
            <MicrophoneOffFilled20 onClick={e=>handleAudioOff(e)}/>}
            </div>
            <div>

            {screenOn?<ScreenOff20 onClick={e=>handleScreenOn(e)}/>:
            <Screen20 onClick={e=>handleScreenOff(e)}/>}
            </div>
            <div onClick={(e=>openChat(e))}>

            <Chat20/>
            </div>
            <div onClick={(e)=>openPeople(e)}>

            <EventsAlt20/>
            </div>
            <div onClick={(e)=>shareLink(e)}>

            <FaceActivatedAdd20 />
            </div>
            <div className="end__call" onClick={e=>leaveMeet()}>

            <PhoneBlockFilled20 />
            </div>
        </div>
    )
}

export default VideoControls

如果有人愿意,我也可以提供 github 链接。

0 个答案:

没有答案