基于node+vue+webrtc+socket.io实现实时视频、录制视频、录音并上传为mp3、mp4文件

前言

有个项目需要用到视频技术以及录制音频等,就去研究了一下。当然很多是有个思路之后借助AI的。

图挺复杂的,我就自己画了一个简单点的,也当作一个思路。

 自己先建一个vue项目,因为我这里是个demo,所以我router和store都没建。

创建完之后先加载一个socket.io。

npm i socket.io-client

实时视频

首先是加入房间,其实就是弄一个roomId,让两个人都加入之后,服务端发送的时候直接往这个roomid里面的用户发送就行。我这里就都设置为001。

发起邀请

点击发送邀请后会开启本地的视频,生成stream流,然后把video的src设置为这个stream流。

<script setup>
import { ref, onMounted } from 'vue'
import { io } from 'socket.io-client';
const socket = ref(null)
const roomid = '001'
const localStream = ref(null)//本人视频流
const localVideo = ref(null)//播放本人视频的video
const inviter = ref(false)//是否是邀请的人
const Beinviter = ref(false)//是否是被邀请的人
// 获取视频和音频流
const getLocalStream = async () => {
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: true,
      video: { facingMode: "user" }
    })
    localVideo.value.srcObject = stream
    localVideo.value.play()
    localStream.value = stream
    return stream
}

onMounted(() => {
  socket.value = io('http://127.0.0.1:3000', {
    transports: ['websocket'], // 指定传输方式,如WebSocket
    autoConnect: true, // 是否自动连接
    reconnection: true, // 是否自动重新连接
    reconnectionAttempts: 3, // 重新连接尝试次数
    reconnectionDelay: 1000, // 重新连接延迟时间(毫秒)
  })
  socket.value.emit('joinroom', roomid)
})

const invite = async () => {
  inviter.value = true
  await getLocalStream()
  // getOffice()
  socket.value.emit('invite', roomid)
}
</script>

<template>
  <div style="display: flex;text-align: center;">
    <div class="box" style="width: 200px; height: 300px; border: 1px solid black; margin: 20px auto;">
      <h2>自己的</h2>
      <video ref="localVideo" style="width: 100%; height: 100%;" autoplay muted playsinline></video>
    </div>
    <div class="box" style="width: 200px; height: 300px; border: 1px solid black; margin: 20px auto;">
      <h2>对方的</h2>
      <video ref="remoteVideo" style="width: 100%; height: 100%;" autoplay muted playsinline></video>
    </div>
    <div class="box" style="width: 200px; height: 300px; border: 1px solid black; margin: 20px auto;">
      <h2>录制的</h2>
      <video ref="recorderVideo" style="width: 100%; height: 100%;" controls></video>
    </div>
    <div class="box" style="width: 200px; height: 50px; border: 1px solid black; margin: 20px auto;">
      <h2>录制的音频</h2>
      <audio ref="recorderAudio" controls></audio>
    </div>
  </div>
  <div style="width: 100%;
height: 300px;"></div>
  <button @click="invite">发起</button>
  <button @click="agree">接受</button>
  <button @click="beginRecorder">开启录制</button>
  <button @click="overRecorder">结束录制</button>
  <button @click="beginRecorderAudio">只录制音频</button>
  <button @click="overRecorderAudio">结束录制音频</button>
  <button>挂断</button>
</template>


<style scoped>

</style>

搭建一个服务端来转发。

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const cors = require('cors');

const app = express();
const server = http.createServer(app);
const io = socketIo(server);
let connectedUsers = {};
const PORT = process.env.PORT || 3000;
let index=0
// 允许来自所有来源的跨域请求
app.use(cors());
io.on('connection', (socket) => {
    socket.emit('connectsuccess', socket.id)
    // 存储连接的用户
    connectedUsers[socket.id] = socket;
    socket.on('disconnect', () => {
        console.log('A user disconnected');
    });
    socket.on('joinroom', value => {
        socket.join(value)
    })
    socket.on('invite', roomid => {
        socket.to(roomid).emit('callRemote')
    })
});

server.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

 然后本地的视频就可以了!(后面的再一一实现 )

接收邀请

对方接收到之后,需要首先接收,然后点击接收之后再发送一次给服务端,告诉发起者我同意了。




//接收邀请
  socket.value.on('callRemote', () => {
    if (!inviter.value) {
      Beinviter.value = true
      console.log('收到邀请')
    }
  })



const agree = () => {
  socket.value.emit('replyInvite', {
    reply: true,
    roomid
  })
}

同样服务端要转发同样的事件。

    //服务端
socket.on('replyInvite', ({ reply, roomid }) => {
        console.log(reply)
        if (reply) {
            socket.to(roomid).emit('otherReply', true)
        }
    })

 

邀请者生成offer

邀请者收到对方的同意之后就可以生成offer并发送出去了。

  
  const peer = ref(null)
  const offer = ref(null)
  peer.value = new RTCPeerConnection()
//收到对方同意邀请
  socket.value.on('otherReply', async (value) => {
    if (value) {
      if (inviter.value) {
        await getOffice()//生成offer
        console.log('生成offer', offer.value)
        socket.value.emit('sendOffer', {
          offer: offer.value,
          roomid
        })
      }
    }
  })

//生成offer
const getOffice = async () => {
  if (localStream.value) {
    peer.value.addStream(localStream.value)
    const offerCreated = await peer.value.createOffer({
      offerToReceiveAudio: 1,
      offerToReceiveVideo: 1
    })
    offer.value = offerCreated
    await peer.value.setLocalDescription(offerCreated)
    // getAnswer()

  } else {
    console.error('Local stream is not available.')
  }
}

同样管理端要转发offer

    socket.on('sendOffer', ({ offer, roomid }) => {
        console.log(roomid)
        socket.to(roomid).emit('otherOffice', offer)
    })

被邀请者收到offer。

  socket.value.on('otherOffice', async (Office) => {
    if (Beinviter.value) {
      console.log('收到offer',Office)
    }
  })

 一边点邀请,一边点接收就可以了。

 接收者打开本地视频并生成answer

接收者收到offer就可以打开视频并且发送answer了。

 socket.value.on('otherOffice', async (Office) => {
    if (Beinviter.value) {
      offer.value = Office
      console.log('收到Office', Office)
      //添加本地音视频流
      const stream = await getLocalStream()
      peer.value.addStream(stream)
      //设置远端描述信息SDP
      await peer.value.setRemoteDescription(offer.value)
      //生成answer
      const answer = await peer.value.createAnswer()
      console.log('生成answer', answer)
      //在本地设置answer信息
      await peer.value.setLocalDescription(answer)
      //发送answer
      socket.value.emit('sendAnswer', { answer: answer, roomid: roomid })
    }
  })

服务端转发answer


                
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值