程式碼研究室簡介
1. 事前準備
本程式碼研究室將教您如何建構應用程式,以透過網路攝影機擷取影片及拍攝快照,並透過 WebRTC 點對點分享。同時瞭解如何使用核心 WebRTC API,以及如何使用 Node.js 設定訊息伺服器。
必要條件
- HTML、CSS 和 JavaScript 基礎知識
建構項目
- 使用網路攝影機拍攝影片。
- 透過
RTCPeerConnection
串流播放影片。 - 使用
RTCDataChannel
串流資料。 - 設定信號服務以交換訊息。
- 結合對等點連線和訊號。
- 拍攝相片並透過資料管道分享。
軟硬體需求
- Chrome 47 以上版本
- Chrome 網路伺服器或您選擇的網路伺服器
- 您選擇的文字編輯器
- Node.js
2. 取得範例程式碼
下載程式碼
- 如果您對 Git 還很熟悉,請執行下列指令,從 GitHub 複製這個程式碼研究室的程式碼:
git clone https://github.com/googlecodelabs/webrtc-web
您也可以按此連結,下載程式碼的 ZIP 檔案:
- 開啟已下載的 ZIP 檔案,解壓縮名稱為「
webrtc-web-master
」的專案資料夾,其中包含每個程式碼研究室步驟和您需要的所有資源所在的資料夾。
您會在「work
」目錄中執行所有程式碼工作。
這個 step-nn
資料夾包含每個程式碼研究室步驟的已完成版本。請隨時參考。
安裝並驗證網路伺服器
雖然您可以自由使用自己的網路伺服器,但這個程式碼研究室是專為 Chrome 專用 Web Server 所設計。
- 如果您沒有 Chrome 的網路伺服器,請按一下以下連結,透過 Chrome 線上應用程式商店安裝:
- 按一下 [加到 Chrome] 即可安裝 Chrome 專用的 Web Server,並在新分頁中開啟 Google 應用程式。
- 按一下 [網路伺服器]:
系統隨即會顯示對話方塊,讓您設定本機網路伺服器:
- 按一下 [選擇資料夾]。
- 選取您建立的
work
資料夾。
在「網路伺服器網址」下,您可以看到處理中網址:
。
- 在「選項 (可能需要重新啟動)」下方,勾選 [自動顯示 index.html] 核取方塊。
- 切換「Web Server: Start」(網路伺服器:已啟動) 兩次,即可停止及重新啟動伺服器。
- 按一下「網路伺服器網址」下方的網址,即可查看您在網路瀏覽器中的工作。
您應該會看到類似 work/index.html
的網頁:
當然,這個應用程式目前並未執行任何操作,這個 API 只是最基本的架構,可確保您的網路伺服器正常運作。您可以在後續步驟中新增功能和版面配置功能。
3. 透過網路攝影機串流播放影片
這個步驟的完整版本位於 step-01
資料夾中。
新增破折號
複製此程式碼並貼到 work
目錄中的 index.html
檔案中,以新增 video
和 script
元素:
<!DOCTYPE html>
<html>
<head>
<title>Real-time communication with WebRTC</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>Real-time communication with WebRTC</h1>
<video autoplay playsinline></video>
<script src="js/main.js"></script>
</body>
</html>
新增一堆 JavaScript
複製此程式碼並貼到 js
資料夾中的 main.js
檔案中:
'use strict';
// In this codelab, you only stream video, not audio (video: true).
const mediaStreamConstraints = {
video: true,
};
// The video element where the stream is displayed
const localVideo = document.querySelector('video');
// The local stream that's displayed on the video
let localStream;
// Handle success and add the MediaStream to the video element
function gotLocalMediaStream(mediaStream) {
localStream = mediaStream;
localVideo.srcObject = mediaStream;
}
// Handle error and log a message to the console with the error message
function handleLocalMediaStreamError(error) {
console.log('navigator.getUserMedia error: ', error);
}
// Initialize media stream
navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
.then(gotLocalMediaStream).catch(handleLocalMediaStreamError);
試試看
在瀏覽器中開啟 index.html
檔案後,你應該會看到類似下方的內容,不過必須透過網路攝影機來觀看:
運作方式
在 getUserMedia()
呼叫之後,如果瀏覽器要求取得目前來源的第一次攝影機存取要求,瀏覽器就會要求存取攝影機。
如果成功,系統會傳回 MediaStream
,這個 media
元素可透過 srcObject
屬性使用:
navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
.then(gotLocalMediaStream).catch(handleLocalMediaStreamError);
}
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
}
constraints
引數可讓您指定要取得的媒體。在本範例中,媒體預設為影片,因為音訊預設為停用:
const mediaStreamConstraints = {
video: true,
};
您可以利用其他限制來滿足其他條件,例如影片解析度:
const hdConstraints = {
video: {
width: {
min: 1280
},
height: {
min: 720
}
}
}
MediaTrackConstraints
規格列出了所有潛在限制條件類型,但並非所有瀏覽器都支援所有的選項。如果目前選取的攝影機不支援你所要求的解析度,getUserMedia()
會顯示 OverconstrainedError
,並會提示你授予相機存取權。
如果 getUserMedia()
成功,攝影機的視訊串流就會設為影片元素的來源:
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
}
累計獎勵積分
- 傳送至
getUserMedia()
的localStream
物件位於全域範圍中,因此您可以透過瀏覽器控制台檢查這個物件。開啟主控台,輸入stream,
,然後按下Enter
(Mac 上為Return
)。如要在 Chrome 中查看主控台,請按下Control+Shift+J
(在 Mac 電腦上為Command+Option+J
)。 localStream.getVideoTracks()
會傳回什麼?- 歡迎致電
localStream.getVideoTracks()[0].stop()
。 - 查看限制條件物件。變更為
{audio: true, video: true}
會有什麼影響? - 影片元素的大小為何?相較於顯示大小,如何從 JavaScript 取得影片的自然大小?請使用 Google Chrome 開發人員工具進行檢查。
- 在影片元素中加入 CSS 濾鏡,如下所示:
video {
filter: blur(4px) invert(1) opacity(0.5);
}
- 新增 SVG 濾鏡,如下所示:
video {
filter: hue-rotate(180deg) saturate(200%);
}
訣竅
- 別忘了對
video
元素上的autoplay
屬性。如果沒有這個圖示,你只會看到單一畫面! - 其他關於
getUserMedia()
限制條件的選項有很多。如需更多範例,請參閱限制和使用統計資料和更多限制條件和使用統計資料WebRTC 樣本上的統計資料。
最佳做法
確認您的影片元素並未溢出其容器。此程式碼研究室新增了 width
和 max-width
檔案,以設定影片的偏好大小和大小上限。瀏覽器會自動計算高度。
video {
max-width: 100%;
width: 320px;
}
4. 透過 RTCPeerConnection API 串流播放影片
這個步驟的完整版本位於 step-2
資料夾中。
新增影片元素和控制按鈕
在 index.html
檔案中,將單一 video
元素替換為兩個 video
和三個 button
元素:
<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
<div>
<button id="startButton">Start</button>
<button id="callButton">Call</button>
<button id="hangupButton">Hang Up</button>
</div>
其中一個影片元素顯示來自 getUserMedia()
的串流,另一個影片則顯示了透過 RTCPeerconnection
串流的相同影片。(在實際應用程式中,一個 video
元素會顯示本機串流,另一個顯示遠端串流。)
新增 Adapter.js shim
複製這個指令碼元素,並貼到 main.js
的指令碼元素上方:
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
您的 index.html
檔案現在應如下所示:
<!DOCTYPE html>
<html>
<head>
<title>Real-time communication with WebRTC</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>Real-time communication with WebRTC</h1>
<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
<div>
<button id="startButton">Start</button>
<button id="callButton">Call</button>
<button id="hangupButton">Hang Up</button>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
安裝 RTCPeerConnection
程式碼
將 main.js
替換為「step-02
」資料夾中的版本。
撥打電話
- 開啟
index.html
檔案。 - 按一下 [開始],即可透過網路攝影機取得影片。
- 按一下 [通話] 建立對等連線
您應該會在網路攝影機的 video
元素中看見同一部影片。
- 查看瀏覽器控制台以查看 WebRTC 記錄。
運作方式
這個步驟非常複雜。
WebRTC 會使用 RTCPeerConnection
API 來設定連結,以在 WebRTC 用戶端 (又稱為對等點) 之間串流播放影片。
在這個範例中,兩個 RTCPeerConnection
物件都位於同一個頁面上:pc1
和 pc2
。
WebRTC 對等點之間的呼叫設定包含三項工作:
- 為每通電話建立
RTCPeerConnection
,然後在每端新增getUserMedia()
中的本機串流。 - 取得及分享網路資訊。
潛在的連線端點稱為 ICE 候選項目。
- 取得及分享本機和遠端說明。
本地媒體的中繼資料採用 Session Description Protocol (SDP) 格式。
假設小莉想使用RTCPeerConnection
建立視訊通訊,
首先,Alice 和 Bob 交換網路資訊「尋找候選對象」運算式是指使用 ICE 架構尋找網路介面和通訊埠的程序。
- 小莉使用
onicecandidate (addEventListener('icecandidate'))
處理常式建立RTCPeerConnection
物件。
這與來自 main.js
的下列程式碼相符:
let localPeerConnection;
localPeerConnection = new RTCPeerConnection(servers);
localPeerConnection.addEventListener('icecandidate', handleConnection);
localPeerConnection.addEventListener(
'iceconnectionstatechange', handleConnectionChange);
- 小艾呼叫
getUserMedia()
並新增傳遞至該串流的串流:
navigator.mediaDevices.getUserMedia(mediaStreamConstraints).
then(gotLocalMediaStream).
catch(handleLocalMediaStreamError);
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
localStream = mediaStream;
trace('Received local stream.');
callButton.disabled = false; // Enable call button.
}
localPeerConnection.addStream(localStream);
trace('Added local stream to localPeerConnection.');
- 當網路候選項目可用時,系統會呼叫第一個步驟的
onicecandidate
處理常式。 - 小莉傳送序列化的候選資料給 Bob。
在實際應用程式中,這個過程稱為「傳送信號」,是透過訊息服務進行。您將在後續步驟中說明如何執行這項動作。當然,在這個步驟中,這兩個 RTCPeerConnection
物件會位於同一個頁面上,而且可直接收發訊息,不需使用外部訊息。
- 他收到來自小莉的候選人訊息時,會呼叫
addIceCandidate()
將候選者新增至遠端同行說明:
function handleConnection(event) {
const peerConnection = event.target;
const iceCandidate = event.candidate;
if (iceCandidate) {
const newIceCandidate = new RTCIceCandidate(iceCandidate);
const otherPeer = getOtherPeer(peerConnection);
otherPeer.addIceCandidate(newIceCandidate)
.then(() => {
handleConnectionSuccess(peerConnection);
}).catch((error) => {
handleConnectionFailure(peerConnection, error);
});
trace(`${getPeerName(peerConnection)} ICE candidate:\n` +
`${event.candidate.candidate}.`);
}
}
WebRTC 對等點也需要探索和交換本機與遠端音訊和視訊媒體資訊,例如解析度和轉碼器功能。用來進行媒體設定資訊的信號,會使用 SDP 格式交換中繼資料 Blob (也就是優惠與答案),
- 小艾執行
RTCPeerConnection
createOffer()
方法。
傳回的承諾提供 RTCSessionDescription
—Alice 的本機工作階段說明:
trace('localPeerConnection createOffer start.');
localPeerConnection.createOffer(offerOptions)
.then(createdOffer).catch(setSessionDescriptionError);
- 如果成功,Alice 會使用
setLocalDescription()
設定本機說明,然後透過信號管道將此工作階段說明傳送給小包。 - 小包將「小艾」提供的說明設為「
setRemoteDescription()
」為遙控器說明。 - 小包負責執行
RTCPeerConnection
createAnswer()
方法並傳送自己從小艾取得的遠端說明,然後產生與自己相容的當地工作階段。 - 《
createAnswer()
》承諾會通過一個「RTCSessionDescription
」,但 Bob 將「:」設為當地說明,並傳送給 Alice。 - 後,Alice 得到 Bob's 評論描述,她使用
setRemoteDescription()
設置為遠程描述。
// Logs offer creation and sets peer connection session descriptions
function createdOffer(description) {
trace(`Offer from localPeerConnection:\n${description.sdp}`);
trace('localPeerConnection setLocalDescription start.');
localPeerConnection.setLocalDescription(description)
.then(() => {
setLocalDescriptionSuccess(localPeerConnection);
}).catch(setSessionDescriptionError);
trace('remotePeerConnection setRemoteDescription start.');
remotePeerConnection.setRemoteDescription(description)
.then(() => {
setRemoteDescriptionSuccess(remotePeerConnection);
}).catch(setSessionDescriptionError);
trace('remotePeerConnection createAnswer start.');
remotePeerConnection.createAnswer()
.then(createdAnswer)
.catch(setSessionDescriptionError);
}
// Logs answer to offer creation and sets peer-connection session descriptions
function createdAnswer(description) {
trace(`Answer from remotePeerConnection:\n${description.sdp}.`);
trace('remotePeerConnection setLocalDescription start.');
remotePeerConnection.setLocalDescription(description)
.then(() => {
setLocalDescriptionSuccess(remotePeerConnection);
}).catch(setSessionDescriptionError);
trace('localPeerConnection setRemoteDescription start.');
localPeerConnection.setRemoteDescription(description)
.then(() => {
setRemoteDescriptionSuccess(localPeerConnection);
}).catch(setSessionDescriptionError);
}
累計獎勵積分
- 前往 chrome://webrtc-internals。
本頁提供 WebRTC 統計資料和偵錯資料。(如需完整的 Chrome 網址清單,請前往 chrome://about)。
- 使用 CSS 設定網頁樣式:
- 將影片並排放置。
- 讓按鈕的寬度與寬度較大。
- 確保版面配置可在行動裝置上使用。
- 在 Chrome 開發人員工具主控台中查看「
localStream
」、「localPeerConnection
」和「remotePeerConnection
」。 - 在主控台中查看「
localPeerConnectionpc1.localDescription
」。
SDP 格式是什麼樣子?
訣竅
- 如要進一步瞭解 Adapter.js 填充碼,請參閱 adapter.js GitHub 存放區。
- 查看 AppRTC 和程式碼,這是 WebRTC 專案是 WebRTC 呼叫的標準應用程式。通話設定時間少於 500 毫秒。
最佳做法
為了確保您的程式碼日後不受影響,請使用新版 Promise 的 API,並和不支援 adapter.js 的瀏覽器相容。
5. 使用資料管道交換資料
這個步驟的完整版本位於 step-03
資料夾中。
更新 HTML
在這個步驟中,您使用 WebRTC 資料管道來在同一個頁面中,在兩個 textarea
元素之間傳送文字。雖然這項功能不實用,但可確實說明 WebRTC 可以用來分享資料及串流播放影片。
將 video
和 button
元素從 index.html,
中移除,並替換為下列 HTML:
<textarea id="dataChannelSend" disabled
placeholder="Press Start, enter some text, then press Send."></textarea>
<textarea id="dataChannelReceive" disabled></textarea>
<div id="buttons">
<button id="startButton">Start</button>
<button id="sendButton">Send</button>
<button id="closeButton">Stop</button>
</div>
一個textarea
是輸入文字,另一個則是在對等點之間以串流方式顯示文字。
您的 index.html
檔案現在應如下所示:
<!DOCTYPE html>
<html>
<head>
<title>Real-time communication with WebRTC</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>Real-time communication with WebRTC</h1>
<textarea id="dataChannelSend" disabled
placeholder="Press Start, enter some text, then press Send."></textarea>
<textarea id="dataChannelReceive" disabled></textarea>
<div id="buttons">
<button id="startButton">Start</button>
<button id="sendButton">Send</button>
<button id="closeButton">Stop</button>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
更新 JavaScript
- 將
main.js
替換成step-03/js/main.js
的內容。
- 嘗試在對等點之間串流資料:
- 開啟
index.html
。 - 按一下 [開始] 設定對等互連連線。
- 在左側的 [
textarea
] 中輸入文字。 - 按一下 [傳送],即可透過 WebRTC 資料管道傳輸文字。
運作方式
這個程式碼會使用 RTCPeerConnection
和 RTCDataChannel
來收發簡訊。
這個步驟中的大部分的程式碼都與 RTCPeerConnection
範例相同。sendData()
和 createConnection()
函式含有大部分的新程式碼:
function createConnection() {
dataChannelSend.placeholder = '';
var servers = null;
pcConstraint = null;
dataConstraint = null;
trace('Using SCTP based data channels');
// For SCTP, reliable and ordered delivery is true by default.
// Add localConnection to global scope to make it visible
// from the browser console.
window.localConnection = localConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created local peer connection object localConnection');
sendChannel = localConnection.createDataChannel('sendDataChannel',
dataConstraint);
trace('Created send data channel');
localConnection.onicecandidate = iceCallback1;
sendChannel.onopen = onSendChannelStateChange;
sendChannel.onclose = onSendChannelStateChange;
// Add remoteConnection to global scope to make it visible
// from the browser console.
window.remoteConnection = remoteConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created remote peer connection object remoteConnection');
remoteConnection.onicecandidate = iceCallback2;
remoteConnection.ondatachannel = receiveChannelCallback;
localConnection.createOffer().then(
gotDescription1,
onCreateSessionDescriptionError
);
startButton.disabled = true;
closeButton.disabled = false;
}
function sendData() {
var data = dataChannelSend.value;
sendChannel.send(data);
trace('Sent Data: ' + data);
}
RTCDataChannel
的語法與使用 send()
方法和 message
事件的 WebSocket 很相似。
請注意 dataConstraint
的使用行為。資料管道可設為啟用不同類型的資料分享方式,例如優先顯示穩定傳送的成效優於效能。
累計獎勵積分
- 使用 SCTP 時,WebRTC 資料管道所使用的通訊協定預設會啟用可靠且已排序的傳輸資料。在什麼情況下,
RTCDataChannel
是否需要提供可靠的資料,且何時應該更重視效能指標 (即使遺失部分資料也是如此)。 - 使用 CSS 改善網頁版面配置,並為
dataChannelReceive
textarea
新增預留位置屬性。 - 透過行動裝置測試網頁。
瞭解詳情
6. 設定信號服務以收發訊息
您已經學習如何在同個網頁上交換彼此的資料,但要如何在不同的電腦之間交換資料?首先,您必須設定信號管道來交換中繼資料訊息。
這個步驟的完整版本位於 step-04
資料夾中。
關於應用程式
WebRTC 使用用戶端 JavaScript API,但要用於實際操作,也需要一個訊號 (訊息傳遞) 伺服器,以及 STUN 和 TURN 伺服器。詳情請參閱這篇說明文章。
在這個步驟中,您將使用 Socket.IO Node.js 模組和 JavaScript 程式庫來建構簡易型 Node.js 訊號伺服器。
在此範例中,伺服器 (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>Real-time communication with WebRTC</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>Real-time communication with WebRTC</h1>
<script src="/socket.io/socket.io.js"></script>
<script src="js/main.js"></script>
</body>
</html>
執行此步驟時,網頁並不會顯示任何內容。所有記錄都會在瀏覽器控制台中執行。如要在 Chrome 中查看主控台,請按下 Control+Shift+J
(在 Mac 電腦上為 Command+Option+J
)。
- 將
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);
});
設定在 Node.js 上執行的 Socket.IO 檔案
您可能注意到 HTML 檔案使用的是 Socket.IO 檔案:
<script src="/socket.io/socket.io.js"></script>
- 在
work
目錄的頂層,建立名為package.json
的檔案,並在當中加入下列內容:
{
"name": "webrtc-codelab",
"version": "0.0.1",
"description": "WebRTC codelab",
"dependencies": {
"node-static": "^0.7.10",
"socket.io": "^1.2.0"
}
}
這是應用程式資訊清單,用於告知 Node Package Manager (npm
) 哪一項專案
要安裝的依附元件。
- 如要安裝依附元件 (例如
/socket.io/socket.io.js
),請從work
目錄的指令列終端機執行下列指令:
npm install
安裝紀錄應該會如下所示:
如您所見,npm
安裝了 package.json
中定義的依附元件。
- 在
work
目錄 (而非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);
}
});
}
});
});
- 透過指令列的終端機在
work
目錄中執行下列指令:
node index.js
- 透過瀏覽器前往 http://localhost:8080。
每次您前往這個網址時,系統都會提示您輸入房間名稱。
如要加入相同的會議室,請每次輸入相同的會議室名稱,例如 foo
。
- 開啟新分頁,再瀏覽至 http://localhost:8080,然後再次輸入相同的會議室名稱。
- 開啟另一個新分頁,再次瀏覽至 http://localhost:8080,然後重新輸入相同的會議室名稱。
- 請檢查各個分頁中的主控台。
您應該會看到來自 JavaScript 的記錄。
累計獎勵積分
- 您還能使用其他哪些訊息機制?使用 pure WebSocket 時,可能會遇到哪些問題?
- 擴充該應用程式時可能遇到哪些問題?您能否開發出可行測試的數千種聊天室要求?
- 這個應用程式使用 JavaScript 提示取得聊天室名稱。瞭解如何從網址取得會議室名稱。舉例來說,http://localhost:8080/foo 會以會議室名稱「
foo
」命名。
瞭解詳情
7. 結合對等連線和訊號
這個步驟的完整版本位於 step-05
資料夾中。
取代 HTML 和 JavaScript
- 將
index.html
改成以下內容:
<!DOCTYPE html>
<html>
<head>
<title>Real-time communication with WebRTC</title>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<h1>Real-time 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="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
- 將
js/main.js
替換成step-05/js/main.js
的內容。
執行 Node.js 伺服器
如果您不是從 work
目錄追蹤這個程式碼研究室,您可能需要安裝 step-05
資料夾或目前工作資料夾的依附元件。
- 在您的工作目錄中執行下列指令:
npm install
- 安裝完成後,如果您的 Node.js 伺服器並未執行,請在
work
目錄中執行下列指令,以啟動該伺服器:
node index.js
請確認您使用的是執行 Socket.IO 的步驟的 index.js
版本。如要進一步瞭解節點和 IO IO,請參閱「設定信號服務」以交換訊息。
- 透過瀏覽器前往 http://localhost:8080。
- 開啟新分頁,然後再次瀏覽至 http://localhost:8080。
其中一個 video
元素顯示來自 getUserMedia()
的本機串流,另一個顯示透過 RTCPeerconnection
串流的遠端影片。
- 在瀏覽器主控台中查看記錄。
b 分****分
- 這個應用程式僅支援一對一視訊通訊功能。如果想讓多人共用相同的視訊通訊聊天室,你該如何調整設計?
- 範例中的會議室名稱是「
foo
」,硬式編碼。啟用其他會議室名稱的最佳方式為何? - 使用者如何共用會議室名稱?請試著建立替代聊天室名稱的分享方式。
- 如何變更應用程式?
訣竅
- 前往 chrome://webrtc-internals 找出 WebRTC 統計資料及偵錯資料。
- 使用 WebRTC 疑難排解工具來檢查本機環境,並測試攝影機和麥克風。
- 如果您在快取時遇到問題,請嘗試下列做法:
- 按下
Control
並點選 [重新載入此頁面]。 - 重新啟動瀏覽器。
- 透過指令列執行
npm cache clean
。
8. 拍攝相片並透過資料管道分享相片
這個步驟的完整版本位於 step-06
資料夾中。
運作方式
您之前已經學會如何使用 RTCDataChannel
交換簡訊。這個步驟可讓您共用整個檔案。在本範例中,相片是以getUserMedia()
拍攝。
這個步驟的核心部分如下:
- 建立資料管道。
在此步驟中,您並未將任何媒體串流加入對等連線。
- 使用
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);
});
}
- 按一下 [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);
}
- 按一下 [Send] (傳送),將圖片轉換為位元組,並透過資料管道傳送圖片:
function sendPhoto() {
// Split the 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 approximately 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 applicable.
if (len % CHUNK_LEN) {
console.log('last ' + len % CHUNK_LEN + ' byte(s)');
dataChannel.send(img.data.subarray(n * CHUNK_LEN));
}
}
接收端會將資料管道訊息位元組轉換為圖片,並向使用者顯示圖片:
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');
// The trail is the element that holds 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);
}
取得程式碼
- 將
work
資料夾的內容替換為step-06
的內容。
「work
」中的「index.html
」檔案現在應如下所示:**
<!DOCTYPE html>
<html>
<head>
<title>Real-time communication with WebRTC</title>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<h1>Real-time communication with WebRTC</h1>
<h2>
<span>Room URL: </span><span id="url">...</span>
</h2>
<div id="videoCanvas">
<video id="camera" autoplay></video>
<canvas id="photo"></canvas>
</div>
<div id="buttons">
<button id="snap">Snap</button><span> then </span><button id="send">Send</button>
<span> or </span>
<button id="snapAndSend">Snap & Send</button>
</div>
<div id="incoming">
<h2>Incoming photos</h2>
<div id="trail"></div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
- 如果您不是從
work
目錄追蹤這個程式碼研究室,您可能需要安裝step-06
資料夾或目前工作資料夾的依附元件。只要從工作目錄執行下列指令即可:
npm install
- 安裝完成後,如果您的 Node.js 伺服器並未執行,請從
work
目錄執行下列指令,以啟動該伺服器:
node index.js
Make sure that you're using the version of `index.js` that implements Socket.IO and
完成變更後,請記得重新啟動 Node.js 伺服器。
如要進一步瞭解 Node 和 Socket.IO,請參閱「設定信號」一節
服務來交換訊息。
- 如有需要,請按一下 [允許],允許應用程式使用網路攝影機。
應用程式會建立隨機房間 ID,並將 ID 加到網址中。
- 在新的瀏覽器分頁或視窗中開啟網址列中的網址。
- 按一下 [Snap &&;;傳送],然後查看頁面底部其他分頁中的 [收到的相片]。
應用程式會在不同的分頁間移動相片。
如下所示:
累計獎勵積分
如何變更程式碼,以共用任何檔案類型?