WebRTC-Codelab小白初体验(下)

开篇导读,这篇是从 https://codelabs.developers.google.com/codelabs/webrtc-web 代码实验室搬运过来的,属于WebRTC理论入门,毕竟WebRTC从名字是来说,人家建立的出发点就是在Web上的,所以我们从正统渠道入手,往后再学习基于操作系统移植的SDK。

7、设置信令服务以交换消息

您将学到什么?

  • 使用npm安装package.json中指定的项目依赖项。Use npm to install project dependencies as specified in package.json
  • 运行Node.js服务器,并使用node-static来提供静态文件。Run a Node.js server and use node-static to serve static files.
  • 使用Socket.IO在Node.js上设置消息传递服务。Set up a messaging service on Node.js using Socket.IO.
  • 使用它来创建“房间”并交换消息。Use that to create ‘rooms' and exchange messages.

该步骤的完整版本位于step-04文件夹中。

相关概念

为了建立和维护WebRTC的呼叫调用,WebRTC客户端(端对端对等方)需要交换元数据:

  • (网络)候选人的信息。Candidate (network) information.
  • Offer和answer的消息,提供有关媒体的信息,例如分辨率和编解码器。Offer and answer messages providing information about media, such as resolution and codecs.

换句话说,在可以进行音频,视频或数据的对等流传输之前,需要交换元数据。该过程称为信令。
在前面的步骤中,发送方和接收方RTCPeerConnection对象位于同一页上,因此“信号发送”仅是在对象之间传递元数据的问题。
在实际的应用程序中,发送方和接收方RTCPeerConnections在不同设备上的网页中运行,所以需要一种使它们通信元数据的方法。
为此,我们使用信令服务器:可以在WebRTC客户端(端对端)之间传递消息的服务器。实际消息为纯文本:字符串化的JavaScript对象。

前置条件:安装Node.js

为了运行此代码实验室的后续步骤(文件夹步骤04至步骤06),您将需要使用Node.js在本地主机上运行服务器。(windows安装npm教程 )

一旦安装完成后,您将能够导入下一步所需的依赖项(运行npm install)以及运行小型的本地服务器来执行实验室的代码(运行节点index.js)。

关于应用程序

WebRTC使用客户端JavaScript API,但对于实际使用,还需要(消息传递)信令服务器——STUN和TURN服务器。你可以在这里 here 找到更多。

在这一步中,您将使用Socket.IO Node.js模块和JavaScript库进行消息传递,从而构建一个简单的Node.js信令服务器。使用Node.js和Socket.IO的经验将是有用的,但不是至关重要的。消息传递组件非常简单。

在此示例中,服务器(Node.js应用程序)在index.js中实现,在其上运行的客户端(网络应用程序)在index.html中实现。此步骤中的Node.js应用程序有两个任务。

首先,它充当消息的中转服务器:

socket.on('message', function (message) {
  log('Got message: ', message);
  socket.broadcast.emit('message', message);
});

其次,它管理WebRTC视频聊天“房间”:

if (numClients === 0) {
  socket.join(room);
  socket.emit('created', room, socket.id);
} else if (numClients === 1) {
  socket.join(room);
  socket.emit('joined', room, socket.id);
  io.sockets.in(room).emit('ready');
} else { // max two clients
  socket.emit('full', room);
}

我们这个简单的WebRTC应用程序将允许最多两个对等方共享一个房间。

HTML & JavaScript

更新index.html,使其如下所示:

<!DOCTYPE html>
<html>

<head>
  <title>Realtime communication with WebRTC</title>
  <link rel="stylesheet" href="css/main.css" />
</head>

<body>
  <h1>Realtime communication with WebRTC</h1>

  <script src="/socket.io/socket.io.js"></script>
  <script src="js/main.js"></script>
 
</body>

</html>

在此步骤中,您将不会在页面上看到任何内容,所有日志记录都已完成到浏览器控制台。

将js / main.js替换为以下内容:

'use strict';

var isInitiator;

window.room = prompt("Enter room name:");

var socket = io.connect();

if (room !== "") {
  console.log('Message from client: Asking to join room ' + room);
  socket.emit('create or join', room);
}

socket.on('created', function(room, clientId) {
  isInitiator = true;
});
socket.on('full', function(room) {
  console.log('Message from client: Room ' + room + ' is full :^(');
});
socket.on('ipaddr', function(ipaddr) {
  console.log('Message from client: Server IP address is ' + ipaddr);
});
socket.on('joined', function(room, clientId) {
  isInitiator = false;
});
socket.on('log', function(array) {
  console.log.apply(console, array);
});

设置Socket.IO以在Node.js上运行。在HTML文件中,您可能已经看到您正在使用Socket.IO文件:

