webRTC

webRTC的愿景是希望通過開發(fā)瀏覽器的程序,來實現(xiàn)音視頻應(yīng)用

https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API
谷歌webRTC demo:https://appr.tc/

一柏腻、安裝web服務(wù)器

1. 安裝nodejs

提供web服務(wù)

yum install nodejs -y
node -v

2. 安裝npm

包管理器

yum install npm -y
npm -v

3. nodejs 實現(xiàn)最簡單的http服務(wù)

  • require引入http模塊;類似于java里的import
  • 創(chuàng)建http服務(wù)
  • 監(jiān)聽端口
mkdir -p /opt/aladdin/nodejs && cd  mkdir -p /opt/aladdin/nodejs
vim server.js
'use strict' #使用js最嚴格的語法膨俐,防止js的語法漏洞
var http = require('http'); //引入http模塊
var app = http.createServer(function( req , res ){
  res.writeHead(200,{'Content-Type':'text/plain'});  //設(shè)置返回的請求頭
  res.end('hello world\n'); //body
}).listen(8080,'0.0.0.0');  //用http模塊創(chuàng)建一個http服務(wù)抬吟,并打開監(jiān)聽(0.0.0.0表示所有網(wǎng)卡都綁定8080端口并監(jiān)聽)

最后啟動服務(wù)

#前臺運行
node server.js
#后臺運行一
nohub node app.js &
#后臺運行二
npm install forever -g #-g forever 這個命令在整個環(huán)境中都生效弛针;否則只是當(dāng)前目錄下生效
forever start server.js #forever stop server.js

瀏覽器訪問

http://服務(wù)器ip:8080

4. https

chrome對個人隱私要求嚴格,不配置https不準使用麥克風(fēng)/攝像頭
http協(xié)議本身的內(nèi)容是明文的铸董,經(jīng)過TLS/SSL加密祟印,并經(jīng)過第三方認證,最后 傳輸
HTTPS=HTTP+TLS/SSL

image.png

5. nodejs搭建https服務(wù)

生成證書

  • 私有證書: 我們自己生成的粟害,瀏覽器不認可
  • 認證證書: 第三方機構(gòu)認證頒發(fā)
mkdir -p /opt/aladdin/nodejs/cert && cd  mkdir -p /opt/aladdin/nodejs/cert

然后將證書拷貝到此目錄下
*.key 是證書的key蕴忆;
*.pem 是證書 ;

vim ../https_server.js
'use strict' #使用js最嚴格的語法,防止js的語法漏洞
var https = require('https'); //引入https模塊
var filesystem= require('fs'); //引入文件模塊悲幅,用于讀取證書 
var options= { //類似于json格式
  key : fs.readFileSync('./cert/155467_www.xxx.com.key'),
  cert : fs.readFileSync('./cert/155467_www.xxx.com.pem')
}
var app = https.createServer(options,function( req , res )){ //options參數(shù)是證書
  res.writeHead(200,{'Content-Type':'text/plain'});  //設(shè)置返回的請求頭
  res.end('hello world\n'); //body
}).listen(443,'0.0.0.0');  //用http模塊創(chuàng)建一個http服務(wù)套鹅,并打開監(jiān)聽(0.0.0.0表示所有網(wǎng)卡都綁定443端口并監(jiān)聽)

域名訪問

https://xxxx.com

6. 真正的web服務(wù)

  • 引入express模塊【nodejs里專門處理web服務(wù)的,里面有很多功能】
  • serve-index模塊【將整個目錄里發(fā)布出來汰具,這個目錄里的文件都共享出來卓鹿,可以直接通過瀏覽器瀏覽】
  • 指定發(fā)布目錄
npm install express
npm install serve-index
mkdir -p /opt/aladdin/nodejs/webserver && cd  mkdir -p /opt/aladdin/nodejs/webserver
vim webserver.js
'use strict'
var http = require('http'); //既支持http

var https = require('https'); //也支持https

var filesystem= require('fs'); 

var express = require('express');

var serverIndex= require('serve-index');

