【问题标题】:Video chat via WEBRTC does not work in both FireFox and Chrome通过 WEBRTC 进行视频聊天在 FireFox 和 Chrome 中都不起作用
【发布时间】:2022-01-20 21:31:34
【问题描述】:

我对此有点陌生,我已经连续花了大约 4 天的时间来解决这个问题,但我仍然无法让它工作,所以如果我遗漏了什么,我有点兴奋,但我会尽力解释。

p>

目标:

一旦第二个呼叫者进入房间,呼叫就会启动。为简单起见,用户名和房间位于 URL 中。我正在尝试启动一个 WEBRTC MESH 网络。我了解其中的缺陷,但性能不是我关心的问题。我希望能够实现每种风格。稍后我将介绍 SFU 和 MCU。代码也不是 100% 没有错误的,但这只是一个粗略的草图。

随着 icecandidates “涓涓细流”进来,如果没有进行远程描述,我会尝试推迟添加它们。在控制台的“许多”日志中,代码似乎最终会添加所有候选者,据我所知应该这样做。因此,对于setLocalDescription 的每次调用,都会产生大约 40 个候选人,总共 80 个。因此,好像会有 80 次对套接字的调用,每个都应该添加到单个连接中,这似乎正在发生。最后,我无法判断应用程序是否由于代码或服务器而无法正常工作,感谢您的帮助。提前谢谢你。


堆栈:

使用 Django Channels for WebScotets 的 Django 后端

Coturn(在 Linux 上运行)

反应前端

发生了什么:

我想测试以确保我当然有一个好的配置。以下是两种浏览器的屏幕截图。这两款浏览器产品的结果令人质疑。虽然他们似乎“聚集”好。每个测试都有自己独特的反应

响应:返回 701,尽管 srf 和中继返回 Done。我认为“中继”是我想要的,因为它是一个 TURN 服务器。 ;我有的

火狐

响应不可达?

我已经为 TCP 和 UDP 打开了 3478 和 5439 端口


CommunicationScreen.js

import React, { useState, useEffect } from 'react'
import useWebSocket from "react-use-websocket"

import {Row, Col, Button } from "react-bootstrap"




function RemoteVideo({stream}){
    const vidRef = React.useRef(null)
    useEffect(()=>{
        if (!vidRef.current){
            vidRef.current.srcObject = stream
            vidRef.current.play()
        }
    },[vidRef])

    return(<div>
        <video ref={vidRef} src={vidRef.current} autoPlay />

    </div>)
}

function MyVideo(props){

    const { username } = props

    const mediaConstraints = {
        audio:true,
        video: { width: 600, height: 350 }
    }

    const videoRef = React.useRef(null) 

    const startCamera = async ()=>{
        let stream = await navigator.mediaDevices.getUserMedia(mediaConstraints)
        videoRef.current.srcObject = stream
        console.log('my stream', stream)
        props.setLocalStream(stream)
    
    }
    
    useEffect(()=>{
        startCamera()
    }, [])

    return(
        <div>
            <h2>User {username}</h2>
            <video ref={videoRef} src={videoRef.current} autoPlay/>
            
        </div>)
}

const MyMemoVideo = React.memo(MyVideo)
const RemoteMemoVideo = React.memo(RemoteVideo)

