我试图遵循this example by Dan Ristic进行RTCDataChannel浏览器p2p通信,并与Google的Channel API进行信号传输。它似乎无声地失败了-我无法触发RTCDataChannel.onopenRTCPeerConnection.onicecandidateRTCPeerConnection.ondatachannel事件。

客户端JS / HTML:

<html>
<head>
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="/_ah/channel/jsapi"></script>
<script>
    $(document).ready(function(){

        var IS_CHROME = !!window.webkitRTCPeerConnection,
            RTCPeerConnection = window.webkitRTCPeerConnection || mozRTCPeerConnection,
            RTCIceCandidate = window.RTCIceCandidate || RTCSessionDescription,
            RTCSessionDescription = window.RTCSessionDescription || mozRTCSessionDescription,
            SESSION_ID = "12345",
            weAreHost,
            optionalRtpDataChannels = {
                optional: [{RtpDataChannels: true}]
            },
            mediaConstraints = {
                    optional: [],
                    mandatory: {
                        OfferToReceiveAudio: false, // Hmm!!
                        OfferToReceiveVideo: false // Hmm!!
                    }
                };

        // Signaling Channel Object
        function SignalingChannel(peerConnection) {
          // Setup the signaling channel here
          this.peerConnection = peerConnection;
        }

        function setChannelEvents(dataChannel) {
            dataChannel.onmessage = function (event) {
                console.log("I got data channel message: ", event.data);
            }

            dataChannel.onopen = function (event) {
                dataChannel.send("RTCDataChannel Open!");
            }

            dataChannel.error = function(event) {
                console.log("data channel error:", event)
            }
        }

        SignalingChannel.prototype.send = function(message) {
            console.log("signal send:", message);
            var url = "/api/signal/send/";
            url += weAreHost ? "client"+SESSION_ID : "host"+SESSION_ID;
            $.ajax({
                type: "PUT",
                url: url,
                contentType: "application/json",
                data: JSON.stringify(message)
            });
        };

        SignalingChannel.prototype.onmessage = function(message) {
          console.log("signal receive:", message);
          // If we get a sdp we have to sign and return it
          if (message.sdp != null) {
            var that = this;
            this.peerConnection.setRemoteDescription(new RTCSessionDescription(message), function () {
              that.peerConnection.createAnswer(function (description) {
                that.send(description);
              }, null, mediaConstraints);
            });
          } else {
            this.peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate));
          }
        };

        function initiateConnection(input) {
            weAreHost = input;

            // setup signaling mechanism with Google Channel API
            var url = "/api/signal/init/";
            url += weAreHost ? "host"+SESSION_ID : "client"+SESSION_ID;
            $.post(url, "", function(response){

                var channel = new goog.appengine.Channel(response.token);
                var socket = channel.open();
                socket.onerror = function(){console.log(arguments);};
                socket.onclose = function(){console.log(arguments);};

                var closeSocket = function() {
                    if(socket) return socket.close();
                    else return "google socket does not exist"
                }
                $(window).unload(closeSocket);
                window.onbeforeunload = closeSocket;

                socket.onopen = function() {
                    console.log("google socket opened");

                    // Create a peer connection object
                    var connection = new RTCPeerConnection({
                      iceServers: [
                        { 'url': (IS_CHROME ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121') }
                      ]
                    }, optionalRtpDataChannels);

                    // Initiate a signaling channel between two users
                    var signalingChannel = new SignalingChannel(connection);

                    connection.onicecandidate = function (event) {
                        console.log("onicecandidate:", event);
                        if (!event || !event.candidate) return;
                        signalingChannel.send({candidate:event.candidate});
                    };

                    // Effectively set SignalingChannel as google channel socket inbound event handler
                    socket.onmessage = function(input) {
                        console.log("received from google:", input);
                        var message = $.parseJSON(input.data);
                        signalingChannel.onmessage(message);
                    };

                    // Only one client should initiate the connection, the other client should wait.
                    if(weAreHost) {
                        connection.ondatachannel = function(event) {
                            setChannelEvents(event.channel);
                        }
                    } else {
                        // Create client RTCDataChannel
                        var clientChannel = connection.createDataChannel("my_label", {reliable: false});
                        setChannelEvents(clientChannel);

                        connection.createOffer(function (description) {
                            signalingChannel.send(description);
                        }, function(error){
                            console.log(error);
                        }, mediaConstraints);
                    }
                };
            }, "json");
        };

        // Create a button on the page so only one client initiates the connection.
        $("#i-am-host").click(function() {
            initiateConnection(true);
        });
        $("#i-am-client").click(function() {
            initiateConnection(false);
        });
    });
</script>
</head>
<body>
    <p id="i-am-host" style="background-color: green;">I AM HOST</p>
    <p id="i-am-client" style="background-color: blue;">I AM CLIENT</p>
</body>
</html>


App Engine Python:

from google.appengine.api import channel

from django.shortcuts import render
from django.http import HttpResponse

import json

def init(request, browser_id):

    token = channel.create_channel(browser_id);

    return HttpResponse(json.dumps({'token':token}))

def send(request, browser_id):

    channel.send_message(browser_id, request.body)

    return HttpResponse()


浏览器控制台:

[HOST]
received from google:
Object {data: "{"sdp":"v=0\r\no=- 6804947085651458452 2 IN IP4 12…5000 webrtc-datachannel 1024\r\n","type":"offer"}"}
test.html:34
signal receive:
Object {sdp: "v=0
↵o=- 6804947085651458452 2 IN IP4 127.0.0.1
↵s…id:data
↵a=sctpmap:5000 webrtc-datachannel 1024
↵", type: "offer"}
test.html:22
signal send:
RTCSessionDescription {sdp: "v=0
↵o=- 600524556593905006 2 IN IP4 127.0.0.1
↵s=…id:data
↵a=sctpmap:5000 webrtc-datachannel 1024
↵", type: "answer"}

[CLIENT]
 signal send:
RTCSessionDescription {sdp: "v=0
↵o=- 6804947085651458452 2 IN IP4 127.0.0.1
↵s…id:data
↵a=sctpmap:5000 webrtc-datachannel 1024
↵", type: "offer"}
test.html:82
received from google:
Object {data: "{"sdp":"v=0\r\no=- 600524556593905006 2 IN IP4 127…000 webrtc-datachannel 1024\r\n","type":"answer"}"}
test.html:34
signal receive: Object {sdp: "v=0
↵o=- 600524556593905006 2 IN IP4 127.0.0.1
↵s=…id:data
↵a=sctpmap:5000 webrtc-datachannel 1024
↵", type: "answer"}

最佳答案

Firefox不(永远不会)支持RtpDataChannels。它仅支持符合规范(和更高级)的SCTP数据通道。删除可选约束将使您切换到它们,而无需其他更改。

您的SDP中似乎包含sctp行有点奇怪。 http://googlechrome.github.io/webrtc/samples/web/content/datachannel/处的google示例
使用rtp数据通道时不起作用。

09-16 23:39