WebRTC的google官方入门教程

一、前言

WebRTC可以实现基于浏览器的实时通信。
这个教程演示了如何建立一个视频和文本聊天应用。
关于WebRTC的更多资料,可以看在HTML5 Rock上的
《Getting started with WebRTC》http://www.html5rocks.com/en/tutorials/webrtc/basics
一文,
也可以向WebRTC APIs at bit.ly/webrtcfeedback(http://bit.ly/webrtcfeedback)
提交你的意见。
本文所有链接,如果打不开,则需要。


二、先期准备

基础知识:
1. HTML, CSS and JavaScript
2. git
3. Chrome DevTools


有Node.js (http://nodejs.org/)和socket.io(http://socket.io/)使用经验更好。


在你的开发机器上安装以下程序:
1. Google Chrome or Firefox.
2. 代码编码器,
   可以是UE,
   也可以是HTML开发工具,如Chrome Web Developer Tools
3. 网络摄像头.
4. git, in order to get the source code.
5. The source code(https://bitbucket.org/webrtc/codelab/src).
   下载源码的zip包并解压。
6. Node.js with socket.io and node-static. 
   (Node.js hosting would also be an advantage -- see below for some options.)
   Node.js的安装
   1)前往 http://www.nodejs.org/download/  选择Windows Installer (.msi)版下载; 
   2)点击安装node-v0.10.31-x86.msi文件(文件名根据版本号命名,安装最新版便是)。
   3)测试是否安装成功:
      打开cmd窗口,执行node -v,如果显示版本号说明安装成功。
      如果提示未找到node命令,则通过cmd窗口定位(cd命令)到安装目录,
      再次执行node -v 即可看到版本号了。


      如果希望执行命令前不要每次定位到安装目录,
      那么请在windows环境变量中添加path路径。


    4)安装nodeJS类库。
       在cmd中执行以下命令(如果找不到npm命令,处理方法同上)
       > npm install express
       > npm install yetify
       > npm install getconfig
       > npm install node-uuid
       > npm install socket.io
       > npm install node-static


       如果执行npm命令提示错误如下:Error:ENOENT,stat'c:\Users\Adminstr.....\npm'
       请在该目录下创建npm文件夹,再次执行以上命令。
    至此安装宣告成功。


本文假设你使用的是Mac OS, Linux或Windows操作系统,在ChromeBook上不在好用。
安装了Chrome的Android设备本例子也好用,可以用来移动端来测试这个本例子。
为了在Android的Chrome上运用WebRTC API, 
你需要在移动设备的Chrome浏览器打开chrome://flags页面,开启这个功能。

三、Running The Examples

为了运行本文中提供的所有的例子程序,你需要用命令 cd 到刚解压后代码的 "complete"目录,
然后再进入到每一步的目录下,以本地的方式运行命令:
   > node server.js
这样路径下的DEMO程序就激活了。


3.0 Step 0: 获得代码

使用git, 从 codelab上下载代码到你的开发机上。如果不会用git,那先到网上去查下。
也可从这个页面下载:https://bitbucket.org/webrtc/codelab/downloads


3.1 Step 1: 创建一个空 HTML5 文档

Complete example: complete/step1.
创建一个 HTML 文档的框架.
index.html的代码如下:







WebRTC codelab: step 1





   












在本地目录下运行如下命令:
E:...\complete\step1>node server.js
然后在浏览器地址栏中输入:http://localhost:2013/


1. 出现错误提示:
module.js:338
    throw err;
          ^
Error: Cannot find module 'node-static'
    at Function.Module._resolveFilename (module.js:336:15)
    at Function.Module._load (module.js:278:25)
    at Module.require (module.js:365:17)
    at require (module.js:384:17)
    at Object. (E:\...\complete\step1\server.js:1:76)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)


可以看 server.js的代码如下:
var static = require('node-static');
var http = require('http');
var file = new(static.Server)();
var app = http.createServer(function (req, res) {
  file.serve(req, res);
}).listen(2013)


解决办法:
运行命令:
> npm install node-static


因为:
如果使用了如下的代码:
// foo.js
var static = require('node-static');
var fileServer = new static.Server('./public');
require('http').createServer(function (request, response) {
    request.addListener('end', function () {
        fileServer.serve(request, response);
    }).resume();
}).listen(8080);


就不能使用 -g 选项安装,而要使用
$ npm install node-static




为什么屏幕上什么也没有呢?
看index.html的页面代码就知道,因为标签中本身就没有写内容。


3.2 Step 2: 从你的网络摄像头中获得视频

Complete example: complete/step2.
给你的页面添加一个视频元素。
将下面的代码添加到刚才页面的 script元素中,
用来开启 getUserMeida() ,以设置摄像头的视频源。
代码如下:
navigator.getUserMedia = navigator.getUserMedia ||
  navigator.webkitGetUserMedia || navigator.mozGetUserMedia;