export default function CommunicationScreen({history, match}) {
    const iceServers = {
        iceServers: [
            {
                urls: 'turn:picksixcity.com:3478',
                username: <username>, // these dont matter as turnserver.conf is set to no-auth for testing
                credential: <password>
            }
        ]
    }
    const { roomName, username } = match.params 

    const { sendMessage, lastMessage, readyState } = useWebSocket('ws://localhost:8000/ws/' + roomName + '/'+username+'/')
    const [message, setMessage] = useState("")
    const [chat, setChat] = useState("")

    const [peers, setPeers] = useState([])
    const [pendingCandidates, setPendingCandidates] = useState([])
    const [socketId, setSocketId] = useState(null)
    const [localStream, setLocalStream] = useState()

    const [remoteStreams, setRemoteStreams] = useState([])
    let peerId;
    

    const setMyLocalStreamCallback = React.useCallback((stream)=>{
        // PRevents re-create of function on each render
        // for memoized Video
        setLocalStream(stream)
    },[])

    const createPeerConnection = React.useCallback(()=> {
        return new RTCPeerConnection(iceServers)
    }, [])

    function addPendingCandidates(peerId){
        console.log('adding pendingCandidates to connection', peerId)
        pendingCandidates.forEach(candidate=>{ // each candidate iterate over peer
            let foundPeer = peers.find(peer=>peer.id === peerId)
            console.log('adding candidate from pending')
            foundPeer.connection.addIceCandidate(candidate).catch(err=>console.log('Err adding candi', err))
        })
        console.log('completed adding candidates')
    }

    function onIceCandidate(e, peerId){ // send ICE candidate
        if (e.candidate) {
            sendMessage(JSON.stringify({
                'webrtc_ice_candidate': {
                    room: roomName,
                    label: e.candidate.sdpMLineIndex,
                    candidate: e.candidate.candidate,
                    // peerId:peerId
                },
            }))
        }
    }

    function submitMessage(){
        sendMessage(JSON.stringify({'message':message}))
        setMessage("")
    }

    function addRemoteStream(e){
        // Called when tracks are added
        console.log('remote stream object', e)
        let newStream = e.streams[0]
        setRemoteStreams(streams=>[
            ...streams.filter(stream=> stream.id !== newStream.id),
            newStream])
    }

    function addLocalTracks(rtcPeerConnection){
        // Add local tracks, media Track and Audio track
        // Tracks can be added to PeerConnection object before connecting to 
        // a Peer
        localStream.getTracks().forEach(track=>
            rtcPeerConnection.addTrack(track, localStream))
    }

    async function createOffer(newPeer, myPeerConnection){
        //Send an SIP request containing SDP information
        // uwth this peerconnection object to 
        //everyone in the room

        //this invokdes sendIceCandidates
        console.log('Creating offer')
        let sessionDescription;
        try{
            sessionDescription = await myPeerConnection.createOffer()
            myPeerConnection.setLocalDescription(sessionDescription)
        }catch(error){
            console.log('Error creating offer')
        }
        sendMessage(JSON.stringify({"webrtc_offer" : { sdpInfo:sessionDescription, room:roomName, user:username, peerId:newPeer } }))
        console.log('offer sent')
    }


    async function createAnswer(newPeer, myPeerConnection){
        let sessionDescription;
        try{
            sessionDescription = await myPeerConnection.createAnswer()
            myPeerConnection.setLocalDescription(sessionDescription) // will initiate onicecandidateevent 
        }catch(error){
            console.log('Error creating answer', error)
            console.log('session description ', sessionDescription)
        }
        sendMessage(JSON.stringify({"webrtc_answer": { sdpInfo: sessionDescription, room: roomName, peerId:newPeer } }))
    }


    const handleLastMessage = React.useCallback((messageDict)=>{
        // Hanldes last Websocket sent Message (could be anything)
        // MEssageDict is a React built returned
        // websocket object that wraps .onmessage
        //access our own 'message' property
        let data = JSON.parse(messageDict.data)
        if (data.message){
            setChat(chat + "\n" + data.message)
        }else if (data.connection_made){
            // addLocalTracks(connection)
            setSocketId(data.connection_made.peerId)
            addPendingCandidates(data.connection_made.peerId)
            let {users} = data.connection_made
            if (users >= 2){
                // start call
                if (localStream){
                    sendMessage(JSON.stringify({ 'start_call': true }))
                }
            }
        }else if(data.start_call){
            // On 2nd person joining room, we signal to 'host'(or everyone not recently joined user)
            // to automatically start the call process
            let newPeer = data.start_call.skip
            if (newPeer !== socketId){
                // Once a user has joined, primeUser will invoke an offer   
                //    addLocalTracks(connection)
                let peerConnection = createPeerConnection()
                    peerConnection.addEventListener('icecandidateerror', e=>console.log('candiate error', e))
                    addLocalTracks(peerConnection)// side note: negotatiatneeded might actually be invoked here but we wont use it
                    peerConnection.ontrack = addRemoteStream

                    
                    peerConnection.onicecandidate = e=>onIceCandidate(e, newPeer)
                    
                    console.log('offer made')
                    createOffer(newPeer, peerConnection)
                    .then(res=>{
                        setPeers(peers=>([
                            ...peers.filter(peer=> peer.id !== newPeer), 
                            {
                                id:newPeer,
                            connection:peerConnection
                            }]
                        ))
                        addPendingCandidates(newPeer)
                    })
            }
        }  else if(data.webrtc_ice_candidate){
            // console.log('this is peer', data.peerId)
            let icePeerId = data.peerId
            let found = false;
            let add = false;
            peers.forEach(peer=>{
                // if (icePeerId === peer.id){
                    found = true;
                    // Offers and answers need to be set on connection 
                    // for this to work 
                    if (!peer.connection.remoteDescription){
                        console.log('remote confirmed')
                        if(!add){
                            setPendingCandidates(candidates=>[...candidates, data.webrtc_ice_candidate])
                            add = true; // Keep iterating but dont update the list
                        }
                    }else{
                        console.log('attach icecandidate to connection')
                        peer.connection.addIceCandidate(new RTCIceCandidate({
                        sdpMLineIndex: data.webrtc_ice_candidate.label,
                        candidate: data.webrtc_ice_candidate.candidate
                    }))
                    }
                // }
            })

            
        }else if (data.webrtc_offer){
            // offer is sent to indivudal channels
            // create new connection for each offer received
            console.log('got an offer')
            peerId  = data.peerId
            let newPeerConnection = createPeerConnection()
            newPeerConnection.addEventListener('icecandidateerror', e => console.log('candiate error', e))
            newPeerConnection.ontrack = addRemoteStream

            newPeerConnection.onicecandidate = e=>onIceCandidate(e, peerId)

            newPeerConnection.setRemoteDescription({type:"offer",sdp:data.webrtc_offer})
            .then(res=>{
                addLocalTracks(newPeerConnection)
                createAnswer(peerId, newPeerConnection) // answer with a connection
            }).catch(err=>console.log('Error creating offer', err))
        
            setPeers(peers=>([...peers, 
                { 
                    id:peerId,
                    connection:newPeerConnection}]))
            addPendingCandidates(peerId)

        }else if (data.webrtc_answer){
            let setPeer = peers.find(peer=>peer.id === data.peerId)
            setPeer.connection.setRemoteDescription({ type: "answer", sdp:data.webrtc_answer})
        }      
    }, [lastMessage, localStream])



    useEffect(()=>{
    
            if (lastMessage){
                handleLastMessage(lastMessage)
            }
        
    }, [lastMessage, handleLastMessage])


    return (
        <div>
            <h2>{roomName}</h2>
            <Row>
                <Col>
                    <MyMemoVideo setLocalStream={setMyLocalStreamCallback}  username={username} /> </Col>
                <Col>
                {!console.log(remoteStreams) && remoteStreams.map((stream, ix)=>(
                    <RemoteMemoVideo key={ix} stream={stream} />
                ))}</Col>
                <div style={{ textAlign: "center", margin: "0 auto", width: "100%" }}>
                    <input onChange={e => setMessage(e.target.value)} value={message} />
                    <Button onClick={submitMessage}>Submit</Button>
                </div>
                <Col>
                </Col>
                <Col>
                    <textarea value={chat} onChange={() => { }} rows="10" cols="50"/>
                </Col>
            </Row>
            
        </div>
    )
}



