编辑:我写了详细的教程,解释了如何构建一个包括信号服务器的简单Videochat应用程序:

Tutorial: Create your own Videochat-Application with HTML and JavaScript

请告诉我您是否对您有帮助并且可以理解。谢谢!

我试图通过WebRTC和Websocket(nodejs服务器)使流工作。据我所知,通过SDP进行的握手有效,并且建立了Peerconnection。
问题是-远程视频无法播放。 src-Attribute获取Blob并设置了自动播放功能,但不会播放。
也许我对ICE候选者做错了(它们用于媒体流,对吗?)。
有什么方法可以检查PeerConnection的设置是否正确?

编辑:也许我应该解释代码如何工作

  • 在网站加载时,建立了与websocket-server的连接,使用Google STUN服务器创建了PeerConnection,并收集了视频和音频流并将其添加到PeerConnection
  • 当一个用户单击“创建要约”按钮时,包含其 session 描述(SDP)的消息将发送到服务器(客户端func sendOffer()),该消息将广播给另一用户
  • 另一个用户收到消息并保存他收到的SDP
  • 如果用户单击“接受要约”,则将SDP添加到RemoteDescription(func createAnswer())中,然后它将应答消息(包含应答用户的SDP)发送到要约用户
  • 在提供用户侧执行func offerAccepted(),它将另一个用户的SDP添加到他的RemoteDesription中。

  • 我不知道应在什么时候使用icecandidate-handlers,但是我认为它们应该可以工作,因为我两面都有日志。

    这是我的代码(这只是用于测试,因此即使有一个名为broadcast的功能,也意味着一次只能在同一网站上有2个用户):

    index.html的标记:
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <style>
                #acceptOffer  {
                    display: none;
                }
            </style>
        </head>
        <body>
            <h2>Chat</h2>
            <div>
                <textarea class="output" name="" id="" cols="30" rows="10"></textarea>
            </div>
            <button id="createOffer">create Offer</button>
            <button id="acceptOffer">accept Offer</button>
    
            <h2>My Stream</h2>
            <video id="myStream" autoplay src=""></video>
            <h2>Remote Stream</h2>
            <video id="remoteStream" autoplay src=""></video>
    
            <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
            <script src="websocketClient.js"></script>
    </body>
    </html>
    

    这是服务器代码:
    "use strict";
    
    var webSocketsServerPort = 61122;
    
    var webSocketServer = require('websocket').server,
    http = require('http'),
    clients = [];
    
    
    var server = http.createServer(function(request, response) {
        // Not important for us. We're writing WebSocket server, not HTTP server
    });
    server.listen(webSocketsServerPort, function() {
        console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
    });
    
    var wsServer = new webSocketServer({
        httpServer: server
    });
    
    wsServer.on('request', function(request) {
        console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
    
        var connection = request.accept(null, request.origin),
        index = clients.push(connection) - 1,
        userName=false;
        console.log((new Date()) + ' Connection accepted from '+connection.remoteAddress);
    
        // user sent some message
        connection.on('message', function(message) {
            var json = JSON.parse(message.utf8Data);
    
            console.log(json.type);
            switch (json.type) {
                case 'broadcast':
                    broadcast(json);
                break;
    
                case 'emit':
                    emit({type:'offer', data:json.data.data});
                break;
    
                case 'client':
                    respondToClient(json, clients[index]);
                break;
    
                default:
                    respondToClient({type:'error', data:'Sorry, i dont understand that.'}, clients[index]);
                break;
    
            }
    
        });
    
        connection.on('close', function(connection) {
            clients.splice(index,1);
            console.log((new Date()) + " Peer " + connection.remoteAddress + " disconnected.");
            broadcast({type:'text', data: userName+' has left the channel.'});
        });
    
        var respondToClient = function(data, client){
            client.sendUTF(JSON.stringify( data ));
        };
    
        var broadcast = function(data){
            for(var i = 0; i < clients.length; i++ ) {
                if(i != index ) {
                    clients[i].sendUTF(JSON.stringify( data ));
                }
            }
        };
        var emit = function(){
            // TBD
        };
    });
    

    ,这里是客户端代码:
    $(function () {
        "use strict";
    
        /**
        * Websocket Stuff
        **/
    
        window.WebSocket = window.WebSocket || window.MozWebSocket;
    
        // open connection
        var connection = new WebSocket('ws://url-to-node-server:61122'),
        myName = false,
        mySDP = false,
        otherSDP = false;
    
        connection.onopen = function () {
            console.log("connection to WebSocketServer successfull");
        };
    
        connection.onerror = function (error) {
            console.log("WebSocket connection error");
        };
    
        connection.onmessage = function (message) {
            try {
                var json = JSON.parse(message.data),
                output = document.getElementsByClassName('output')[0];
    
                switch(json.callback) {
                    case 'offer':
                        otherSDP = json.data;
                        document.getElementById('acceptOffer').style.display = 'block';
                    break;
    
                    case 'setIceCandidate':
                    console.log('ICE CANDITATE ADDED');
                        peerConnection.addIceCandidate(json.data);
                    break;
    
                    case 'text':
                        var text = output.value;
                        output.value = json.data+'\n'+output.value;
                    break;
    
                    case 'answer':
                        otherSDP = json.data;
                        offerAccepted();
                    break;
    
                }
    
            } catch (e) {
                console.log('This doesn\'t look like a valid JSON or something else went wrong.');
                return;
            }
        };
        /**
        * P2P Stuff
        **/
        navigator.getMedia = ( navigator.getUserMedia ||
           navigator.webkitGetUserMedia ||
           navigator.mozGetUserMedia ||
           navigator.msGetUserMedia);
    
        // create Connection
        var peerConnection = new webkitRTCPeerConnection(
            { "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] }
        );
    
    
        var remoteVideo = document.getElementById('remoteStream'),
            myVideo = document.getElementById('myStream'),
    
            // get local video-Stream and add to Peerconnection
            stream = navigator.webkitGetUserMedia({ audio: false, video: true }, function (stream) {
                myVideo.src = webkitURL.createObjectURL(stream);
                console.log(stream);
                peerConnection.addStream(stream);
        });
    
        // executes if other side adds stream
        peerConnection.onaddstream = function(e){
            console.log("stream added");
            if (!e)
            {
                return;
            }
            remoteVideo.setAttribute("src",URL.createObjectURL(e.stream));
            console.log(e.stream);
        };
    
        // executes if my icecandidate is received, then send it to other side
        peerConnection.onicecandidate  = function(candidate){
            console.log('ICE CANDITATE RECEIVED');
            var json = JSON.stringify( { type: 'broadcast', callback:'setIceCandidate', data:candidate});
            connection.send(json);
        };
    
        // send offer via Websocket
        var sendOffer = function(){
            peerConnection.createOffer(function (sessionDescription) {
                peerConnection.setLocalDescription(sessionDescription);
                // POST-Offer-SDP-For-Other-Peer(sessionDescription.sdp, sessionDescription.type);
                var json = JSON.stringify( { type: 'broadcast', callback:'offer',data:{sdp:sessionDescription.sdp,type:'offer'}});
                connection.send(json);
    
            }, null, { 'mandatory': { 'OfferToReceiveAudio': true, 'OfferToReceiveVideo': true } });
        };
    
        // executes if offer is received and has been accepted
        var createAnswer = function(){
    
            peerConnection.setRemoteDescription(new RTCSessionDescription(otherSDP));
    
            peerConnection.createAnswer(function (sessionDescription) {
                peerConnection.setLocalDescription(sessionDescription);
                // POST-answer-SDP-back-to-Offerer(sessionDescription.sdp, sessionDescription.type);
                var json = JSON.stringify( { type: 'broadcast', callback:'answer',data:{sdp:sessionDescription.sdp,type:'answer'}});
                connection.send(json);
            }, null, { 'mandatory': { 'OfferToReceiveAudio': true, 'OfferToReceiveVideo': true } });
    
        };
    
        // executes if other side accepted my offer
        var offerAccepted = function(){
            peerConnection.setRemoteDescription(new RTCSessionDescription(otherSDP));
            console.log('it should work now');
        };
    
        $('#acceptOffer').on('click',function(){
            createAnswer();
        });
    
        $('#createOffer').on('click',function(){
            sendOffer();
        });
    });
    

    我还读到,在发送任何要约之前必须收集本地媒体流。创建PeerConnection时是否必须添加它?
    即像这样的东西:
    // create Connection
    var peerConnection = new webkitRTCPeerConnection(
        {
            "iceServers": [{ "url": "stun:stun.l.google.com:19302" }],
            "mediaStream": stream // attach media stream here?
        }
    );
    

    在此先感谢,感谢您的帮助!

    编辑2:
    我现在有点远。似乎由于“指定了无效或非法的字符串。”而无法添加远程的冰候选对象(客户端代码中的切换用例setIceCandidate)。 json.data.candidate-object看起来像这样:
    candidate: "a=candidate:1663431597 2 udp 1845501695 141.84.69.86 57538 typ srflx raddr 10.150.16.92 rport 57538 generation 0
    ↵"
    sdpMLineIndex: 1
    sdpMid: "video"
    

    我试图创建这样的新候选人
     var remoteCandidate = new RTCIceCandidate(json.data.candidate);
     peerConnection.addIceCandidate(remoteCandidate);
    

    但我仍然有语法错误

    最佳答案

    我最近在同一件事上遇到了麻烦,从其他人那里得到的最佳建议是创建程序的一个版本,在该版本中,我从一个“对等”中手动复制并粘贴了SDP和ICE信息(即,浏览器标签),反之亦然。

    通过这样做,我意识到了几件事:

  • 在尝试创建任何要约/答案之前,必须先调用对等连接对象的addStream方法。
  • 调用createOffer或createAnswer方法后,将立即生成该客户端的ICE候选对象。但是,一旦将ICE信息发送给其他对等方,您就无法真正设置ICE信息,直到设置了远程描述之后(使用收到的要约/答案)。
  • 确保您正确编码了有关将在网络上发送的所有信息。在JS中,这意味着您应该对要在网络上发送的所有数据使用encodeURIComponent函数。我有一个问题,其中SDP和ICE信息有时会被正确设置,而有时却没有被正确设置。这与我没有对数据进行URI编码这一事实有关,这导致数据中的任何加号都变成空格,从而使所有内容混乱。

  • 无论如何,就像我说的那样,我建议创建程序的一个版本,在该版本中,您有一堆文本区域可将所有数据吐出到屏幕上,然后还有其他文本区域可将复制的数据粘贴到其中以进行设置另一个同伴。
    这样做确实澄清了整个WebRTC流程,说实话,在我所见过的任何文档/教程中都没有很好地说明这一点。

    祝您好运,请告诉我是否可以提供帮助。

    关于stream - 远程VideoStream无法与WebRTC一起使用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17391750/

    10-09 14:03