var constraints = {video: true};


function successCallback(localMediaStream) {
  window.stream = localMediaStream; // stream available to console
  var video = document.querySelector("video");
  video.src = window.URL.createObjectURL(localMediaStream);
  video.play();
}


function errorCallback(error){
  console.log("navigator.getUserMedia error: ", error);
}


navigator.getUserMedia(constraints, successCallback, errorCallback);


在本地目录下运行如下命令:
E:...\complete\step1>node server.js
然后在chrome浏览器地址栏中输入:http://localhost:2013/
可以看到页面将摄像头调起,允许通过后,会在页面显示出一个视频预览框。


1. 解析
getUserMedia 的调用方式如下:
  navigator.getUserMedia(constraints, successCallback, errorCallback);


  . constraints 参数指定要取得数据的媒体,在这里仅用了视频:
        var constraints = {"video": true}


如果成功,则把从摄像头得到的视频源设置成视频元素的源:


function successCallback(localMediaStream) {
  window.stream = localMediaStream; // stream available to console
  var video = document.querySelector("video");
  video.src = window.URL.createObjectURL(localMediaStream);
  video.play();
}


2. 附加奖励
1) Inspect the stream object from the console.
2) Try calling stream.stop().
3) What does stream.getVideoTracks() return?
4) Look at the constraints object: 
   what happens when you change it to {audio: true, video: true}?
5) What size is the video element?
   How can you get the video's natural size from JavaScript? 
   Use the Chrome Dev Tools to check. Use CSS to make the video full width.
   How would you ensure the video is no higher than the viewport?
6) Try adding CSS filters to the video element (more ideas here).
7) Try changing constraints: see the sample at 
   simpl.info/getusermedia/constraints(http://simpl.info/getusermedia/constraints).
For example:
video {
  filter: hue-rotate(180deg) saturate(200%);
  -moz-filter: hue-rotate(180deg) saturate(200%);
  -webkit-filter: hue-rotate(180deg) saturate(200%);
}


3.3 Step 3: Stream video with RTCPeerConnection

Complete example: complete/step3.
RTCPeerConnection 是WebRTC API中用于视频和音频呼叫


这个例子用来为在同一个页面两个用户建立连接,
没有其它的功能,但能很好地理解RTCPeerConnection 的工作原理。


编辑HTML页面,添加三个按键:Start, Call and Hang Up:







 
 
 



更详细的代码见:complete/step3/index.html.
按前面的方式运行测试下。


1. 解析
这些代码做了很多工作,包括:
  . 获得并共享本地和远端的描述子:以SDP1格式表述的本地媒体的 metadata
  . 获得并共享ICE2的候选者:网络信息
  . 传输本地流到远端的RTCPeerConnection


2.  附加奖励
1) 查看一下 chrome://webrtc-internals. 
    (There is a full list of Chrome URLs at chrome://about.)
2) Style the page with CSS:
    Put the videos side by side.
    Make the buttons the same width, with bigger text.
    Make sure it works on mobile.
3) 使用Chrome Dev Tools 控制台上查看:, 
    localStream, localPeerConnection and remotePeerConnection.
4) Take a look at localPeerConnection.localDescription. What does SDP format look like?




3.4 Step 4: Stream arbitrary data with RTCDataChannel

Complete example: complete/step4.
For this step, we'll use RTCDataChannel to send text between two textareas on the same page. 
Not very useful, except to demonstrate how the API works.
在这个例子中,我们使用RTCDataChannel在同个页面的两个文本区域发送文本,
虽然不是很有用,但可以用来演示API是如何工作的。
创建一个新的文档并添加下面的HTML代码:







 
 
 



完整代码可见 complete/step4/index.html.
运行并测试;


1. 解析
例子中的代码使用了RTCPeerConnection和RTCDataChannel来开启文本消息交换,
这个例子中的代码绝大部分和RTCPeerConnection例子中的相同,
不同的部分如下:


function sendData(){
  var data = document.getElementById("dataChannelSend").value;
  sendChannel.send(data);
}
...
localPeerConnection = new RTCPeerConnection(servers,
  {optional: [{RtpDataChannels: true}]});
sendChannel = localPeerConnection.createDataChannel("sendDataChannel",
  {reliable: false});
sendChannel.onopen = handleSendChannelStateChange;
sendChannel.onclose = handleSendChannelStateChange;
...
remotePeerConnection = new RTCPeerConnection(servers,
  {optional: [{RtpDataChannels: true}]});
function gotReceiveChannel(event) {
  receiveChannel = event.channel;
  receiveChannel.onmessage = gotMessage;
}
...
remotePeerConnection.ondatachannel = gotReceiveChannel;
function gotMessage(event) {
  document.getElementById("dataChannelReceive").value = event.data;
}