<script src="/socket.io/socket.io.js"></script>

在工作目录的顶层,创建一个名为package.json的文件,这是一个应用清单,它告诉Node Package Manager(npm)要安装哪些项目依赖项。其中包含以下内容:

{
  "name": "webrtc-codelab",
  "version": "0.0.1",
  "description": "WebRTC codelab",
  "dependencies": {
    "node-static": "^0.7.10",
    "socket.io": "^1.2.0"
  }
}

要安装依赖项(例如/socket.io/socket.io.js),请从命令行终端在工作目录中运行以下命令:npm install。如您所愿,npm会安装package.json中定义的依赖项。

在工作目录的顶层(不在js目录中)创建一个新文件index.js,并添加以下代码:

'use strict';
var os = require('os');
var nodeStatic = require('node-static');
var http = require('http');
var socketIO = require('socket.io');

var fileServer = new(nodeStatic.Server)();
var app = http.createServer(function(req, res) {
  fileServer.serve(req, res);
}).listen(8080);

var io = socketIO.listen(app);
io.sockets.on('connection', function(socket) {

  // convenience function to log server messages on the client
  function log() {
    var array = ['Message from server:'];
    array.push.apply(array, arguments);
    socket.emit('log', array);
  }

  socket.on('message', function(message) {
    log('Client said: ', message);
    // for a real app, would be room-only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', function(room) {
    log('Received request to create or join room ' + room);

    var clientsInRoom = io.sockets.adapter.rooms[room];
    var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;

    log('Room ' + room + ' now has ' + numClients + ' client(s)');

    if (numClients === 0) {
      socket.join(room);
      log('Client ID ' + socket.id + ' created room ' + room);
      socket.emit('created', room, socket.id);

    } else if (numClients === 1) {
      log('Client ID ' + socket.id + ' joined room ' + room);
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room, socket.id);
      io.sockets.in(room).emit('ready');
    } else { // max two clients
      socket.emit('full', room);
    }
  });

  socket.on('ipaddr', function() {
    var ifaces = os.networkInterfaces();
    for (var dev in ifaces) {
      ifaces[dev].forEach(function(details) {
        if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
          socket.emit('ipaddr', details.address);
        }
      });
    }
  });

});

从命令行终端,在工作目录中运行以下命令:node index.js。在浏览器中,打开localhost:8080。
每次打开此URL时,系统都会提示您输入房间名称。要加入同一房间,请每次选择相同的房间名称,例如“ foo”。
打开一个新的标签页,然后再次打开localhost:8080。选择相同的房间名称。
在第三个标签或窗口中打开localhost:8080。再次选择相同的房间名称。
检查每个选项卡中的控制台:您应该在上面的JavaScript中看到日志记录。

8、结合对等连接和信令

您将学到什么?

  • 使用在Node.js上运行的Socket.IO运行WebRTC信令服务。Run a WebRTC signaling service using Socket.IO running on Node.js
  • 使用该服务在对等方之间交换WebRTC元数据。Use that service to exchange WebRTC metadata between peers.

此步骤的完整版本位于step-05文件夹中。

替换HTML和JavaScript

将index.html的内容替换为以下内容:

<!DOCTYPE html>
<html>

<head>
  <title>Realtime communication with WebRTC</title>

  <link rel="stylesheet" href="/css/main.css" />
</head>

<body>
  <h1>Realtime communication with WebRTC</h1>

  <div id="videos">
    <video id="localVideo" autoplay muted></video>
    <video id="remoteVideo" autoplay></video>
  </div>

  <script src="/socket.io/socket.io.js"></script>
  <script src="js/lib/adapter.js"></script>
  <script src="js/main.js"></script>
</body>

</html>

运行Node.js服务器

如果您不是从工作目录中遵循此代码实验室,则可能需要安装step-05文件夹或当前工作文件夹的依赖项。从您的工作目录中运行以下命令:npm install

安装后,如果您的Node.js服务器未运行,请通过在工作目录中调用以下命令来启动它:node index.js

确保您使用的是实现Socket.IO的上一步中的index.js版本。有关节点和套接字IO的更多信息,请查看“设置信令服务以交换消息”部分。

在新的标签或窗口中再次打开localhost:8080。一个视频元素将显示来自getUserMedia()的本地流,另一个视频元素将显示通过RTCPeerconnection流式传输的“远程”视频。

在浏览器控制台中查看日志记录。