consumers.py

import json
from collections import defaultdict


from channels.generic.websocket import AsyncWebsocketConsumer
from channels.layers import get_channel_layer



room_count = defaultdict(lambda : 0)


class ChatConsumer(AsyncWebsocketConsumer):
    
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.user_name = self.scope['url_route']['kwargs']['user_name']
        self.room_group_name = 'chat_%s' % self.room_name

        # once a connection is made, join room to with channel layer 'utility' as 1st arg
        # async_to_sync(if used) becomes a callable when passed the 'channel layer utility'
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )


        room_count[self.room_group_name] +=1
        # send message that connection made
        peerId = self.channel_name
        peers[peerId] = ''
        await self.send(text_data=json.dumps({
            "connection_made":{
                "users":count,
                "peerId": peerId
                }
        }))

    async def disconnect(self, close_code): # runs anytime frontend loses connection
        room_count[self.room_group_name] -=1
        # make room leave group
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )


    async def receive(self, text_data):
        print(text_data)
        text_data_json = json.loads(text_data)
        message = text_data_json.get('message')
        start_call = text_data_json.get('start_call')
        webrtc_ice_candidate = text_data_json.get('webrtc_ice_candidate')
        webrtc_offer = text_data_json.get('webrtc_offer')
        webrtc_answer = text_data_json.get('webrtc_answer')

        if message:

            # broadcast a message to the group
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'chat_message', # this is mandatory and a function is needed below, name of method invoked by consumers that recieve 
                                            #  this message
                    'message':message
                }
            )
        elif start_call:
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'start_call',
                    'skip': self.channel_name
                }
            )

        elif webrtc_ice_candidate: 
            # broacast to all peers there is a candidate
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'webrtc_ice_candidate',
                    'candidate': webrtc_ice_candidate,
                    'peerId':webrtc_ice_candidate.get('peerId')
                }
            )

        elif webrtc_offer: # user 1 chennl, peerId == user2
            peer_to_send = webrtc_offer.get('peerId')
            if peer_to_send: # send offer back to requesting peer WITH callee peerId
                channel_layer = get_channel_layer()
                await channel_layer.send(peer_to_send,
                {
                    "type":"single_offer",
                    'webrtc_offer':webrtc_offer['sdpInfo']['sdp'], "peerId":self.channel_name})
            else:
                await self.channel_layer.group_send(
                    self.room_group_name,
                    {
                        'type': 'webrtc_offer',
                        'webrtc_offer': webrtc_offer['sdpInfo']['sdp']
                    }
                )            
        elif webrtc_answer:
            peer_to_send = webrtc_answer.get('peerId')
            if peer_to_send:
                channel_layer = get_channel_layer()
                
                await channel_layer.send(peer_to_send,
                {
                    'type':"single_answer", 
                    'webrtc_answer':webrtc_answer['sdpInfo']['sdp'], "peerId":self.channel_name})

            else:
                await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'webrtc_answer',
                    'webrtc_answer': webrtc_answer['sdpInfo']['sdp']
                }
            )               

    async def start_call(self, event):
        # Broadcast to entire toom that call is start 
        await self.send(text_data=json.dumps({
            "start_call" : {'skip':event['skip']}
        }))
               
        
    async def chat_message(self, event):
        # this is the type of event described in the async_to_sync, instead of calling channel_layer.recieve, this func is a wrapper
        # every consumer instance will have this, this is like a 'listener' 
        message = event['message']

        await self.send(text_data=json.dumps({
            'message':message
        }))

    async def webrtc_ice_candidate(self, event):
        # this broadcasts to all channel a new arrival
        candidate = event['candidate']
        peerId = event['peerId']
        await self.send(text_data=json.dumps({
            'webrtc_ice_candidate':candidate,
        }))


    async def single_offer(self, event):
        print('single offer')
        webrtc_offer = event['webrtc_offer']
        await self.send(text_data=json.dumps({'webrtc_offer':webrtc_offer,'peerId':event['peerId']}))

    async def webrtc_offer(self, event):
        print('group offer')
        sdp = event['webrtc_offer']
        await self.send(text_data=json.dumps({
            'webrtc_offer': sdp
        }))

    async def webrtc_answer(self, event):
        sdp = event['webrtc_answer']
        await self.send(text_data=json.dumps({
            'webrtc_answer': sdp
        })) 

    async def single_answer(self, event):
        webrtc_answer = event['webrtc_answer']
        await self.send(text_data=json.dumps({'webrtc_answer':webrtc_answer, 'peerId':event['peerId']}))