RTCDataChannel的语法和WebSocket极相似,都使用了一个send()方法和一个消息事件。


2. 额外奖励
1)Try out RTCDataChannel file sharing with Sharefest(http://www.sharefest.me/). 
   When would RTCDataChannel need to provide reliable delivery of data, 
   and when might performance be more important -- even if that means losing some data?
2)Use CSS to improve page layout, 
   and add a placeholder attribute to the dataChannelReceive textarea.
3. 在移动设备上测试这个页面;


3.5 Step 5: 创建一个信令服务器并交换消息

Complete example: complete/step5.
为了建立并维护一个WebRTC呼叫,RTCPeerConnection实例需要交换metadata:
  . 候选者(网络)信息。
  . 提交并回答消息,提供媒体信息,包括分辨率,编码格式等。


换句话话说,metadata的交换要在P2P音频、视频和数据流发生之前,这个过程叫信令通信。


在上面的那个例子中,RTCPeerConnection对象的发送者和接收者都是同一个页面,
因此信令通信非常简单,只是方法间的一个对象传递动作。


在实际应用中,RTCPeerConnection的发送者和接收都肯定不会是同一个页面,
因些,我们需要一种方式在这两者之间进行metadata通信。


为了做到这一点,我们需要使用信令服务器:
这个服务器可以在运行于浏览器之上的WebRTC应用进行消息交换。
实际的消息都字符串化成JavaScript对象。


重申: 
对于RTCPeerConnection的P2P音频、视频、数据流通信来说,
通过信令服务器在WebRTC客户端间进行metadata交换是必须的。


在本例中,我们创建了一个简单的Node.js信令服务器,
使用了socket.io 模块和JavaScript库来传递消息。
如果有Node.js和socket.io经验就很有用了。
没有也没有关系,消息通信组件是非常简单的。
在这个例子中,服务器(Node应用)是server.js,
而客户端(网页应用)是index.html


Node服务器应用在本例中有两个任务:
作为消息通信的中介:
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);
} else if (numClients == 1) {
  io.sockets.in(room).emit('join', room);
  socket.join(room);
  socket.emit('joined', room);
} else { // max two clients
  socket.emit('full', room);
}


我们的这个WebRTC应用只允许一个房间中最多两个人。
首先你需要把 Node, socket.io和node-static 安装好。
可以用如下命令安装:
  > npm install socket.io
  > npm install node-static


到step5的目录下,运行 server.js,使用如下命令:
  > node server.js
再到浏览器上,打开localhost:2013;
再在本机打开一个新的浏览器,在地址栏中输入:localhost:2013,
不断这样重复,可以用开发工具看下发生了什么 (Command-Option-J, or Ctrl-Shift-J)。
可以在开发工具的Console上看到信息如下:
This appears to be Chrome
Joining rook xxx
>>> Message from server: Room kks has 2 client(s)
>>> Message from server: Request to create or join room xxx
Room xxx is full


1. 附加奖励
1) 试着开发你自己的消息服务器,这样你可以在公共URL上访问它。
   (有几个免费试用且易于部署的支持Node的消息服务器,包括: 
   including nodejitsu(http://www.nodejitsu.com/), 
   heroku(http://www.heroku.com/) 
   and nodester(http://www.nodester.com/).)
2) What alternative messaging mechanisms are available? 
   (Take a look at apprtc.appspot.com.)
   What problems might we encounter using 'pure' WebSocket? 
   (Take a look at Arnout Kazemier's presentation, WebSuckets.)
3) What issues might be involved with scaling this application?
   Can you develop a method for testing thousands or millions of simultaneous room requests.
4)Try out Remy Sharp's tool nodemon. 
   This monitors any changes in your Node.js application and automatically restarts the server 
   when changes are saved.
5) This app uses a JavaScript prompt to get a room name. 
   Work out a way to get the room name from the URL, 
   for example localhost:2013/foo would give the room name foo.


3.6 Step 6: RTCPeerConnection with messaging

Complete example: complete/step6.
在本例中,我们将使用在例5中的信令服务器 和例3中的RTCPeerConnection代码,
搭建一个视频聊天客户端:
1) 确保你的Node, socket.io 和 node-static都已经安装并可工作,否则见例5.
2) 使用例6目录下的代码,并运行服务器(server.js). 
   使用下面的命令即可:
    >  node server.js
3) 在浏览器的地址栏中输入 : localhost:2013. 
   并有新的标签页和浏览器中输入:localhost:2013
4. 在Chrome DevTools console 中查看日志,
   并可在chrome://webrtc-internals下看到的WebRTC的调试信息。


