# codelab **Repository Path**: cnjack/codelab ## Basic Information - **Project Name**: codelab - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2015-11-04 - **Last Updated**: 2020-12-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # WebRTC ##概述 WebRTC 支持浏览器的实时通讯. 本教程将讲解如何创建一个建议的视频&文字聊天室 在HTML5 Rocks 查询更多关于WebRTC的信息[Getting started with WebRTC](http://www.html5rocks.com/en/tutorials/webrtc/basics) 你可以在[bit.ly/webrtcfeedback](http://bit.ly/webrtcfeedback)提交关于WebRTC APIs的反馈 ## 准备 基础知识: 1. HTML, CSS and JavaScript 2. [git](http://git-scm.com/) 3. [Chrome DevTools](https://developers.google.com/chrome-developer-tools/) 拥有 [Node.js](http://nodejs.org/) 和 [socket.io](http://socket.io/)的经验 也将非常有帮助. 软件&硬件准备: 1. Google Chrome or Firefox. 1. 代码编辑器. 1. 摄像头. 1. git, 用来获取代码. 1. git clone [https://bitbucket.org/webrtc/codelab/src](https://bitbucket.org/webrtc/codelab/src). 1. Node.js with socket.io and node-static. (Node.js hosting would also be an advantage -- see below for some options.) * codelab 里面的代码可以运行在win, linux, ox 系统上面. 然而在 Chromebook 上运行可能不太容易!* It would also be useful to have an Android device with Google Chrome installed in order to try out the examples on mobile. To run WebRTC APIs on Chrome for Android, you must enable WebRTC from the chrome://flags page. ## 运行例子 To run any of the examples provided in the `complete` directory of this repo simply cd into the specific step directory you'd like to see locally and run ``` node server.js ``` That directory's demo will then be live [here](http://localhost:2013). ## Step 0: 获取代码 使用git clone codelab到你的电脑上,如果你还没有git,[git website](http://git-scm.com/)上有些教程 ## Step 1: 创建一个html5页面 完整示例: [complete/step1](https://bitbucket.org/webrtc/codelab/src/master/complete/step1). 1. 创建一个基本的HTML文档. 1. 测试 打开 [locally](http://localhost:2013) (参见以上运行演示). - 为什么屏幕上没有任何东西? ## Step 2: 从你的网络摄像头获取视频 完整示例: [complete/step2](https://bitbucket.org/webrtc/codelab/src/master/complete/step2). 1. 在HTML文档上面创建一个video节点. 2. 把如下JS代码放到你的HTML上面(如此可以通过 getUserMedia() 获取到摄像头的资源): 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); 1. 通过 [locally](http://localhost:2013) 测试 (参见以上运行演示). ### 注解 `getUserMedia` 按照如下方法使用: navigator.getUserMedia(constraints, successCallback, errorCallback); 通过constraints可是可以只获得视频(没有声音): var constraints = {"video": true} 如果没有什么错误,video就可以获取到stream的资源了(ps stream就是摄像头传过来的视频资源): function successCallback(localMediaStream) { window.stream = localMediaStream; // stream available to console var video = document.querySelector("video"); video.src = window.URL.createObjectURL(localMediaStream); video.play(); } ### 课后作业 1. 尝试通过console输出Stream. 2. 尝试 `stream.stop()`. 3. `stream.getVideoTracks()` 返回了什么? 4. 如果传入 `{audio: true, video: true}` 参数,将会发生什么? 5. video的宽度是什么决定的? 如何通过js代码获取视频的尺寸? 使用 Chrome Dev Tools 检测. 使用 CSS 使 video 撑满全屏. 如何确保视频不超过你的设置高度? 6. 尝试添加 CSS filters 到 video element (more ideas [here](http://html5-demos.appspot.com/static/css/filters/index.html)). 7. 尝试修改 constraints 参数: see the sample at [simpl.info/getusermedia/constraints](https://simpl.info/getusermedia/constraints/). 比如: video { filter: hue-rotate(180deg) saturate(200%); -moz-filter: hue-rotate(180deg) saturate(200%); -webkit-filter: hue-rotate(180deg) saturate(200%); } ## Step 3: 使用 RTCPeerConnection 传输视频流 完整示例: [complete/step3](https://bitbucket.org/webrtc/codelab/src/master/complete/step3). RTCPeerConnection 作为 WEBRTC API服务于视频或语音通话. 本案例在同一个页面建立了两个 peers 的链接,很少会使用到,但是对于理解 RTCPeerConnection 还是起到很大的作用! 1. 抛弃你之前的js观念 -- 我们会做一些不同的东西! 2. 按照如下编辑HTML:
3. 从 [complete/step3/index.html](https://bitbucket.org/webrtc/codelab/raw/master/complete/step3/index.html) 添加 JavaScript. 1. 访问 [locally](http://localhost:2013) 测试 (参见以上运行演示). ### 课后作业 This code does a lot! * Get and share local and remote descriptions: metadata about local media in SDP[^SDP] format. * Get and share ICE[^ICE] candidates: network information. * Pass the local stream to the remote _RTCPeerConnection_. [^SDP]: [Session Description Protocol](http://en.wikipedia.org/wiki/Session_Description_Protocol) [^ICE]: [Interactive Connectivity Establishment Protocol](http://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment) ### Bonus points 1. Take a look at _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. From the Chrome Dev Tools console, inspect _localStream_, _localPeerConnection_ and _remotePeerConnection_. 4. Take a look at _localPeerConnection.localDescription_. What does SDP format look like? ## Step 4: Stream arbitrary data with RTCDataChannel Complete example: [complete/step4](https://bitbucket.org/webrtc/codelab/src/master/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. 1. Create a new document and add the following HTML:
1. Add the JavaScript from [complete/step4/index.html](https://bitbucket.org/webrtc/codelab/raw/master/complete/step4/index.html). 1. Test it out [locally](http://localhost:2013) (see instructions above on running demos). ### Explanation This code uses RTCPeerConnection and RTCDataChannel to enable exchange of text messages. Most of the code in this section is the same as for the RTCPeerConnection example. Additional code is as follows: 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; } The syntax of RTCDataChannel is deliberately similar to WebSocket, with a `send()` method and a `message` event. Notice the use of constraints. ### Bonus points 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. 4. Test the page on a mobile device. ## Step 5: Set up a signaling server and exchange messages Complete example: [complete/step5](https://bitbucket.org/webrtc/codelab/src/master/complete/step5). RTCPeerConnection instances need to exchange metadata in order to set up and maintain a WebRTC 'call': * Candidate (network) information. * _Offer_ and _answer_ messages providing information about media such as resolution and codecs. In other words, an exchange of metadata is required before peer-to-peer audio, video or data streaming can take place. This process is called _signaling_. In the examples already completed, the 'sender' and 'receiver' RTCPeerConnection objects are on the same page, so signaling is simply a matter of passing objects between methods. In a real world application, the sender and receiver RTCPeerConnections are not on the same page, and we need a way for them to communicate metadata. For this, we use a signaling server: a server that can exchange messages between a WebRTC app (client) running in one browser and a client in another browser. The actual messages are stringified JavaScript objects. **To reiterate: metadata exchange between WebRTC clients (via a signaling server) is required for RTCPeerConnection to do audio, video and data streaming (peer to peer).** In this step we'll build a simple Node.js signaling server, using the socket.io Node module and JavaScript library for messaging. Experience of [Node.js](http://nodejs.org/) and [socket.io](http://socket.io/) will be useful, but not crucial -- the messaging components are very simple. In this example, the server (the Node app) is _server.js_ and the client (the web app) is _index.html_. The Node server application in this step has two tasks. To act as a messaging intermediary: socket.on('message', function (message) { log('Got message: ', message); socket.broadcast.emit('message', message); }); To manage WebRTC video chat 'rooms': 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); } Our simple WebRTC application will only permit a maximum of two peers to share a room. 1. Ensure you have Node, socket.io and [node-static](https://github.com/cloudhead/node-static) installed. Node can be downloaded from [nodejs.org](http://nodejs.org/); installation is straightforward and quick. To install socket.io and node-static, run Node Package Manager from a terminal in your application directory: npm install socket.io npm install node-static (You don't need to learn about node-static for this exercise: it just makes the server simpler.) 2. Using the code from the [step 5](complete/step5) directory, run the server (_server.js_). To start the server, run the following command from a terminal in your application directory: node server.js 3. From your browser, open _localhost:2013_. Open a new tab page or window in any browser and open _localhost:2013_ again, then repeat. 4. To see what's happening, check the Chrome DevTools console (Command-Option-J, or Ctrl-Shift-J). ### Bonus points 1. Try deploying your messaging server so you can access it via a public URL. (Free trials and easy deployment options for Node are available on several hosting sites 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](http://apprtc.appspot.com).) What problems might we encounter using 'pure' WebSocket? (Take a look at Arnout Kazemier's presentation, [WebSuckets](https://speakerdeck.com/3rdeden/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](https://github.com/remy/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_. ## Step 6: RTCPeerConnection with messaging Complete example: [complete/step6](https://bitbucket.org/webrtc/codelab/src/master/complete/step6). In this step, we build a video chat client, using the signaling server we created in Step 5 and the RTCPeerConnection code from Step 3. **This step users [adapter.js](https://bitbucket.org/webrtc/codelab/src/master/complete/step6/js/lib/adapter.js). This is a [JavaScript shim](http://stackoverflow.com/questions/6599815/what-is-the-difference-between-a-shim-and-a-polyfill), maintained by Google, that abstracts away browser differences and spec changes.** 1. Ensure you have Node, socket.io and [node-static](https://github.com/cloudhead/node-static) installed and working. If in doubt, try the code in Step 5. 2. Using the code from the _step 6_ directory, run the server (_server.js_). To start the server, run the following from your application directory: node server.js 3. From your browser, open [localhost:2013](http://localhost:2013). Open a new tab page or window and open [localhost:2013](http://localhost:2013) again. 4. View logging from the Chrome DevTools console and WebRTC debug information from chrome://webrtc-internals. ### Bonus points 1. This application only supports one-to-one video chat. How might you change the design to enable more than one person to share the same video chat room? (Look at [talky.io](http://talky.io) for an example of this in action.) 2. The example has the room name _foo_ hard coded. What would be the best way to enable other room names? 3. Does the app work on mobile? 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. Deploy your app at a public 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. ## Step 7: File sharing using RTCDataChannel Complete example: [complete/step7](https://bitbucket.org/webrtc/codelab/src/4dc79328c01d890e7b2476721c42fbd6a31f15bf/complete/stepx/?at=step-x). Step 5 showed how to exchange text messages using RTCDataChannel. This step makes it possible to share entire files: in this example, photos captured via `getUserMedia()`. The core parts of this step are as follows: 1. Establish a data channel. Note that we don't add any media streams to the peer connection in this step. 2. Grab the user's webcam video stream with `getUserMedia()`: var video = document.getElementById('video'); getUserMedia({video: true}, function(stream) { video.src = window.URL.createObjectURL(stream); }, getMediaErrorCallback); 3. When the user clicks on the **Snap** button, get a snapshot (a video frame) from the video stream and display it: var canvas = document.querySelector('canvas); var context = photo.getContext('2d'); context.drawImage(video, 0, 0, canvasWidth, canvasHeight); 4. When the user clicks on the **Send** button, convert the image to bytes and send them over a data channel: // 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. The receiving side converts data channel message bytes back to an image and displays this to the user: 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); } ### Playing with the sample code 1. As in previous steps, start a local server with `node server.js` and navigate to http://localhost:2013. 2. Allow the app to use your camera. 3. The app will create a random room. Copy and paste the URL into a new window. 4. Click the 'Snap & send' button and see what happens in the other window. ### Bonus points 1. Try combinations of different browsers, 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? ## Step 8: Use a WebRTC library: SimpleWebRTC Complete example: [complete/step8](https://bitbucket.org/webrtc/codelab/src/master/complete/step8). Abstraction libraries such as SimpleWebRTC make it simple to create WebRTC applications. (You can find other examples of WebRTC libraries at [bit.ly/webrtcwebaudio](http://bit.ly/webrtcwebaudio).) 1. Create a new document using the code from [complete/step8/index.html](https://bitbucket.org/webrtc/codelab/src/master/complete/step8/index.html). 2. Open the document in multiple windows or tab. ### 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](https://github.com/andyet/signalmaster). ## Step 9: Support three or more users in the same room This is a DIY step! How would you implement an app combining video chat and file exchange for three or more users? 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](http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/#beyond-one-to-one-multi-party-webrtc) for inspiration.) ### Bonus points 1. So far, the app hasn't had any work done on layout. Sort it out! Make sure your app works well across different devices.