vue项目基于WebRTC实现一对一音视频通话

 更新时间:2024年05月31日 11:48:29   作者:草样的年华  
这篇文章主要介绍了vue项目基于WebRTC实现一对一音视频通话效果,实现代码分为前端和后端两部分代码,需要的朋友可以参考下

效果

前端代码

<template>
  <div class="flex items-center flex-col text-center p-12 h-screen">
    <div class="relative h-full mb-4 fBox">
      <video id="localVideo"></video>
      <video id="remoteVideo"></video>
      <div v-if="caller && calling">
        <p class="mb-4 text-white">等待对方接听...</p>
        <img style="width: 60px;" @click="hangUp" src="@/assets/guaDuang.png" alt="">
      </div>
      <div v-if="called && calling">
        <p>收到视频邀请...</p>
        <div class="flex">
          <img style="width: 60px" @click="hangUp" src="@/assets/guaDuang.png" alt="">
          <img style="width: 60px" @click="acceptCall" src="@/assets/jieTing.png" alt="">
        </div>
      </div>
    </div>
    <div>
      <button @click="callRemote" style="margin-right: 10px">发起视频</button>
      <button @click="hangUp" style="margin-left: 10px">挂断视频</button>
    </div>
  </div>
</template>
<script>
import { io, Socket } from "socket.io-client";
let roomId = '001';
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data(){
    return{
      wsSocket:null,//实例
      called:false,// 是否是接收方
      caller:false,// 是否是发起方
      calling:false,// 呼叫中
      communicating:false,// 视频通话中
      localVideo:null,// video标签实例,播放本人的视频
      remoteVideo:null,// video标签实例,播放对方的视频
      peer:null,
      localStream:null,
    }
  },
  methods:{
    // 发起方发起视频请求
    async callRemote(){
      let that = this;
      console.log('发起视频');
      that.caller = true;
      that.calling = true;
      // await getLocalStream()
      // 向信令服务器发送发起请求的事件
      await that.getLocalStream();
      that.wsSocket.emit('callRemote', roomId)
    },
    // 接收方同意视频请求
    acceptCall(){
      console.log('同意视频邀请');
      this.wsSocket.emit('acceptCall', roomId)
    },
    // 挂断视频
    hangUp(){
      this.wsSocket.emit('hangUp', roomId)
    },
    reset(){
      this.called = false;
      this.caller = false;
      this.calling = false;
      this.communicating = false;
      this.peer = null;
      this.localVideo.srcObject = null;
      this.remoteVideo.srcObject = null;
      this.localStream = undefined;
      console.log('挂断结束视频-------')
    },
    // 获取本地音视频流
    async getLocalStream(){
      let that = this;
      let obj = { audio: true, video: true };
      const stream = await navigator.mediaDevices.getUserMedia(obj); // 获取音视频流
      that.localVideo.srcObject = stream;
      that.localVideo.play();
      that.localStream = stream;
      return stream;
    }
  },
  mounted() {
    let that = this;
    that.$nextTick(()=>{
      that.localVideo = document.getElementById('localVideo');
      that.remoteVideo = document.getElementById('remoteVideo');
    })
    let sock = io('localhost:3000'); // 对应服务的端口
    // 连接成功
    sock.on('connectionSuccess', (sock) => {
      console.log('连接成功:');
    });
    sock.emit('joinRoom', roomId) // 前端发送加入房间事件
    sock.on('callRemote', (sock) => {
      // 如果是发送方自己收到这个事件就不用管
      if (!that.caller){ // 不是发送方(用户A)
        that.called = true; // 接听方
        that.calling = true; // 视频通话中
      }
    });
    sock.on('acceptCall',async ()=>{
      if (that.caller){
        // 用户A收到用户B同意视频的请求
        that.peer = new RTCPeerConnection();
        // 添加本地音视频流
        that.peer.addStream && that.peer.addStream(that.localStream);
        // 通过监听onicecandidate事件获取candidate信息
        that.peer.onicecandidate = (event) => {
          if (event.candidate) {
            console.log('用户A获取candidate信息', event.candidate);
            // 通过信令服务器发送candidate信息给用户B
            sock.emit('sendCandidate', {roomId, candidate: event.candidate})
          }
        }
        // 接下来用户A和用户B就可以进行P2P通信流
        // 监听onaddstream来获取对方的音视频流
        that.peer.onaddstream = (event) => {
          console.log('用户A收到用户B的stream',event.stream);
          that.calling = false;
          that.communicating = true;
          that.remoteVideo.srcObject = event.stream;
          that.remoteVideo.play();
        }
        // 生成offer
        let offer = await that.peer.createOffer({
          offerToReceiveAudio: 1,
          offerToReceiveVideo: 1
        })
        console.log('offer', offer);
        // 设置本地描述的offer
        await that.peer.setLocalDescription(offer);
        // 通过信令服务器将offer发送给用户B
        sock.emit('sendOffer', { offer, roomId })
      }
    })
    // 收到offer
    sock.on('sendOffer',async (offer) => {
      if (that.called){ // 接收方 - 用户B
        console.log('收到offer',offer);
        // 创建自己的RTCPeerConnection
        that.peer = new RTCPeerConnection();
        // 添加本地音视频流
        const stream = await that.getLocalStream();
        that.peer.addStream && that.peer.addStream(stream);
        // 通过监听onicecandidate事件获取candidate信息
        that.peer.onicecandidate = (event) => {
          if (event.candidate) {
            console.log('用户B获取candidate信息', event.candidate);
            // 通过信令服务器发送candidate信息给用户A
            sock.emit('sendCandidate', {roomId, candidate: event.candidate})
          }
        }
        // 接下来用户A和用户B就可以进行P2P通信流
        // 监听onaddstream来获取对方的音视频流
        that.peer.onaddstream = (event) => {
          console.log('用户B收到用户A的stream',event.stream);
          that.calling = false;
          that.communicating = true;
          that.remoteVideo.srcObject = event.stream;
          that.remoteVideo.play();
        }
        // 设置远端描述信息
        await that.peer.setRemoteDescription(offer);
        let answer = await that.peer.createAnswer();
        console.log('用户B生成answer',answer);
        await that.peer.setLocalDescription(answer);
        // 发送answer给信令服务器
        sock.emit('sendAnswer', { answer, roomId })
      }
    })
    // 用户A收到answer
    sock.on('sendAnswer',async (answer) => {
      if (that.caller){ // 接收方 - 用户A   判断是否是发送方
        // console.log('用户A收到answer',answer);
        await that.peer.setRemoteDescription(answer);
      }
    })
    // 收到candidate信息
    sock.on('sendCandidate',async (candidate) => {
      console.log('收到candidate信息',candidate);
      // await that.peer.addIceCandidate(candidate) // 用户A和用户B分别收到candidate后,都添加到自己的peer对象上
      // await that.peer.addCandidate(candidate)
      await that.peer.addIceCandidate(candidate)
    })
    // 挂断
    sock.on('hangUp',()=>{
      that.reset()
    })
    that.wsSocket = sock;
  }
}
</script>

