一旦我将媒体轨道添加到连接,WebRTC 数据通道连接就会失败

问题描述

我一直在尝试创建视频群组通话应用程序。它遵循网状拓扑结构,我遵循的步骤如下:

  1. 当客户端连接时。如果房间是第一个客户端,或者它向该房间中的所有其他客户端广播其套接字 ID,则会创建一个房间
  2. 当客户端收到套接字 id 时,它会向它发送一个报价,然后循环开始。其他客户端也遵循类似的流程。 问题是当我简单地创建数据通道时,它工作正常,但是一旦我开始添加媒体轨道,事情就会中断,甚至数据通道也无法连接。下面附上代码
import { io } from 'socket.io-client'
class RTConnection {
    constructor() {
        this.config = {
            iceServers: [
                {
                    urls: ["stun:stun.l.google.com:19302"]
                }
            ]
        }
        this.connections = {}
        this.videoElements = {}
        this.ICEqueue = {}
    }

    init = async (roomId,videoElemRef) => {
        this.roomId = roomId
        this.videoElemRef = videoElemRef.current
        let video = document.createElement('VIDEO')
        this.myVideo = video
        video.onloadedMetadata = e => video.play()
        this.videoElemRef.appendChild(video)
        const stream = await navigator.mediaDevices.getUserMedia({video: true,audio: false})
        video.srcObject = stream
        this.socket = io()
        
        this.socket.emit("join-room",roomId)
        
        this.socket.on('new-member',id => {
            this.createOffer(id)
        })

        this.socket.on("offer",(id,offer) => {
            this.acceptOffer(id,offer)
        })

        this.socket.on("new-ice-candidate",candidate) => {
            this.handleIceCandidate(id,candidate)
        })

        this.socket.on("answer",answer) => {
            this.acceptAnswer(id,answer)
        })
    }

    acceptAnswer = async (id,answer) => {
        const peerConnection = this.connections[id]
        await peerConnection.setRemoteDescription(answer)
        console.log("Answer Accepted")
    }

    handleIceCandidate = async (id,candidate) => {
        if(!candidate) {
            return
        }

        const peerConnection = this.connections[id]
        if(peerConnection.remoteDescription) {
            console.log("Setting Ice")
            console.log(candidate)
            await peerConnection.addIceCandidate(new RTCIceCandidate(candidate))
        } else {
            if(id in this.ICEqueue) {
                console.log("Storing Ice Candidates")
                console.log(id)
                this.ICEqueue[id].push(candidate)
            } else {
                this.ICEqueue[id] = []
            }
        }
    }

    hydrateIceCandidates = async (id) => {
        const peerConnection = this.connections[id]
        console.log("Hydrating Ice")
        for(let i in this.ICEqueue[id]) {
            const candidate = this.ICEqueue[id][i]
            await peerConnection.addIceCandidate(candidate)
        }
    }

    acceptOffer = async (id,offer) => {
        const peerConnection = new RTCPeerConnection()
        peerConnection.ondatachannel = e => {
            console.log("Data Channel Received")
            peerConnection.dc = e.channel
            peerConnection.dc.onopen = e => console.log("Connected!!!!")
        }
        this.connections[id] = peerConnection
        peerConnection.onicecandidate = e => {
            this.socket.emit("new-ice-candidate",e.candidate)
        }
        peerConnection.ontrack = (e) => {
            console.log("Received a track");
            console.log(e.streams)
            const video = document.createElement('VIDEO')
            this.videoElemRef.appendChild(video)
            video.onloadeddata = ev => {console.log("Meta Data Loaded") ; video.play()}
            video.srcObject = e.streams[0]
        }
        const stream = this.myVideo.srcObject
        console.log("Private Stream")
        console.log(stream)
        //await stream.getTracks().forEach(async track => peerConnection.addTrack(track,stream))
        await peerConnection.setRemoteDescription(offer)
        console.log("Remote Description Set")
        const ans = await peerConnection.createAnswer()
        console.log("Answer Created")
        await peerConnection.setLocalDescription(ans)
        console.log("Local Description Set")
        await this.hydrateIceCandidates(id)
        this.socket.emit("answer",id,ans)
    }

    createOffer = async (id) => {
        const peerConnection = new RTCPeerConnection()
        const stream = this.myVideo.srcObject
        //stream.getTracks().forEach(track => peerConnection.addTrack(track,stream))
        peerConnection.dc = peerConnection.createDataChannel("channel")
        peerConnection.dc.onopen = e => {

            console.log("Connected!!!")
        }
        this.connections[id] = peerConnection
        peerConnection.ontrack = (e) => {
            console.log("Received a track");
            console.log(e.streams)
            const video = document.createElement('VIDEO')
            console.log("Video Element")
            console.log(video)
            this.videoElemRef.appendChild(video)
            video.onloadedMetadata = ev => video.play()
            video.srcObject = e.streams[0]
        }
        peerConnection.onicecandidate = (e) => {
            this.socket.emit("new-ice-candidate",e.candidate)
        }
        const offer = await peerConnection.createOffer()
        await peerConnection.setLocalDescription(offer)
        console.log(peerConnection.localDescription)
        this.socket.emit("offer",offer)
    }

    stopStream = (stream,elem) => {
        stream.getTracks().forEach(track => {
            track.stop()
        })
        this.videoElemRef.current.removeChild(elem)
    }

    cleanMedia = () => {
        this.stopStream(this.videoEl.srcObject,this.videoEl)
        for(let id in this.videoElements) {
            this.stopStream(this.videoElements[id].srcObject,this.videoElements[id])
            delete this.videoElements[id]
        }
        delete this.videoElements
    }

    leaveCall = () => {
        for(let id in this.connections) {
            this.connections[id].close()
            delete this.connections[id]
        }
        delete this.connections
        this.cleanMedia()
        this.socket.disconnect()
    }
}

export default RTConnection

服务端代码

const express = require('express')
const fs = require('fs')
const app = express()
const path = require('path')
const v4 = require('uuid').v4

const options = {
    key: fs.readFileSync("key.pem"),cert: fs.readFileSync("cert.pem")
}

const server = require('http').createServer(app)
const io = require('socket.io')(server)
const port = process.env.port || '5000'

app.set('view engine','ejs')

app.use("/",express.static(path.resolve(__dirname + '/public')))

app.get('/',(req,res) => {
    res.render('home')   
})

app.get('/room',res) => {
    const roomId = v4()
    res.redirect(`/room/${roomId}`)
})

app.get('/room/:roomId',res) => {
    res.render('room',{roomId: req.params.roomId})
})

server.listen(port,() => {
    console.log(`Starting server on ${port}`)
})

io.on("connection",socket => {
    socket.on('join-room',(roomId) => {
        socket.join(roomId)
        socket.to(roomId).emit("new-member",socket.id)
        
        socket.on("new-ice-candidate",(candidate) => {
            socket.to(roomId).emit("new-ice-candidate",socket.id,candidate)
        })

        socket.on("offer",offer) => {
            socket.to(id).emit("offer",offer)          
        })

        socket.on("answer",answer) => {
            socket.to(id).emit("answer",answer)
        })

        socket.on("disconnecting",() => {
            for(let room of socket.rooms) {
                socket.to(room).emit("leaveCall",socket.id)
            }
        })
    })  
})

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)