React实现融云音视频会议

本文档展示了如何使用RongCloud的IMLib和RTC插件简单实现音视频会议功能。从初始化IM连接,到创建和加入RTC房间,再到发布和订阅音视频流,以及处理用户操作如禁用音视频,整个过程详细地用React代码进行了演示。
摘要由CSDN通过智能技术生成

简单实现了一下音视频会议功能,代码写得比较随意。


.main{
  margin: 0 auto;
  margin-top:80px;
  width: 800px;
  height: 800px;
}
.video-box{
    display:flex;
    justify-content: space-between;
    width: 100%;
    height: 450px;
    border: 5px solid #ccc;
   
}
.vbox{
    margin: 12px;
    width:200px;
    height:200px;
    border: 1px solid #000;
    overflow: hidden;
}

.video{
    position: relative;
    margin: 12px;
    width:200px;
    height:200px;
    border: 1px solid #000;
    background-color: rgba(11, 10, 10, 0.893);
    overflow: hidden;
}
.video .avatar{
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -50px;
    margin-top: -50px;
    background-image: url('../../../public/imgaes/Avatar.jpg');
    background-size: 100px 100px;
    width: 100px;
    height: 100px;
    border-radius: 50px;
    border: 1px solid #ccc;
    overflow: hidden;
}
.video .rong-video{
    width: 100%;
    height: 100%;
    background-color: #000;
}
input{
    margin-right: 12px;
    width: 160px;
    height: 30px;
    border: 1px solid #ccc;
    border-radius: 2px;
}
button{
    margin-right: 12px;
    height: 30px;
    background-color: rgb(0, 119, 255);
    border: none;
    border: 1px solid rgb(0, 98, 255);
    border-radius: 2px;
    color: #fff;
}
.operation{
    display: flex;
    width: 100%;
    height: 50px;
    border: 1px solid #000;
}
.icon{
   margin-left: 16px;
   cursor: pointer;
   text-align: center;
   padding-top: 10px;
   width: 100px;
   height:calc(100% - 10px);
   font-size: 12px;
   background-color: rgba(174, 178, 181, 0.307);
}
import { useCallback, useEffect, useRef, useState } from "react"
import * as RongIMLib from '@rongcloud/imlib-v4-adapter'
import { installer, RCRTCCode } from '@rongcloud/plugin-rtc'
import {
  CameraOutlined,
  AudioOutlined,
  LaptopOutlined,
} from '@ant-design/icons';
import "./index.css"
import "../../index.css"