var app= express(); #創(chuàng)建一個對象,實例化express模塊
app.use(express.static('./public')); #發(fā)布靜態(tài)資源的路徑
app.usr(serverIndex('./public')); #瀏覽發(fā)布路徑的文件

//http
var http_server = https.createServer(app);
http_server.listen(80, '0.0.0.0')

var options= { 
  key : fs.readFileSync('../cert/155467_www.xxx.com.key'),
  cert : fs.readFileSync('../cert/155467_www.xxx.com.pem')
}

//https
var https_server = https.createServer(options,app);
https_server.listen(443 , '0.0.0.0')

二留荔、回顧JavaScript基礎(chǔ)知識

image.png

1. 變量與類型

image.png

image.png

2. 基本運算

image.png

image.png

3. if else

image.png

image.png

image.png

4. 循環(huán)

image.png

image.png

5. 函數(shù)

image.png

image.png

三吟孙、 webRTC設(shè)備管理

1. enumerateDevices

通過這個api 能獲取到電腦的音頻/視頻設(shè)備

Promise是js中特有的對象
Promise中有一個重要的結(jié)構(gòu)體MediaDevicesInfo

image.png

背景:
JavaScript他是用單線程去處理整個邏輯,所以防止它被阻塞,大量使用了異步調(diào)用杰妓,Promise就是異步調(diào)用其中的一種方式
它的基本思想就是: 首先你在創(chuàng)建Promise的時候藻治,要傳給它一個handle函數(shù),這個handle處理你的主要邏輯巷挥,那么處理完成之后桩卵,如果成功了,它就會調(diào)resolve這個函數(shù)倍宾,如果失敗了它就調(diào)reject這個函數(shù)吸占,這樣就創(chuàng)建好了一個Promise;
Promise可以注冊兩個方法凿宾,一個是通過then矾屯,一個是通過catch;then就是當(dāng)整個邏輯處理成功之后就會收到on_reolve事件初厚,當(dāng)收到事件的時候可以處理一些邏輯件蚕;catch就是當(dāng)失敗的時候收到on_reject處理一些失敗的邏輯;
then成功的時候還可以返回一個Promise产禾,你可以繼續(xù)then排作。。亚情⊥荆【鏈式】


image.png

回到

var ePromise= navigator.mediaDevices.enumerateDevices();

在enumerateDevices()這個函數(shù)里它就new了一個Promise,中間給它注冊了一個handle楞件, 所以當(dāng)這個函數(shù)執(zhí)行的時候衫生,它就返回一個Promise,在我們用的時候土浸,拿到這個Promise罪针,我們就給它注冊兩個函數(shù),一個是then的方法黄伊,一個是catch的方法泪酱,如果成功調(diào)用then的方法做成功的邏輯,如果失敗了調(diào)catch

2. 獲取用戶音頻/視頻設(shè)備實戰(zhàn)

#首先進入上面發(fā)布的目錄还最,在目錄下寫我們的程序墓阀,這樣當(dāng)我們寫完它就發(fā)布出來了寒屯,我們通過瀏覽器就能看到相應(yīng)的結(jié)果
cd  /opt/aladdin/nodejs/webserver/public 
#建一個子目錄
mkdir device && cd device
vim index.html
<html>
  <head>
    <title>WebRTC get autio and video device</title>
  </head>

  <body>
    <script src="./js/client.js"></script> <!--引入我們js代碼乱陡,這樣當(dāng)我們打開頁面的時候js代碼就會執(zhí)行,chrome瀏覽器會把它交到底層的v8引擎解析渲染-->
  </body>
</html>
mkdir js && cd js
vim client.js
'use strict'

#首先看瀏覽器是否支持我們的方法(方法是否存在)芬首,支持則調(diào)用悦即,不支持報錯 
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices()) {
  console.log('enumerateDevices is not supported!');
}else{
  navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
}

function gotDevices(devicesInfos){
  devicesInfos.forEach(function(devicesInfo){
    console.log(devicesInfo.kind+":"+devicesInfo.label+":"+ devicesInfo.deviceId+":"+devicesInfo.groupId);
  });
}