<hr>

Final Result
----

**Firefox** 
displays "there has been an ICE error"


**chrome**
The icecandidateerror callback is iniated and returns about the Host does not resolve

In both results, one can see the **remoteStreams** containing a media  stream object.

服务器日志 我不确定每个浏览器从哪里开始和结束,如果需要,我可以重新测试,关键是它们似乎正在访问服务器,所以主机正在“解析”不?

...

18889: session 000000000000000035: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
18889: session 000000000000000034: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
18890: session 000000000000000036: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
18891: handle_udp_packet: New UDP endpoint: local addr 142.11.199.8:3478, remote addr 68.80.137.255:63401
18891: IPv4. Local relay addr: 142.11.199.8:63707
18891: session 000000000000000037: new, realm=<picksixcity.com>, username=<>, lifetime=3600
18891: session 000000000000000037: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
...
18894: session 000000000000000034: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
18895: session 000000000000000037: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
18897: session 000000000000000037: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
18902: session 000000000000000037: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19507: handle_udp_packet: New UDP endpoint: local addr 142.11.199.8:3478, remote addr 68.80.137.255:55567
19507: session 000000000000000038: realm <picksixcity.com> user <>: incoming packet BINDING processed, success
19507: IPv4. Local relay addr: 142.11.199.8:56232
19507: session 000000000000000038: new, realm=<picksixcity.com>, username=<>, lifetime=600
19507: session 000000000000000038: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19507: session 000000000000000038: refreshed, realm=<picksixcity.com>, username=<>, lifetime=0
19507: session 000000000000000038: realm <picksixcity.com> user <>: incoming packet REFRESH processed, success
19508: session 000000000000000038: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:55567, reason: allocation timeout
19508: session 000000000000000038: delete: realm=<picksixcity.com>, username=<>
19589: handle_udp_packet: New UDP endpoint: local addr 142.11.199.8:3478, remote addr 68.80.137.255:53749
19589: IPv4. Local relay addr: 142.11.199.8:58705
19589: session 000000000000000039: new, realm=<picksixcity.com>, username=<>, lifetime=3600
19589: session 000000000000000039: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19589: handle_udp_packet: New UDP endpoint: local addr 142.11.199.8:3478, remote addr 68.80.137.255:53751
19589: IPv4. Local relay addr: 142.11.199.8:60027
19589: session 000000000000000040: new, realm=<picksixcity.com>, username=<>, lifetime=3600
19589: session 000000000000000040: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
...
19591: session 000000000000000039: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19591: session 000000000000000040: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19592: session 000000000000000039: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19592: session 000000000000000040: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19596: session 000000000000000039: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19596: session 000000000000000040: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19626: handle_udp_packet: New UDP endpoint: local addr 142.11.199.8:3478, remote addr 68.80.137.255:65336
19626: IPv4. Local relay addr: 142.11.199.8:59891
19626: session 001000000000000025: new, realm=<picksixcity.com>, username=<>, lifetime=3600
19626: session 001000000000000025: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19626: handle_udp_packet: New UDP endpoint: local addr 142.11.199.8:3478, remote addr 68.80.137.255:60802
19626: IPv4. Local relay addr: 142.11.199.8:54372
19626: session 000000000000000041: new, realm=<picksixcity.com>, username=<>, lifetime=3600
19626: session 000000000000000041: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19626: session 001000000000000025: realm <picksixcity.com> user <>: 
...

