带有 Google Channel API 的 RTCDataChannel

Lig*_*ard 0 javascript google-app-engine channel-api webrtc rtcdatachannel

我正在尝试按照Dan Ristic 的这个示例进行RTCDataChannel浏览器 p2p 通信,与 Google 的Channel API进行信号通信。这似乎是默默的失败-我不能得到的RTCDataChannel.onopenRTCPeerConnection.onicecandidate或者RTCPeerConnection.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>
Run Code Online (Sandbox Code Playgroud)

应用引擎 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()
Run Code Online (Sandbox Code Playgroud)

浏览器控制台:

[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"}
Run Code Online (Sandbox Code Playgroud)

Phi*_*cke 5

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

您的 SDP 中似乎有 sctp 行,这有点奇怪。http://googlechrome.github.io/webrtc/samples/web/content/datachannel/ 上的 google 示例在使用 rtp 数据通道时不会。