export default function Meeting() {
    const [meethingId, setMeethingId] = useState("")//会议号
    const [remoteTracks, setRemoteTracks] = useState([])//tracks初始化
    const[track,setTrack] = useState(null)
    const [crtRoom,setCtrRoom] =useState(null)//room实例
    const [rtcClient, setRtcClient] = useState()//初始化rtc
    const [token, setToken] = useState('ccOTgRXdriqNuRTHlZlHUYWGJFGTBY7L@62zf.cn.rongnav.com;62zf.cn.rongcfg.com')
    const [openCamer, setOpenCamer] = useState(true)
    const videoRef = useRef(null)
    
  
  useEffect(() => {
        // 获取IMLIb实例,IMLib 是即时通讯能力库,封装了通信能力和会话、消息等对象
       const im = RongIMLib.init({ appkey: '8luwapkv8dmsl' })
        // 监听IMLib连接的状态变化
        im.watch({
            status(status) {
                // IM 连接状态变更通知
                console.log("连接status", status)
            }
        })
        //初始化RCRTCClient
        setRtcClient(im.install(installer, {
            timeout: 30*1000,
        })) 
      
        // 建立IM连接
        try {
            const user = im.connect({ token: token })
            console.log('链接成功, 链接用户 id 为: ', user.id);
        } catch (error) {
            console.log('链接失败: ', error.code, error.msg);
        }
  }, [])
  
  async function joinRoom() {
    setOpenCamer(false)
    const { code, room, userIds, tracks } = await rtcClient.joinRTCRoom(meethingId)
        if (code !== RCRTCCode.SUCCESS) {
          console.log('加入房间失败:', code)
            return
          }else{
          console.log('加入房间成功:', code)
    }
        setCtrRoom(room)//创建的房间实例
        tracks.length && setRemoteTracks(tracks)
        room.registerRoomEventListener({
            onKickOff (byServer) {
            	console.log('onKickedByServer', byServer);
            // 当本地已获取资源后,需要调用 track.destroy() 销毁已获取的资源, track 为 RCMicphoneAudioTrack 或 RCCameraVideoTrack 类型实例
          },
          
            //房间其他用户发布新资源时触发
              onTrackPublish(tracks) {
                console.log('onTrackPublish:', JSON.stringify(tracks));
                // 按业务需求选择需要订阅资源,通过 room.subscribe 接口进行订阅
                const { code } =  room.subscribe(tracks)
                if (code == RCRTCCode.SUCCESS) {
                  console.log('资源订阅成功 ->', code)
               }
                 appendVideoEl(tracks);//添加节点
          },
              
            // 订阅音视频流通道建立,track可以进行播放
            onTrackReady (track) {
                console.log('onTrackReady')
                if (track.isAudioTrack()) {
                  // 音轨不需要传递播放控件
                  track.play()
                } else {
                     const node = document.getElementById('rc-video-' + track.getTrackId())
                     track.play(node);
                }
            },
            // 人员加入
            onUserJoin(userIds) {
              console.log('onUserJoined', JSON.stringify(userIds));
              
              
            },
            // 人员退出
            onUserLeave (userIds) {
              console.log('onUserLeft', JSON.stringify(userIds));
            //   userIds.forEach((userId) => {
            //     document.querySelectorAll('.video-wrap-'+userId).forEach((el) => {
            //       el.remove();
            //     });
            // });
          }
        })
        
    
        // 进入房间打开摄像头
      const videoEl = await rtcClient.createMicrophoneAndCameraTracks()
       if (videoEl.code === RCRTCCode.SUCCESS) {
         console.log("打开摄像头")
         setTrack(videoEl.tracks)
          const [ audioTrack, videoTrack ] = videoEl.tracks
          await room.publish([audioTrack, videoTrack])
          videoEl.tracks.map((track) => {
            if (track.isVideoTrack()) {
              var node = document.createElement("div");
              node.classList.add("vbox")
              const tempHtml = `
                                 <video class="rong-video" id="rc-video-${track.getTrackId()}"></video>
                              `;
              node.innerHTML = tempHtml;
              videoRef.current.appendChild(node)
              // 播放视频
            if (track.isVideoTrack()) {
              const node = document.getElementById('rc-video-' + track.getTrackId())
              track.play(node);
              return;
            }
            // 播放音频
            if (false) {
              track.play();
              }
             } 
          });
        }
    }
   
  
    function appendVideoEl(tracks) {
      tracks.map((track) => {
        if (track.isVideoTrack()) {
          var node = document.createElement("div");
          node.classList.add("vbox")
          const tempHtml = `
                             <video class="rong-video" id="rc-video-${track.getTrackId()}"></video>
                         `;
          node.innerHTML = tempHtml;
          videoRef.current.appendChild(node)
        }
      });
}
  
    // 订阅资源
    async function subscribe(tracks){
        const { code } = await crtRoom.subscribe(tracks)
        console.log("订阅成功",code)
          if (code !== RCRTCCode.SUCCESS) {
            console.log('资源订阅失败 ->', code)
         }
  }

    // 获取摄像头资源 麦克风资源
  async function publishMicrophoneCamera() {
      setOpenCamer(false)
      const { code, tracks } = await rtcClient.createMicrophoneAndCameraTracks()
       setTrack(tracks)
        if (code === RCRTCCode.SUCCESS) {
         // tracks 包含一个 RCMicphoneAudioTrack 实例和一个 RCCameraVideoTrack 实例
         const [ audioTrack, videoTrack ] = tracks
          publish(tracks);
        } else {
            console.log("获取资源失败",code)
        }
  }
  
         // 发布
         async function publish(tracks){
          const pubRes = await crtRoom.publish(tracks);
          // 若资源发布失败
          if (pubRes.code === RCRTCCode.SUCCESS) {
            console.log('播放资源 发布成功')
            appendVideoEl(tracks);
            tracks.forEach((track) => {
              playTrack(track, false);
            });
          }else{
            console.log('资源发布失败:', pubRes)
          }
  }
  
    function playTrack(track, playAudio){
        // 播放视频
        if (track.isVideoTrack()) {
          const node = document.getElementById('rc-video-' + track.getTrackId())
          track.play(node);
          return;
        }
        // 播放音频
        if (playAudio) {
          track.play();
        }
     }

  // 退出房间
  async function leaveRoom() {
      setOpenCamer(true)
      const { code } = await rtcClient.leaveRoom(crtRoom)
      if (code !== RCRTCCode.SUCCESS) {
        console.log('退出房间失败:', code)
      } else {
        console.log("已退出房间")
        removeVideoEl();
      }
      await crtRoom.unpublish(track)
      const [ audioTrack, videoTrack ] = track
      audioTrack.destroy()
      videoTrack.destroy()
  }

    function removeVideoEl(){
      setOpenCamer(true)
  }
  
  
// 禁用音视频
  function disable() {
    setOpenCamer(true)
    const [ audioTrack, videoTrack ] = track
    videoTrack.mute()
    audioTrack.mute()
    audioTrack.destroy()
    videoTrack.destroy()
  }


    return (
      <>
        <div className="main">
          <div>
                 <input onChange={function (e) {
                      setMeethingId(e.target.value)
                      console.log(meethingId)
                  }} placeholder="会议号"></input>   
                  <button onClick={joinRoom}>进入会议</button>
                  <button onClick={leaveRoom}>离开房间</button>
                  <button onClick={publishMicrophoneCamera}>获取摄像头麦克风</button>
          </div>
            <div ref={videoRef} id="video-box" className="video-box">
            {openCamer ?
             <div style={{ marginTop: 10 }} className="video" id="rong-video-box">
             <div
             className="avatar">
             </div> 
              </div>:""
            }
            
          </div>
          <div className="operation">
            <div className="icon">
              <CameraOutlined style={{
                width: 50,
              }} />
              <div onClick={disable}>关闭摄像头</div>
            </div>
            <div className="icon"><AudioOutlined
            style={{
              width: 50,
            }}/> <div>解除静音</div></div>
            <div className="icon"><LaptopOutlined
            style={{
              width: 50,
            }}/> <div>共享屏幕</div></div>
          </div>
         </div>
        </>
    )
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值