1. 附加奖励
1) 本应用仅支持一对一的视频聊天,
   你可以修改它的设计以支持在同一个房间中进行多人视频聊天。
   (Look at talky.io(http://talky.io/) for an example of this in action.)
2) 本例中的房间名是固定的foo, 很不好用,有没有更好的房间命名方式?
3)这个应用可以在移动端使用吗? 
   Try it out on a phone, on a 7" and a 10" tablet. 
   What layout, UI and UX changes would be required to ensure a good mobile experience?
4) 将你的APP部署在一个公共URL上(see above for hosting options)
   Try different network configurations, for example with one user on wifi and another on 3G.
   Any problems?
5)How would users share the room name? Try to build an alternative to sharing room names.


3.7 Step 7: 使用RTCDataChannel实现文件共享

Complete example: complete/step7.
例5显示了如何使用RTCDataChannel进行文件消息交换,
本例则实现了整个文件的共享:
在本例中,通过getUserMedia()来截图。


本例的核心功能如下:
1)创建一个数据通道。
注意,在这时,我们并不需要在本例子中为端之间的连接添加任何媒体流。
2)使用getUserMeida()来抓取用户的摄像头视频流:
var video = document.getElementById('video');
getUserMedia({video: true}, function(stream) {
    video.src = window.URL.createObjectURL(stream);
}, getMediaErrorCallback);


3) 当用户点击了Snap热键后,从视频流中获得一张截图并显示它:
var canvas = document.querySelector('canvas);
var context = photo.getContext('2d');
context.drawImage(video, 0, 0, canvasWidth, canvasHeight);


4)当用户点击Send按键后,将这张图片转换成字节并能过数据通道发送:
// Split data channel message in chunks
var CHUNK_LEN = 64000;
// Get the image bytes and calculate the number of chunks
var img = canvas.getImageData(0, 0, canvasWidth, canvasHeight);
var len = img.data.byteLength;
var numChunks = len / CHUNK_LEN | 0;
// Let the other peer know in advance how many bytes to expect in total
dataChannel.send(len);
// Split the photo in chunks and send it over the data channel
for (var i = 0; i < n; i++) {
    var start = i * CHUNK_LEN;
    var end = (i+1) * CHUNK_LEN;
    dataChannel.send(img.data.subarray(start, end));
}
// Send the reminder, if any
if (len % CHUNK_LEN) {
    dataChannel.send(img.data.subarray(n * CHUNK_LEN));
}


5)接收端将数据通道的消息字节转换成图片并显示给用户:
var buf, count;
// dc is an RTCDataChannel initialized somewhere else
dc.onmessage = function(event) {
  if (typeof event.data === 'string') {
      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;
  if (count == buf.byteLength) {
      // we're done: all data chunks have been received
      renderPhoto(buf);
  }
}
function renderPhoto(data) {
    var photo = document.createElement('canvas');
    trail.insertBefore(photo, trail.firstChild);
    var canvas = photo.getContext('2d');
    var img = canvas.createImageData(300, 150);
    img.data.set(data);
    canvas.putImageData(img, 0, 0);
}


1. 运行样例代码
1) 和前面的例子一样,使用命令:
   > node server.js
   启动一个本地的服务器,并在浏览器中打开: localhost:2013
2) 允许使用摄像头
3) APP将会创建一个随机的房间,复制并粘贴这个URL到本机的新的窗口。
4) 点击 ”Snap" 和“Send"按键。


2. 附加奖励
1)在不同的浏览器中进行测试, e.g. Firefox or Opera.
2) Send the room URL to another person in your codelab. You should be able to connect via WiFi.
3) Deploy the app to a public URL and try 'snap & sending' photos with your friends.
4) How can you change the code to make it possible to share any file type with your peers?




3.8 Step 8: Use a WebRTC library: SimpleWebRTC

Complete example: complete/step8.
抽象库如 SimpleWebRTC可以使得创建WebRTC应该更加简单
(You can find other examples of WebRTC libraries at bit.ly/webrtcwebaudio.)
Create a new document using the code from complete/step8/index.html.
Open the document in multiple windows or tab.


1. Bonus points
1)Find a WebRTC library for RTCDataChannel. (Hint: there's one named PeerJS!)
2)Set up your own signaling server using the SimpleWebRTC server signalmaster.


Step 9: 在同一个房间中支持三个或更多的用户

This is a DIY step!
这个例子需要自己动手。
你可以实现一个APP,包括有三个或更多用户的视频聊天,文件传输。
How many users can an app sustain in full mesh mode, 
i.e. with a peer connection between every user?
What are the alternatives? (Take a look at the HTML5 Rocks WebRTC infrastructure article 
for inspiration.)


1. Bonus points
So far, the app hasn't had any work done on layout. Sort it out! 
Make sure your app works well across different devices.

你可能感兴趣的:(流媒体:WebRTC)