服务端代码

const socket = require('socket.io');
const http = require('http');
const server = http.createServer()
const io = socket(server, {
    cors: {
        origin: '*' // 配置跨域
    }
});
io.on('connection', sock => {
    console.log('连接成功...')
    // 向客户端发送连接成功的消息
    sock.emit('connectionSuccess');
    sock.on('joinRoom',(roomId)=>{
        sock.join(roomId);
        console.log('joinRoom-房间ID:'+roomId);
    })
    // 广播有人加入到房间
    sock.on('callRemote',(roomId)=>{
        io.to(roomId).emit('callRemote')
    })
    // 广播同意接听视频
    sock.on('acceptCall',(roomId)=>{
        io.to(roomId).emit('acceptCall')
    })
    // 接收offer
    sock.on('sendOffer',({offer,roomId})=>{
        io.to(roomId).emit('sendOffer',offer)
    })
    // 接收answer
    sock.on('sendAnswer',({answer,roomId})=>{
        io.to(roomId).emit('sendAnswer',answer)
    })
    // 收到candidate
    sock.on('sendCandidate',({candidate,roomId})=>{
        io.to(roomId).emit('sendCandidate',candidate)
    })
    // 挂断结束视频
    sock.on('hangUp',(roomId)=>{
        io.to(roomId).emit('hangUp')
    })
})
server.listen(3000, () => {
    console.log('服务器启动成功');
});