温馨提示:

  • 可以从chrome:// webrtc-internals获得WebRTC统计信息和调试数据。WebRTC stats and debug data are available from chrome://webrtc-internals.
  • test.webrtc.org 可用于检查您的本地环境并测试您的相机和麦克风。test.webrtc.org can be used to check your local environment and test your camera and microphone.
  • 如果您在缓存方面遇到麻烦,请尝试以下操作:If you have odd troubles with caching, try the following:
  • 按住Ctrl键并单击“重新加载”按钮,以进行强制刷新 Do a hard refresh by holding down ctrl and clicking the Reload button
  • 重新启动浏览器 Restart the browser
  • 命令行运行npm cache clean。 Run npm cache clean from the command line.

9、拍照并通过数据通道分享

您将学到什么?

  • 拍摄照片,并使用canvas元素从中获取数据。Take a photo and get the data from it using the canvas element.
  • 与远程用户交换图像数据。Exchange image data with a remote user.

此步骤的完整版本位于step-06文件夹中。

怎么运行的?

之前已经学习了如何使用RTCDataChannel交换文本消息。此步骤使共享整个文件成为可能:在此示例中,通过getUserMedia()捕获的照片。

此步骤的核心部分如下:

1、建立数据通道。请注意,在此步骤中,您不会将任何媒体流添加到对等连接。

2、使用getUserMedia()捕获用户的网络摄像头视频流:

var video = document.getElementById('video');

function grabWebCamVideo() {
  console.log('Getting user media (video) ...');
  navigator.mediaDevices.getUserMedia({
    video: true
  })
  .then(gotStream)
  .catch(function(e) {
    alert('getUserMedia() error: ' + e.name);
  });
}

3、当用户单击“Snap ”按钮时,从视频流中获取快照(视频帧)并将其显示在canvas元素中:

var photo = document.getElementById('photo');
var photoContext = photo.getContext('2d');

function snapPhoto() {
  photoContext.drawImage(video, 0, 0, photo.width, photo.height);
  show(photo, sendBtn);
}

4、当用户单击“发送”按钮时,将图像转换为字节并通过数据通道发送它们:

function sendPhoto() {
  // Split data channel message in chunks of this byte length.
  var CHUNK_LEN = 64000;
  var img = photoContext.getImageData(0, 0, photoContextW, photoContextH),
    len = img.data.byteLength,
    n = len / CHUNK_LEN | 0;

  console.log('Sending a total of ' + len + ' byte(s)');
  dataChannel.send(len);

  // split the photo and send in chunks of about 64KB
  for (var i = 0; i < n; i++) {
    var start = i * CHUNK_LEN,
      end = (i + 1) * CHUNK_LEN;
    console.log(start + ' - ' + (end - 1));
    dataChannel.send(img.data.subarray(start, end));
  }

  // send the reminder, if any
  if (len % CHUNK_LEN) {
    console.log('last ' + len % CHUNK_LEN + ' byte(s)');
    dataChannel.send(img.data.subarray(n * CHUNK_LEN));
  }
}

5、接收方将数据通道消息字节转换回图像,并向用户显示该图像:

function receiveDataChromeFactory() {
  var buf, count;

  return function onmessage(event) {
    if (typeof event.data === 'string') {
      buf = window.buf = new Uint8ClampedArray(parseInt(event.data));
      count = 0;
      console.log('Expecting a total of ' + buf.byteLength + ' bytes');
      return;
    }

    var data = new Uint8ClampedArray(event.data);
    buf.set(data, count);

    count += data.byteLength;
    console.log('count: ' + count);

    if (count === buf.byteLength) {
      // we're done: all data chunks have been received
      console.log('Done. Rendering photo.');
      renderPhoto(buf);
    }
  };
}

function renderPhoto(data) {
  var canvas = document.createElement('canvas');
  canvas.width = photoContextW;
  canvas.height = photoContextH;
  canvas.classList.add('incomingPhoto');
  // trail is the element holding the incoming images
  trail.insertBefore(canvas, trail.firstChild);

  var context = canvas.getContext('2d');
  var img = context.createImageData(photoContextW, photoContextH);
  img.data.set(data);
  context.putImageData(img, 0, 0);
}

了解更多

10、恭喜啦

您构建了一个应用程序来进行实时视频流和数据交换!

你都学到了什么

  • 从您的网络摄像头获取视频。Get video from your webcam.
  • 使用RTCPeerConnection传输视频。Stream video with RTCPeerConnection.
  • 使用RTCDataChannel传输数据。Stream data with RTCDataChannel.
  • 设置信令服务以交换消息。Set up a signaling service to exchange messages.
  • 结合对等连接和信令。Combine peer connection and signaling.
  • 拍照并通过数据通道共享。Take a photo and share it via a data channel.

下一步

了解更多

  • 可从 webrtc.org. 获得一系列WebRTC入门资源。

猜你喜欢

转载自blog.csdn.net/a360940265a/article/details/114004305