W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
WebRTC是“網(wǎng)絡(luò)實(shí)時(shí)通信”(Web Real Time Communication)的縮寫。它最初是為了解決瀏覽器上視頻通話而提出的,即兩個(gè)瀏覽器之間直接進(jìn)行視頻和音頻的通信,不經(jīng)過服務(wù)器。后來發(fā)展到除了音頻和視頻,還可以傳輸文字和其他數(shù)據(jù)。
Google是WebRTC的主要支持者和開發(fā)者,它最初在Gmail上推出了視頻聊天,后來在2011年推出了Hangouts,語序在瀏覽器中打電話。它推動(dòng)了WebRTC標(biāo)準(zhǔn)的確立。
WebRTC主要讓瀏覽器具備三個(gè)作用。
WebRTC共分成三個(gè)API,分別對(duì)應(yīng)上面三個(gè)作用。
navigator.getUserMedia方法目前主要用于,在瀏覽器中獲取音頻(通過麥克風(fēng))和視頻(通過攝像頭),將來可以用于獲取任意數(shù)據(jù)流,比如光盤和傳感器。
下面的代碼用于檢查瀏覽器是否支持getUserMedia方法。
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
if (navigator.getUserMedia) {
// 支持
} else {
// 不支持
}
Chrome 21, Opera 18和Firefox 17,支持該方法。目前,IE還不支持,上面代碼中的msGetUserMedia,只是為了確保將來的兼容。
getUserMedia方法接受三個(gè)參數(shù)。
navigator.getUserMedia({
video: true,
audio: true
}, onSuccess, onError);
getUserMedia的第一個(gè)參數(shù)是一個(gè)對(duì)象,表示要獲取哪些多媒體設(shè)備,上面的代碼表示獲取攝像頭和麥克風(fēng);onSuccess是一個(gè)回調(diào)函數(shù),在獲取多媒體設(shè)備成功時(shí)調(diào)用;onError也是一個(gè)回調(diào)函數(shù),在取多媒體設(shè)備失敗時(shí)調(diào)用。
下面是一個(gè)例子。
var constraints = {video: true};
function onSuccess(stream) {
var video = document.querySelector("video");
video.src = window.URL.createObjectURL(stream);
}
function onError(error) {
console.log("navigator.getUserMedia error: ", error);
}
navigator.getUserMedia(constraints, onSuccess, onError);
如果網(wǎng)頁使用了getUserMedia方法,瀏覽器就會(huì)詢問用戶,是否同意瀏覽器調(diào)用麥克風(fēng)或攝像頭。如果用戶同意,就調(diào)用回調(diào)函數(shù)onSuccess;如果用戶拒絕,就調(diào)用回調(diào)函數(shù)onError。
onSuccess回調(diào)函數(shù)的參數(shù)是一個(gè)數(shù)據(jù)流對(duì)象stream。stream.getAudioTracks方法和stream.getVideoTracks方法,分別返回一個(gè)數(shù)組,其成員是數(shù)據(jù)流包含的音軌和視軌(track)。使用的聲音源和攝影頭的數(shù)量,決定音軌和視軌的數(shù)量。比如,如果只使用一個(gè)攝像頭獲取視頻,且不獲取音頻,那么視軌的數(shù)量為1,音軌的數(shù)量為0。每個(gè)音軌和視軌,有一個(gè)kind屬性,表示種類(video或者audio),和一個(gè)label屬性(比如FaceTime HD Camera (Built-in))。
onError回調(diào)函數(shù)接受一個(gè)Error對(duì)象作為參數(shù)。Error對(duì)象的code屬性有如下取值,說明錯(cuò)誤的類型。
下面通過getUserMedia方法,將攝像頭拍攝的圖像展示在網(wǎng)頁上。
首先,需要先在網(wǎng)頁上放置一個(gè)video元素。圖像就展示在這個(gè)元素中。
<video id="webcam"></video>
然后,用代碼獲取這個(gè)元素。
function onSuccess(stream) {
var video = document.getElementById('webcam');
}
接著,將這個(gè)元素的src屬性綁定數(shù)據(jù)流,攝影頭拍攝的圖像就可以顯示了。
function onSuccess(stream) {
var video = document.getElementById('webcam');
if (window.URL) {
video.src = window.URL.createObjectURL(stream);
} else {
video.src = stream;
}
video.autoplay = true;
// 或者 video.play();
}
if (navigator.getUserMedia) {
navigator.getUserMedia({video:true}, onSuccess);
} else {
document.getElementById('webcam').src = 'somevideo.mp4';
}
在Chrome和Opera中,URL.createObjectURL方法將媒體數(shù)據(jù)流(MediaStream)轉(zhuǎn)為一個(gè)二進(jìn)制對(duì)象的URL(Blob URL),該URL可以作為video元素的src屬性的值。 在Firefox中,媒體數(shù)據(jù)流可以直接作為src屬性的值。Chrome和Opera還允許getUserMedia獲取的音頻數(shù)據(jù),直接作為audio或者video元素的值,也就是說如果還獲取了音頻,上面代碼播放出來的視頻是有聲音的。
獲取攝像頭的主要用途之一,是讓用戶使用攝影頭為自己拍照。Canvas API有一個(gè)ctx.drawImage(video, 0, 0)方法,可以將視頻的一個(gè)幀轉(zhuǎn)為canvas元素。這使得截屏變得非常容易。
<video autoplay></video>
<img src="">
<canvas style="display:none;"></canvas>
<script>
var video = document.querySelector('video');
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var localMediaStream = null;
function snapshot() {
if (localMediaStream) {
ctx.drawImage(video, 0, 0);
// “image/webp”對(duì)Chrome有效,
// 其他瀏覽器自動(dòng)降為image/png
document.querySelector('img').src = canvas.toDataURL('image/webp');
}
}
video.addEventListener('click', snapshot, false);
navigator.getUserMedia({video: true}, function(stream) {
video.src = window.URL.createObjectURL(stream);
localMediaStream = stream;
}, errorCallback);
</script>
通過瀏覽器捕獲聲音,需要借助Web Audio API。
window.AudioContext = window.AudioContext ||
window.webkitAudioContext;
var context = new AudioContext();
function onSuccess(stream) {
var audioInput = context.createMediaStreamSource(stream);
audioInput.connect(context.destination);
}
navigator.getUserMedia({audio:true}, onSuccess);
getUserMedia方法的第一個(gè)參數(shù),除了指定捕獲對(duì)象之外,還可以指定一些限制條件,比如限定只能錄制高清(或者VGA標(biāo)準(zhǔn))的視頻。
var hdConstraints = {
video: {
mandatory: {
minWidth: 1280,
minHeight: 720
}
}
};
navigator.getUserMedia(hdConstraints, onSuccess, onError);
var vgaConstraints = {
video: {
mandatory: {
maxWidth: 640,
maxHeight: 360
}
}
};
navigator.getUserMedia(vgaConstraints, onSuccess, onError);
如果本機(jī)有多個(gè)攝像頭/麥克風(fēng),這時(shí)就需要使用MediaStreamTrack.getSources方法指定,到底使用哪一個(gè)攝像頭/麥克風(fēng)。
MediaStreamTrack.getSources(function(sourceInfos) {
var audioSource = null;
var videoSource = null;
for (var i = 0; i != sourceInfos.length; ++i) {
var sourceInfo = sourceInfos[i];
if (sourceInfo.kind === 'audio') {
console.log(sourceInfo.id, sourceInfo.label || 'microphone');
audioSource = sourceInfo.id;
} else if (sourceInfo.kind === 'video') {
console.log(sourceInfo.id, sourceInfo.label || 'camera');
videoSource = sourceInfo.id;
} else {
console.log('Some other kind of source: ', sourceInfo);
}
}
sourceSelected(audioSource, videoSource);
});
function sourceSelected(audioSource, videoSource) {
var constraints = {
audio: {
optional: [{sourceId: audioSource}]
},
video: {
optional: [{sourceId: videoSource}]
}
};
navigator.getUserMedia(constraints, onSuccess, onError);
}
上面代碼表示,MediaStreamTrack.getSources方法的回調(diào)函數(shù),可以得到一個(gè)本機(jī)的攝像頭和麥克風(fēng)的列表,然后指定使用最后一個(gè)攝像頭和麥克風(fēng)。
RTCPeerConnection的作用是在瀏覽器之間建立數(shù)據(jù)的“點(diǎn)對(duì)點(diǎn)”(peer to peer)通信,也就是將瀏覽器獲取的麥克風(fēng)或攝像頭數(shù)據(jù),傳播給另一個(gè)瀏覽器。這里面包含了很多復(fù)雜的工作,比如信號(hào)處理、多媒體編碼/解碼、點(diǎn)對(duì)點(diǎn)通信、數(shù)據(jù)安全、帶寬管理等等。
不同客戶端之間的音頻/視頻傳遞,是不用通過服務(wù)器的。但是,兩個(gè)客戶端之間建立聯(lián)系,需要通過服務(wù)器。服務(wù)器主要轉(zhuǎn)遞兩種數(shù)據(jù)。
WebRTC協(xié)議沒有規(guī)定與服務(wù)器的通信方式,因此可以采用各種方式,比如WebSocket。通過服務(wù)器,兩個(gè)客戶端按照Session Description Protocol(SDP協(xié)議)交換雙方的元數(shù)據(jù)。
下面是一個(gè)示例。
var signalingChannel = createSignalingChannel();
var pc;
var configuration = ...;
// run start(true) to initiate a call
function start(isCaller) {
pc = new RTCPeerConnection(configuration);
// send any ice candidates to the other peer
pc.onicecandidate = function (evt) {
signalingChannel.send(JSON.stringify({ "candidate": evt.candidate }));
};
// once remote stream arrives, show it in the remote video element
pc.onaddstream = function (evt) {
remoteView.src = URL.createObjectURL(evt.stream);
};
// get the local stream, show it in the local video element and send it
navigator.getUserMedia({ "audio": true, "video": true }, function (stream) {
selfView.src = URL.createObjectURL(stream);
pc.addStream(stream);
if (isCaller)
pc.createOffer(gotDescription);
else
pc.createAnswer(pc.remoteDescription, gotDescription);
function gotDescription(desc) {
pc.setLocalDescription(desc);
signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
});
}
signalingChannel.onmessage = function (evt) {
if (!pc)
start(false);
var signal = JSON.parse(evt.data);
if (signal.sdp)
pc.setRemoteDescription(new RTCSessionDescription(signal.sdp));
else
pc.addIceCandidate(new RTCIceCandidate(signal.candidate));
};
RTCPeerConnection帶有瀏覽器前綴,Chrome瀏覽器中為webkitRTCPeerConnection,F(xiàn)irefox瀏覽器中為mozRTCPeerConnection。Google維護(hù)一個(gè)函數(shù)庫adapter.js,用來抽象掉瀏覽器之間的差異。
RTCDataChannel的作用是在點(diǎn)對(duì)點(diǎn)之間,傳播任意數(shù)據(jù)。它的API與WebSockets的API相同。
下面是一個(gè)示例。
var pc = new webkitRTCPeerConnection(servers,
{optional: [{RtpDataChannels: true}]});
pc.ondatachannel = function(event) {
receiveChannel = event.channel;
receiveChannel.onmessage = function(event){
document.querySelector("div#receive").innerHTML = event.data;
};
};
sendChannel = pc.createDataChannel("sendDataChannel", {reliable: false});
document.querySelector("button#send").onclick = function (){
var data = document.querySelector("textarea#send").value;
sendChannel.send(data);
};
Chrome 25、Opera 18和Firefox 22支持RTCDataChannel。
由于這兩個(gè)API比較復(fù)雜,一般采用外部函數(shù)庫進(jìn)行操作。目前,視頻聊天的函數(shù)庫有SimpleWebRTC、easyRTC、webRTC.io,點(diǎn)對(duì)點(diǎn)通信的函數(shù)庫有PeerJS、Sharefest。
下面是SimpleWebRTC的示例。
var webrtc = new WebRTC({
localVideoEl: 'localVideo',
remoteVideosEl: 'remoteVideos',
autoRequestMedia: true
});
webrtc.on('readyToCall', function () {
webrtc.joinRoom('My room name');
});
下面是PeerJS的示例。
var peer = new Peer('someid', {key: 'apikey'});
peer.on('connection', function(conn) {
conn.on('data', function(data){
// Will print 'hi!'
console.log(data);
});
});
// Connecting peer
var peer = new Peer('anotherid', {key: 'apikey'});
var conn = peer.connect('someid');
conn.on('open', function(){
conn.send('hi!');
});
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: