我在哪里可以找到简单的webrtc视频聊天1to1样本?

时间:2014-04-14 21:59:00

标签: webrtc

我搜索过很多网页,甚至还有“开始使用webrtc”这本书,其中的样本会出错。 但是我无法找到工作1to1视频聊天的任何工作示例。

更好的是使用nodeJS传输。

我认为我发现的样本不遵守这些规则:

  1. 在发送SIP(提供/回答SDP)
  2. 之前,对等方必须与本地流媒体视频一起出现
  3. 对于'回答者';在对等体生成之前不要添加ICE候选者 '回答SDP'
  4. 一旦远程媒体开始流媒体停止添加ICE候选人
  5. 在获得'Offer SDP'
  6. 之前,永远不要为回答者创建对等连接

    我找到了许多个人API或个人aproaches,但找不到SIMPLEST方法来创建单个1to1视频聊天。

    祝你好运

    来自webrtc book的样本(确实有效)给出了错误:

    Mon Apr 14 2014 23:19:26 GMT+0200 (Paris, Madrid invalid signal:
    {"type":"new_ice_candidate","candidate":{"candidate":"candidate:1 2 UDP 16924671
    98 90.7.245.247 63704 typ srflx raddr 192.168.1.15 rport 63704","sdpMid":"","sdp
    MLineIndex":0}}
    

    客户方:

    <!DOCTYPE html>
    <html>
    <head>
    <script>
    
    var webrtc_capable = true;
    var rtc_peer_connection = null;
    var rtc_session_description = null;
    var get_user_media = null;
    var connect_stream_to_src = null;
    var stun_server = "stun01.sipphone.com";
    
    if (navigator.getUserMedia) { // WebRTC 1.0 standard compliant browser
      rtc_peer_connection = RTCPeerConnection;
      rtc_session_description = RTCSessionDescription;
      get_user_media = navigator.getUserMedia.bind(navigator);
      connect_stream_to_src = function(media_stream, media_element) {
        // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21606
        media_element.srcObject = media_stream;
        media_element.play();
      };
    } else if (navigator.mozGetUserMedia) { // early firefox webrtc implementation
      rtc_peer_connection = mozRTCPeerConnection;
      rtc_session_description = mozRTCSessionDescription;
      get_user_media = navigator.mozGetUserMedia.bind(navigator);
      connect_stream_to_src = function(media_stream, media_element) {
        media_element.mozSrcObject = media_stream;
        media_element.play();
      };
      stun_server = "74.125.31.127:19302";
    } else if (navigator.webkitGetUserMedia) { // early webkit webrtc implementation
      rtc_peer_connection = webkitRTCPeerConnection;
      rtc_session_description = RTCSessionDescription;
      get_user_media = navigator.webkitGetUserMedia.bind(navigator);
      connect_stream_to_src = function(media_stream, media_element) {
        media_element.src = webkitURL.createObjectURL(media_stream);
      };
    } else {
      alert("This browser does not support WebRTC - visit WebRTC.org for more info");
      webrtc_capable = false;
    }
    </script>
    <script>
    
    var call_token; // unique token for this call
    var signaling_server; // signaling server for this call
    var peer_connection; // peer connection object
    
    function start() {
      // create the WebRTC peer connection object
      peer_connection = new rtc_peer_connection({ // RTCPeerConnection configuration 
        "iceServers": [ // information about ice servers
          { "url": "stun:"+stun_server }, // stun server info
        ]
      });
    
      // generic handler that sends any ice candidates to the other peer
      peer_connection.onicecandidate = function (ice_event) {
        if (ice_event.candidate) {
          signaling_server.send(
            JSON.stringify({
              type: "new_ice_candidate",
              candidate: ice_event.candidate ,
            })
          );
        }
      };
    
      // display remote video streams when they arrive using local <video> MediaElement
      peer_connection.onaddstream = function (event) {
        connect_stream_to_src(event.stream, document.getElementById("remote_video"));
        // hide placeholder and show remote video
        document.getElementById("loading_state").style.display = "none";
        document.getElementById("open_call_state").style.display = "block";
      };
    
      // setup stream from the local camera 
      setup_video();
    
      // setup generic connection to the signaling server using the WebSocket API
      signaling_server = new WebSocket("ws://192.168.1.15:1234");
    
      if (document.location.hash === "" || document.location.hash === undefined) { // you are the Caller
    
        // create the unique token for this call 
        var token = Date.now()+"-"+Math.round(Math.random()*10000);
        var token = Math.round(Math.random()*10000);
        call_token = "#"+token;
        // set location.hash to the unique token for this call
        document.location.hash = token;
        signaling_server.onopen = function() {
          // setup caller signal handler
          signaling_server.onmessage = caller_signal_handler;
    
          // tell the signaling server you have joined the call 
          signaling_server.send(
            JSON.stringify({ 
              token:call_token,
              type:"join",
            })
          );
        }
    
        document.title = "You are the Caller";
        document.getElementById("loading_state").innerHTML = "Ready for a call...ask your friend to visit:<br/><br/>"+document.location;
    
      } else { // you have a hash fragment so you must be the Callee 
    
        // get the unique token for this call from location.hash
        call_token = document.location.hash;
    
        signaling_server.onopen = function() {
          // setup caller signal handler
          signaling_server.onmessage = callee_signal_handler;
    
          // tell the signaling server you have joined the call 
          signaling_server.send(
            JSON.stringify({ 
              token:call_token,
              type:"join",
            })
          );
    
          // let the caller know you have arrived so they can start the call
          signaling_server.send(
            JSON.stringify({ 
              token:call_token,
              type:"callee_arrived",
            })
          );
        }
    
        document.title = "You are the Callee";
        document.getElementById("loading_state").innerHTML = "One moment please...connecting your call...";
      }
    }
    
    /* functions used above are defined below */
    
    // handler to process new descriptions
    function new_description_created(description) {
      peer_connection.setLocalDescription(
        description, 
        function () {
          signaling_server.send(
            JSON.stringify({
              token:call_token,
              type:"new_description",
              sdp:description 
            })
          );
        }, 
        log_error
      );
    }
    
    // handle signals as a caller
    function caller_signal_handler(event) {
      var signal = JSON.parse(event.data);
      if (signal.type === "callee_arrived") {
        peer_connection.createOffer(
          new_description_created, 
          log_error
        );
      } else if (signal.type === "new_ice_candidate") {
        peer_connection.addIceCandidate(
          new RTCIceCandidate(signal.candidate)
        );
      } else if (signal.type === "new_description") {
        peer_connection.setRemoteDescription(
          new rtc_session_description(signal.sdp), 
          function () {
            if (peer_connection.remoteDescription.type == "answer") {
              // extend with your own custom answer handling here
            }
          },
          log_error
        );
      } else {
        // extend with your own signal types here
      }
    }
    
    // handle signals as a callee
    function callee_signal_handler(event) {
      var signal = JSON.parse(event.data);
      if (signal.type === "new_ice_candidate") {
        peer_connection.addIceCandidate(
          new RTCIceCandidate(signal.candidate)
        );
      } else if (signal.type === "new_description") {
        peer_connection.setRemoteDescription(
          new rtc_session_description(signal.sdp), 
          function () {
            if (peer_connection.remoteDescription.type == "offer") {
              peer_connection.createAnswer(new_description_created, log_error);
            }
          },
          log_error
        );
      } else {
        // extend with your own signal types here
      }
    }
    
    // setup stream from the local camera 
    function setup_video() {
      get_user_media(
        { 
          "audio": true, // request access to local microphone
          "video": true  // request access to local camera
        }, 
        function (local_stream) { // success callback
          // display preview from the local camera & microphone using local <video> MediaElement
          connect_stream_to_src(local_stream, document.getElementById("local_video"));
          // add local camera stream to peer_connection ready to be sent to the remote peer
          peer_connection.addStream(local_stream);
        },
        log_error
      );
    }
    
    // generic error handler
    function log_error(error) {
      console.log(error);
    }
    
    </script>
    <style>
    html, body {
      padding: 0px;
      margin: 0px;
      font-family: "Arial","Helvetica",sans-serif;
    }
    #loading_state {
      position: absolute;
      top: 45%;
      left: 0px;
      width: 100%;
      font-size: 20px;
      text-align: center;
    }
    #open_call_state {
      display: none;
    }
    #local_video {
      position: absolute;
      top: 10px;
      left: 10px;
      width: 160px;
      height: 120px;
      background: #333333;
    }
    #remote_video {
      position: absolute;
      top: 0px;
      left: 0px;
      width: 1024px;
      height: 768px;
      background: #999999;
    }
    </style>
    </head>
    <body onload="start()">
      <div id="loading_state">
        loading...
      </div>
      <div id="open_call_state">
        <video id="remote_video"></video>
        <video id="local_video"></video>
      </div>
    </body>
    </html>
    

    服务器端(nodejs)

    // useful libs
    var http = require("http");
    var fs = require("fs");
    var websocket = require("websocket").server;
    
    // general variables
    var port = 1234;
    var webrtc_clients = [];
    var webrtc_discussions = {};
    
    // web server functions
    var http_server = http.createServer(function(request, response) {
      var matches = undefined;
      if (matches = request.url.match("^/images/(.*)")) {
        var path = process.cwd()+"/images/"+matches[1];
        fs.readFile(path, function(error, data) {
          if (error) {
            log_error(error);
          } else {
            response.end(data);
          }
        });
      } else {
        response.end(page);
      }
    });
    http_server.listen(port, function() {
      log_comment("server listening (port "+port+")");
    });
    var page = undefined;
    fs.readFile("basic_video_call.html", function(error, data) {
      if (error) {
        log_error(error);
      } else {
        page = data;
      }
    });
    
    // web socket functions
    var websocket_server = new websocket({
      httpServer: http_server
    });
    websocket_server.on("request", function(request) {
      log_comment("new request ("+request.origin+")");
    
      var connection = request.accept(null, request.origin);
      log_comment("new connection ("+connection.remoteAddress+")");
    
      webrtc_clients.push(connection);
      connection.id = webrtc_clients.length-1;
    
      connection.on("message", function(message) {
        if (message.type === "utf8") {
          log_comment("got message "+message.utf8Data);
    
          var signal = undefined;
          try { signal = JSON.parse(message.utf8Data); } catch(e) { };
          if (signal) {
            if (signal.type === "join" && signal.token !== undefined) {
              try {
                if (webrtc_discussions[signal.token] === undefined) {
                  webrtc_discussions[signal.token] = {};
                }
              } catch(e) { };
              try {
                webrtc_discussions[signal.token][connection.id] = true;
              } catch(e) { };
            } else if (signal.token !== undefined) {
              try {
                Object.keys(webrtc_discussions[signal.token]).forEach(function(id) {
                  if (id != connection.id) {
                    webrtc_clients[id].send(message.utf8Data, log_error);
                  }
                });
              } catch(e) { };
            } else {
              log_comment("signal.type="+signal.type+" *invalid signal: "+message.utf8Data);
            }
          } else {
            log_comment("**invalid signal: "+message.utf8Data);
          }
        }
      });
    
      connection.on("close", function(connection) {
        log_comment("connection closed ("+connection.remoteAddress+")");    
        Object.keys(webrtc_discussions).forEach(function(token) {
          Object.keys(webrtc_discussions[token]).forEach(function(id) {
            if (id === connection.id) {
              delete webrtc_discussions[token][id];
            }
          });
        });
      });
    });
    
    // utility functions
    function log_error(error) {
      if (error !== "Connection closed" && error !== undefined) {
        log_comment("ERROR: "+error);
      }
    }
    function log_comment(comment) {
      console.log((new Date())+" "+comment);
    }
    

1 个答案:

答案 0 :(得分:1)

您可以参考codelab上的示例。尝试运行第7步:将所有内容放在一起:RTCPeerConnection + RTCDataChannel +信令。 Node.js是必需的。您还可以阅读文章How to Implement a Real-time Commnication Application with WebRTC,该文章基于codelab分享了一些学习经验。