function handleError(err){
  console.log(err.name+":"+err.message);
}

如果結(jié)果lable為空吮成,則瀏覽器應(yīng)該用https打開橱乱,http是不顯示設(shè)備名稱的

3. 在頁面上顯示設(shè)備

vim index.html
<html>
  <head>
    <title>WebRTC get autio and video device</title>
  </head>

  <body>
    <div>
      <div>
        <lable>audio input device</lable>
        <select id="audioSource"></select>
      </div>  
      <div>
        <lable>audio output device</lable>
        <select id="audioOutput"></select>
      </div>  
      <div>
        <lable>video input device</lable>
        <select id="videoSource"></select>
      </div>  
    </div>  

    <script src="./js/client.js"></script> 
  </body>
</html>
vim ./js/client.js
'use strict'

#首先獲取到音頻輸入設(shè)備(select中的id為audioSource)
var audioSource= document.querySelect("select#audioSource");
var audioOutput= document.querySelect("select#audioOutput");
var videoSource= document.querySelect("select#videoSource");

if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices()) {
  console.log('enumerateDevices is not supported!');
}else{
  navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
}

function gotDevices(devicesInfos){
  devicesInfos.forEach(function(devicesInfo){
    console.log(devicesInfo.kind+":"+devicesInfo.label+":"+ devicesInfo.deviceId+":"+devicesInfo.groupId);
    
    var option = document.createElement('option');
    option.text= devicesInfo.label;
    option.value= devicesInfo.deviceId;

    if(devicesInfo.kind === 'audioinput'){
      audioSource.appendChild(option);
    }else if(devicesInfo.kind === 'audiooutput'){
      audioOutput.appendChild(option);
    }else if(devicesInfo.kind === 'videoinput'){
      videoSource.appendChild(option);    
    }
  });
}

function handleError(err){
  console.log(err.name+":"+err.message);
}

4. 不同瀏覽器運行之間的差別

image.png

四、 音視頻采集API

image.png
mkdir -p /opt/aladdin/nodejs/webserver/public && cd  mkdir -p /opt/aladdin/nodejs/webserver/public 
mkdir mediastream && cd mediastream

一個流(stream)可以包括多個軌(track)
每一條媒體軌就是一種媒體數(shù)據(jù)(音頻/視頻)粱甫,可以有多個音頻/視頻組成一個stream

vim index.html
<html>
  <head>
    <title>WebRTC capture video and audio</title>
  </head>

  <body>
    <video autoplay playsinline id="player"></video> <!--html5的標簽泳叠,可以顯示我們捕獲的音頻數(shù)據(jù) ;autoplay屬性為playsinline 表示在頁面中播放-->
    <script src="./js/client.js"></script> <!-- js代碼用于捕獲音頻數(shù)據(jù)-->
  </body>
</html>
vim ./js/client.js
'use strict'

if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia()) {
  console.log('getUserMedia is not supported!');
}else{
  var constrants:{
    #同時采集音頻和視頻數(shù)據(jù)
    video =true,
    audio=true
  }
  navigator.mediaDevices.getUserMedia(constrants).then(gotMediaStream).catch(handleError);
}

var videoplay=document.querySelector('video#player')

function gotMediaStream(stream){
   #指定數(shù)據(jù)源
  videoplay.srcObject=stream;
}

function handleError(err){
  console.log(err.name+":"+err.message);
}

1. getUserMedia適配

各個瀏覽器廠商對getUserMedia起的名字是不一樣的


image.png

image.png
cd /opt/aladdin/nodejs/webserver/public/mediastream
vim index.html
<html>
  <head>
    <title>WebRTC capture video and audio</title>
  </head>

  <body>
    <video autoplay playsinline id="player"></video> 

    <script src="https://webrtc. github.io/adapter/adapter-latest.js"></script> <!--適配 -->
    <script src="./js/client.js"></script> 
  </body>
</html>

2. 獲取訪問音頻/適配設(shè)備的權(quán)限

