1

我正在尝试在 playCanvas 中实现 WebRTC 通信,但在建立 Peers 之间的连接时遇到了麻烦。

从本地环境开始,我使用的是 Brave Browser 和 Ubuntu OS,连接已建立,我可以在对等点之间发送消息。以下是使用正确的协议 RTP 创建的正确报价。

   "v=0
o=- 6768969287305795266 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic: WMS
m=application 9 UDP/TLS/RTP/SAVPF 118
c=IN IP4 0.0.0.0
b=AS:30
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:kpEN
a=ice-pwd:0cEFgezZPzFBSsTbtx03XZUv
a=ice-options:trickle
a=fingerprint:sha-256 CD:93:28:C9:A1:17:59:C7:3C:E3:F9:03:48:02:59:08:50:FF:A1:8A:AF:53:9B:65:3B:62:B0:5E:E6:23:46:9F
a=setup:actpass
a=mid:0
a=sendrecv
a=msid:channel1 channel1
a=rtcp-mux
a=rtpmap:118 google-data/90000
a=ssrc:3090418315 cname:qaxoIuO1ybfnQaqY
a=ssrc:3090418315 msid:channel1 channel1
a=ssrc:3090418315 mslabel:channel1
a=ssrc:3090418315 label:channel1
"

这是有效的报价,在本地 PC 上,我可以在对等方之间连接和发送消息。但这仅适用于 Brave 浏览器,如果我打开 Chrome 或另一台带有 Brave 浏览器的 PC,它将无法工作,而且如果我在 playcanvas 上实现 WebRTC,它也将无法工作。

以下是我从报价中获得的数据,我认为这是不工作的主要原因。因为它使用SCTP协议。

"v=0
o=- 5811252825165405755 3 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=extmap-allow-mixed
a=msid-semantic: WMS
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=candidate:1528355922 1 udp 2113937151 920a8f71-bd47-4dac-9a24-497783686050.local 59907 typ host generation 0 network-cost 999
a=ice-ufrag:NjXz
a=ice-pwd:M3FdXA3wNEA25FQaFU3GwcVd
a=ice-options:trickle
a=fingerprint:sha-256 F0:59:BF:FF:AB:8B:4A:1A:A2:D8:0E:57:DD:AD:7B:14:24:7E:A7:6F:CC:C3:B2:BB:83:96:BB:53:49:71:CE:9A
a=setup:actpass
a=mid:0
a=sctp-port:5000
a=max-message-size:262144
"

这是我在 playcanvas 上收到的错误。

Uncaught (in promise) DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Data channel type mismatch. Expected RTP, got SCTP.

下面是 playcanvas 内部的实现。

NetworkManager.id = null;
NetworkManager.socket = null;
NetworkManager.clients = null;
NetworkManager.user = null;
NetworkManager.username = null;
NetworkManager.connectedUser = null;
NetworkManager.yourConn = null;
NetworkManager.dataChannel = null;


// initialize code called once per entity
NetworkManager.prototype.initialize = function() {
    
    // Context
    var self = this;

    this.socket = new WebSocket(this.address, "wss");
    
    this.clients = [];
    
    // Listeners
    this.socket.onopen = function(event){
        self.initState();
    };
    
    this.socket.onmessage = function(message) {
        const data = JSON.parse(message.data);
        switch (data.type) {
            case 'login':
                self.handleLogin(data.success, data.name);
                break;
            case 'offer':
                self.handleOffer(data.offer, data.name);
                break;
            case 'answer':
                self.handleAnswer(data.answer);
                break;
            case 'candidate':
                self.handleCandidate(data.candidate);
                break;
            default:
                break;
        }
    
    };

    this.socket.onerror = function (err) {
        console.log('Got error', err);
    };
    
};

// Called every frame
NetworkManager.prototype.update = function(dt) {
    // this.updatePosition();
    // if(this.app.keyboard.wasPressed(pc.KEY_SPACE)){
    //     this.sendPayload("Soldo");
    // }
};

// Functions
NetworkManager.prototype.handleMembers = function(users) {
    this.clients = users;
    this.initializePlayers(this.clients);
};

NetworkManager.prototype.handleLogin = function(success, username) {
    if (!success) {
        alert('try different username');
    } else {
        
        this.clients.push({username});
        
        
        var configuration = {
            iceServers: [{ url: 'stun:stun2.1.google.com:19302' }],
        };

        this.yourConn = new RTCPeerConnection(configuration, {
            optional: [{ RtpDataChannels: true }],
        });
        this.yourConn.onicecandidate = event => {
            if (event.candidate) {
                this.sendMessage({
                    type: 'candidate',
                    candidate: event.candidate,
                });
            }
        };
        

        this.dataChannel = this.yourConn.createDataChannel('channel1', {
            reliable: true,
        });

        this.dataChannel.onerror = function(error) {
            console.log('Error: ', error);
        };

        this.dataChannel.onmessage = function(event) {
            console.log('on message data channel');
            console.log(event.data.data);
        };

        this.dataChannel.onclose = () => {
            console.log('data channel is closed.');
        };
        
    }
    
    
};

NetworkManager.prototype.login = function(name) {
    username = name;

    if (username.length > 0) {
        this.sendMessage({
            type: 'login',
            name: username,
        });
    }
};


NetworkManager.prototype.createOffer = function(name) {
    const callToUsername = name;

    if (callToUsername.length > 0) {
        this.connectedUser = callToUsername;

        this.yourConn.createOffer(
            offer => {
                this.sendMessage({
                    type: 'offer',
                    offer: offer,
                });
                this.yourConn.setLocalDescription(offer);
            },
            error => {
                alert('Error when creating an offer');
            }
        );
    }
};


NetworkManager.prototype.sendMessage = function(message) {
    if (this.connectedUser) {
        message.name = this.connectedUser;
    }
    
    this.socket.send(JSON.stringify(message));
};

NetworkManager.prototype.initState = function() {  
    // this.login(this.username);
};

NetworkManager.prototype.handleCandidate = function(candidate) {
    this.yourConn.addIceCandidate(new RTCIceCandidate(candidate));
};


NetworkManager.prototype.handleOffer = function(offer, name) {
    this.connectedUser = name;
    
    this.yourConn.setRemoteDescription(new RTCSessionDescription(offer));
    this.yourConn.createAnswer(
        answer => {
            this.yourConn.setLocalDescription(answer);
            this.sendMessage({
                type: 'answer',
                answer: answer,
            });
        },
        error => {
            alert('Error while creating an answer');
        }
    );
};


NetworkManager.prototype.handleAnswer = function(answer) {
    this.yourConn.setRemoteDescription(new RTCSessionDescription(answer));

};

NetworkManager.prototype.sendPayload = function (message) {
        this.dataChannel.send(message);
};

我将不胜感激。

4

1 回答 1

1

基于 RTP 的数据通道是一种非标准的仅限 Chrome 的扩展,并且已被弃用很长时间,以支持 SCTP 数据通道(请参阅https://www.chromestatus.com/feature/6485681910054912)。您的 Brave Browser 可能是一个非常旧的版本。

于 2021-10-27T14:51:58.920 回答