完整代码gitee地址: https://gitee.com/wade-nian/wdn-webrtc.git

参考文章:基于WebRTC实现音视频通话_npm create vite@latest webrtc-client

要是在拨打电话过程中,无法打开摄像头或者麦克风,浏览器也没有弹出获取摄像头及麦克风的权限运行,这是需要进行浏览器安全源的设置,步骤如下:

1、在 chrome 中 输入 chrome://flags/#unsafely-treat-insecure-origin-as-secure

2、找到Insecure origins treated as secure

3、添加你服务器的地址 例如:http://192.168.1.10:8080

4、选择Enabled属性

5、点击右下角的Relaunch即可

到此这篇关于vue项目基于WebRTC实现一对一音视频通话的文章就介绍到这了,更多相关vue音视频通话内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue中Vue.use()的原理及基本使用

    Vue中Vue.use()的原理及基本使用

    相信很多人在用Vue使用别人的组件时,会用到 Vue.use() ,例如:Vue.use(VueRouter)、Vue.use(MintUI),这篇文章主要给大家介绍了关于Vue中Vue.use()的原理及基本使用的相关资料,需要的朋友可以参考下
    2021-10-10
  • vue elementUI 处理文件批量上传方式

    vue elementUI 处理文件批量上传方式

    这篇文章主要介绍了vue elementUI 处理文件批量上传方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • 浅谈vue中组件绑定事件时是否加.native

    浅谈vue中组件绑定事件时是否加.native

    今天小编就为大家分享一篇浅谈vue中组件绑定事件时是否加.native,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11
  • vue如何使用el-table遍历循环表头和表体数据

    vue如何使用el-table遍历循环表头和表体数据

    这篇文章主要介绍了vue如何使用el-table遍历循环表头和表体数据,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • Nuxt项目支持eslint+pritter+typescript的实现

    Nuxt项目支持eslint+pritter+typescript的实现

    这篇文章主要介绍了Nuxt项目支持eslint+pritter+typescript的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-05-05
  • vue省市区三联动下拉选择组件的实现

    vue省市区三联动下拉选择组件的实现

    本篇文章主要介绍了vue省市区三联动下拉选择组件的相关知识。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-04-04
  • 教你三分钟掌握Vue过滤器filters及时间戳转换

    教你三分钟掌握Vue过滤器filters及时间戳转换

    这篇文章教你三分钟掌握Vue过滤器filters及时间戳转换,本文将结合时间戳转换的例子带你快速了解filters的用法,需要的朋友可以参考下
    2023-03-03
  • Vue+Element-U实现分页显示效果

    Vue+Element-U实现分页显示效果

    这篇文章主要为大家详细介绍了Vue+Element-U实现分页显示效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • vuepress打包部署踩坑及解决

    vuepress打包部署踩坑及解决

    这篇文章主要介绍了vuepress打包部署踩坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • Vue实现注册页面的用户交互详解

    Vue实现注册页面的用户交互详解

    这篇文章主要为大家详细介绍了Vue实现注册页面的用户交互的相关知识,文中的示例代码讲解详细,对我们深入掌握vue有一定的帮助,需要的小伙伴可以参考下
    2023-12-12

最新评论