為解決之前獲取設(shè)備不同瀏覽器之間有差異問題:不同瀏覽器對權(quán)限獲取的實現(xiàn)不同茶宵,導(dǎo)致有的能獲取到設(shè)備危纫,有的不能;

cd /opt/aladdin/nodejs/webserver/public/mediastream
vim index.html
image.png
vim ./js/client.js
'use strict'

if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia()) {
  console.log('getUserMedia is not supported!');
}else{
  var constrants:{
    video =true,
    audio=true
  }

#因為下面  gotMediaStream()方法返回了一個Promise乌庶,所以我們還可以繼續(xù)進行then操作种蝶,去獲取設(shè)備
navigator.mediaDevices.getUserMedia(constrants).then(gotDevices).then(gotMediaStream).catch(handleError);
}

#獲取標簽
var audioSource=document.querySelector('select#audioSource');
var audioOutput=document.querySelector('select#audioOutput');
var videSource=document.querySelector('select#videoSource');

var videoplay=document.querySelector('video#player');

#當(dāng)我拿到這個流,說明用戶已經(jīng)同意拿到音頻/視頻設(shè)備了
function gotMediaStream(stream){
  videoplay.srcObject=stream;
  #相當(dāng)于將Promise返回回去瞒大。用于繼續(xù)使用Promise的鏈式調(diào)用螃征,繼續(xù)then()
  return avigator.mediaDevices.enumerateDevices();
}

function handleError(err){
  console.log(err.name+":"+err.message);
}

#實現(xiàn)一下獲取設(shè)備后的操作
function gotDevices(devicesInfos){
  devicesInfos.forEach(function(devicesInfo){
    console.log(devicesInfo.kind+":"+devicesInfo.label+":"+ devicesInfo.deviceId+":"+devicesInfo.groupId);
    
    var option = document.createElement('option');
    option.text= devicesInfo.label;
    option.value= devicesInfo.deviceId;

    if(devicesInfo.kind === 'audioinput'){
      audioSource.appendChild(option);
    }else if(devicesInfo.kind === 'audiooutput'){
      audioOutput.appendChild(option);
    }else if(devicesInfo.kind === 'videoinput'){
      videoSource.appendChild(option);    
    }
  });
}

3. 音頻/視頻采集約束

  • 視頻

    設(shè)置具體值

    設(shè)置范圍

  • 音頻

    image.png

4. 切換采集設(shè)備

vim ./js/client.js
'use strict'

function(){ #將這一段設(shè)為一個函數(shù)
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia()) {
  console.log('getUserMedia is not supported!');
  return; #出錯直接返回
}else{
  var deviceId=videoSource.value; #拿到設(shè)備的id
  var constrants:{
    video =true,
    audio=true
  }
  deviceId: deviceId?deviceId:undefind #deviceId為空,則為undefind透敌;不為空則賦值

navigator.mediaDevices.getUserMedia(constrants).then(gotDevices).then(gotMediaStream).catch(handleError);
}
}

#當(dāng)頁面進來就調(diào)用這個函數(shù)
start();

#增加一個事件:當(dāng)我們選擇攝像頭(標簽)的時候盯滚,可以觸發(fā)onchange事件,調(diào)用start()函數(shù)酗电,重新進行初始化 
videSource.onchange=start();

var audioSource=document.querySelector('select#audioSource');
var audioOutput=document.querySelector('select#audioOutput');
var videSource=document.querySelector('select#videoSource');

var videoplay=document.querySelector('video#player');

function gotMediaStream(stream){
  videoplay.srcObject=stream;
  return avigator.mediaDevices.enumerateDevices();
}

function handleError(err){
  console.log(err.name+":"+err.message);
}

function gotDevices(devicesInfos){
  devicesInfos.forEach(function(devicesInfo){
    console.log(devicesInfo.kind+":"+devicesInfo.label+":"+ devicesInfo.deviceId+":"+devicesInfo.groupId);
    
    var option = document.createElement('option');
    option.text= devicesInfo.label;
    option.value= devicesInfo.deviceId;

    if(devicesInfo.kind === 'audioinput'){
      audioSource.appendChild(option);
    }else if(devicesInfo.kind === 'audiooutput'){
      audioOutput.appendChild(option);
    }else if(devicesInfo.kind === 'videoinput'){
      videoSource.appendChild(option);    
    }
  });
}