19626: session 001000000000000025: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
...
19716: handle_udp_packet: New UDP endpoint: local addr 142.11.199.8:3478, remote addr 68.80.137.255:50814
19716: session 000000000000000042: realm <picksixcity.com> user <>: incoming packet BINDING processed, success
19716: IPv4. Local relay addr: 142.11.199.8:64560
19716: session 000000000000000042: new, realm=<picksixcity.com>, username=<>, lifetime=600
19716: session 000000000000000042: realm <picksixcity.com> user <>: incoming packet ALLOCATE processed, success
19716: session 000000000000000042: refreshed, realm=<picksixcity.com>, username=<>, lifetime=0
19716: session 000000000000000042: realm <picksixcity.com> user <>: incoming packet REFRESH processed, success
19717: session 000000000000000042: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:50814, reason: allocation timeout
19717: session 000000000000000042: delete: realm=<picksixcity.com>, username=<>
20814: session 000000000000000029: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:54339, reason: allocation timeout
20814: session 000000000000000029: delete: realm=<picksixcity.com>, username=<>
20814: session 000000000000000030: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:54347, reason: allocation timeout
20814: session 000000000000000030: delete: realm=<picksixcity.com>, username=<>
20815: session 000000000000000031: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:54377, reason: allocation timeout
20815: session 000000000000000031: delete: realm=<picksixcity.com>, username=<>
20815: session 001000000000000019: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:54357, reason: allocation timeout
20815: session 001000000000000019: delete: realm=<picksixcity.com>, username=<>
20815: session 000000000000000032: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:54367, reason: allocation timeout
20815: session 000000000000000032: delete: realm=<picksixcity.com>, username=<>
21237: IPv4. tcp or tls connected to: 34.230.34.4:29009
21237: HTTPS connection has been disabled due Vulnerability in the Web interface !!!
...
21463: session 001000000000000021: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:60122, reason: allocation timeout
21463: session 001000000000000021: delete: realm=<picksixcity.com>, username=<>
21463: session 000000000000000033: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:53251, reason: allocation timeout
21463: session 000000000000000033: delete: realm=<picksixcity.com>, username=<>
21464: session 001000000000000022: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:53261, reason: allocation timeout
21464: session 001000000000000022: delete: realm=<picksixcity.com>, username=<>
21486: session 001000000000000023: closed (2nd stage), user <> realm <picksixcity.com> origin <>, local 142.11.199.8:3478, remote 68.80.137.255:60417, reason: allocation timeout
21486: session 001000000000000023: delete: realm=<picksixcity.com>, username=<>

