RTCDataChannel with Google Channel API

断了今生、忘了曾经 提交于 2020-06-29 10:02:40

问题


I'm trying to follow this example by Dan Ristic for RTCDataChannel browser p2p communication with Google's Channel API for signaling. It seems to be failing silently - I can't get the RTCDataChannel.onopen, RTCPeerConnection.onicecandidate, or RTCPeerConnection.ondatachannel events to fire.

Client 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()

Browser Consoles:

[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"}

回答1:


Firefox does not (and never will) support RtpDataChannels. It only supports the spec-compliant (and more advanced) SCTP datachannels. Removing the optional constraint should switch you over to them without requiring other changes.

It is somewhat odd that your SDP appears to have sctp lines in it. The google sample at http://googlechrome.github.io/webrtc/samples/web/content/datachannel/ does not when using rtp data channels.




回答2:


for starters, you are missing peerConnection.onDataChannel on the other side of the datachannel creation,

the code would be something like:

answerer.ondatachannel = function (event) {
        answererDataChannel = event.channel;
        answererDataChannel.binaryType = 'blob';
        setChannelEvents(answererDataChannel, 'answerer');
    };

...


function setChannelEvents(channel, channelNameForConsoleOutput) {
    channel.onmessage = function (event) {
        console.debug(channelNameForConsoleOutput, 'received a message:', event.data);
    };
    channel.onopen = function () {
        channel.send('first text message over SCTP data ports');
    };
}

for the complete code, you can check this link




回答3:


After many hours of trying, I was able to get my original example to work on Chrome 40.0.2214.93 and Opera 27.0 (latest as of this writing). why doesn't “onicecandidate” work? helped a lot. I had no luck getting it working on Firefox:

<html>
<head>
<script src="https://code.jquery.com/jquery-2.1.3.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 = "123456",
            weAreHost,
            optionalRtpDataChannels = {
                optional: [{RtpDataChannels: true}]
            },
            mediaConstraints = {
                    optional: [],
                    mandatory: {
                        OfferToReceiveAudio: false,
                        OfferToReceiveVideo: false
                    }
                };

        // 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("######### SUCCESS ######### RTCDataChannel Open!");
            }

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

        function error(e) {
            console.log(arguments);
            throw new Error(e);
        }

        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);          
            var self = this;

            if(message.type && message.type === "offer") {
                var offer = new RTCSessionDescription(message);
                this.peerConnection.setRemoteDescription(offer, function() {
                    self.peerConnection.createAnswer(function(answer) {
                        self.peerConnection.setLocalDescription(answer, function() {
                            self.send(answer);
                        }, error);
                    }, error, mediaConstraints);
                });
            } else if(message.type && message.type === "answer") {
                var answer = new RTCSessionDescription(message);
                this.peerConnection.setRemoteDescription(answer, function(){            
                }, error);
            } 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 = error;

                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);

                        // create offer and send to host
                        connection.createOffer(function (offer) {                           
                            connection.setLocalDescription(offer, function() {                          
                                signalingChannel.send(offer);
                            }, error);
                        }, error, mediaConstraints);
                    }           
                };
            }, "json").fail(error);
        };

        // 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>
    <br>
    <p id="print">PRINT SIGNALING STATE<p>
</body>
</html>

I've decided to go with PeerJS with a free Heroku server for signaling. In my opinion WebRTC is far too unstable right now to use directly. The following works on Chrome/FF/Opera:

<html>
<head>
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="http://cdn.peerjs.com/0.3.9/peer.js"></script>
<script>
    $(document).ready(function(){

        var SESSION_ID = "1234";

        // Create a button on the page so only one client initiates the connection.         
        $("#i-am-host").click(function() {

            var host = new Peer('host'+SESSION_ID, {host: 'my-peerjs.herokuapp.com', port: 80});
            host.on("connection", function(conn) {
                conn.on('data', function(data) {
                    console.log(data);
                });
            });

        });
        $("#i-am-client").click(function() {

            var client = new Peer('client'+SESSION_ID, {host: 'my-peerjs.herokuapp.com', port: 80});
            var conn = client.connect('host'+SESSION_ID);
            conn.on("open", function(){
                conn.send("SUCCESS!!");
            });     
        });
    });
</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>


来源:https://stackoverflow.com/questions/28227405/rtcdatachannel-with-google-channel-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!