5. 瀏覽器視頻特效


image.png
image.png
image.png

image.png

image.png

6. 從視頻中獲取圖片

image.png

image.png

image.png

7. 只采集音頻

image.png

image.png

image.png

image.png

五魄藕、 MediaStream

image.png

image.png

image.png

image.png

image.png

六、 WebRTC錄制媒體流

就是錄制上面navigator.mediaDevices.getUserMedia()采集的數(shù)據(jù)


image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png
image.png

image.png

image.png

image.png

image.png

1. WebRTC捕獲桌面

image.png

image.png
vim ./js/client.js

將所有g(shù)etUserMedia換為getDisplayMedia就ok 了


七撵术、socket.io

1. 使用socket.io發(fā)送消息

image.png

image.png

image.png

image.png

2. WebRTC信令服務(wù)器

image.png

image.png

image.png

3. 通過socket.io實現(xiàn)信令服務(wù)器

image.png
vim server.js
image.png
image.png

image.png

日志


image.png

image.png

image.png

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末背率,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嫩与,更是在濱河造成了極大的恐慌寝姿,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕴纳,死亡現(xiàn)場離奇詭異会油,居然都是意外死亡个粱,警方通過查閱死者的電腦和手機古毛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來都许,“玉大人稻薇,你說我怎么就攤上這事〗赫鳎” “怎么了塞椎?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長睛低。 經(jīng)常有香客問我案狠,道長服傍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任骂铁,我火速辦了婚禮吹零,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拉庵。我一直安慰自己灿椅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布钞支。 她就那樣靜靜地躺著茫蛹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烁挟。 梳的紋絲不亂的頭發(fā)上婴洼,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音撼嗓,去河邊找鬼窃蹋。 笑死,一個胖子當(dāng)著我的面吹牛静稻,可吹牛的內(nèi)容都是我干的警没。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼振湾,長吁一口氣:“原來是場噩夢啊……” “哼杀迹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起押搪,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤树酪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后大州,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體续语,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年厦画,在試婚紗的時候發(fā)現(xiàn)自己被綠了疮茄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡根暑,死狀恐怖力试,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情排嫌,我是刑警寧澤畸裳,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站淳地,受9級特大地震影響怖糊,放射性物質(zhì)發(fā)生泄漏帅容。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一伍伤、第九天 我趴在偏房一處隱蔽的房頂上張望丰嘉。 院中可真熱鬧,春花似錦嚷缭、人聲如沸饮亏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽路幸。三九已至,卻和暖如春付翁,著一層夾襖步出監(jiān)牢的瞬間简肴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工百侧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留砰识,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓佣渴,卻偏偏與公主長得像辫狼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辛润,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容

  • WebRTC 學(xué)習(xí)報告 O_禾火_O[http://www.reibang.com/u/44dd44d945dd...
    小杰的簡書閱讀 3,035評論 1 1
  • 前言 本片demo中 的實現(xiàn)為純前端js實現(xiàn),所以 而且可以在本機下運行(有網(wǎng)絡(luò)即可正常鏈接服務(wù)器),所以你可以修...
    MeIsLZHua閱讀 8,744評論 14 22
  • 一膨处、 項目簡介 webrtc是近幾年興起的一種流媒體通信框架。其目的是實現(xiàn)一套web瀏覽器無插件的流媒體框架砂竖。它可...
    wonder912閱讀 5,717評論 0 1
  • 久違的晴天真椿,家長會。 家長大會開好到教室時乎澄,離放學(xué)已經(jīng)沒多少時間了突硝。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,522評論 16 22
  • 今天感恩節(jié)哎置济,感謝一直在我身邊的親朋好友解恰。感恩相遇!感恩不離不棄舟肉。 中午開了第一次的黨會修噪,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,562評論 0 11