【问题讨论】:

    标签: python reactjs webrtc video-streaming django-channels


    【解决方案1】:

    哇。原来我比我想象的要近得多。第一关。我只是去重新检查了别人的“简单示例”。 https://github.com/shanet/WebRTC-Example。 令人惊讶的是,即使使用已弃用的代码,此示例也能完美运行。

    ICE 候选人

    “你不想要我们自己的”意味着发出呼叫以开始发送 iceCandidates 的通道也不应该接收到 SAME 信号。因此,根据后端,您可以省略不应接收它的通道。因此,无论是否在后端/前端进行过滤,为了实现这一点,我发送了创建报价/答案的频道的 ID,以及 ICE 候选者,并确保忽略那些返回到的消息“ice_candidate”同一个频道。如果channel A发送offer,chaneel A在调用(setLocalDescription)后忽略消息返回。

    远程视频

    使用反应。我认为这是我在主要组件(CommunicationScreen)中使用 useEffect 的方式,与使用 RemoteVideo 作为记忆组件混合。我删除了 memoization 并将 remoteStreams 添加到主组件中 useEffect 的依赖项中。我也只是使用 vanilla JS 添加远程视频以验证它们是否实际工作,然后我“按我的方式工作”

    WEBRTC

    我测试配置是否正确的最佳方法是使用工作代码。我拿了上面的链接,只是添加了我自己的 TURN 网址和中提琴!我确实读过所有内容,如果使用回调函数设置,您可以获得 701 错误和“onicecandidateerror”,然后如果使用 Chrome,onicecanddiate 可以/将被调用,但忽略它。在我运行代码之前,这对我来说没有意义,忽略它。我的控制台,即使连接良好,仍然显示 701 错误,onicecandidateerror 仍在触发(因为我添加了回调来验证)。

    我最好的建议是,如果你在自己的 TURN 服务器上尝试这个,在候选人似乎聚集在 8Trickle-Ice* 测试站点之后(这个任务甚至可以跳过),只是以某人的工作示例,并添加您自己的 TURN 服务器配置。这是一个更好的指标,它是否有效。我花了太多时间试图更“彻底”地了解为什么我在使用 Trickle-ICE 工具的 Chrome/Firefox 中得到混合响应。

    【讨论】:

      猜你喜欢
      • 2016-03-19
      • 2013-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-30
      相关资源
      最近更新 更多