webRTC

webRTC的愿景是希望通过开发浏览器的程序,来实现音视频应用

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

一、安装web服务器

1. 安装nodejs

提供web服务

yum install nodejs -y
node -v

2. 安装npm

包管理器

yum install npm -y
npm -v

3. nodejs 实现最简单的http服务

  • require引入http模块;类似于java里的import
  • 创建http服务
  • 监听端口
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'});  //设置返回的请求头
  res.end('hello world\n'); //body
}).listen(8080,'0.0.0.0');  //用http模块创建一个http服务,并打开监听(0.0.0.0表示所有网卡都绑定8080端口并监听)

最后启动服务

#前台运行
node server.js
#后台运行一
nohub node app.js &
#后台运行二
npm install forever -g #-g forever 这个命令在整个环境中都生效;否则只是当前目录下生效
forever start server.js #forever stop server.js

浏览器访问

http://服务器ip:8080

4. https

chrome对个人隐私要求严格,不配置https不准使用麦克风/摄像头
http协议本身的内容是明文的,经过TLS/SSL加密,并经过第三方认证,最后 传输
HTTPS=HTTP+TLS/SSL

webRTC_第1张图片
image.png

5. nodejs搭建https服务

生成证书

  • 私有证书: 我们自己生成的,浏览器不认可
  • 认证证书: 第三方机构认证颁发
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参数是证书
  res.writeHead(200,{'Content-Type':'text/plain'});  //设置返回的请求头
  res.end('hello world\n'); //body
}).listen(443,'0.0.0.0');  //用http模块创建一个http服务,并打开监听(0.0.0.0表示所有网卡都绑定443端口并监听)

域名访问

https://xxxx.com

6. 真正的web服务

  • 引入express模块【nodejs里专门处理web服务的,里面有很多功能】
  • serve-index模块【将整个目录里发布出来,这个目录里的文件都共享出来,可以直接通过浏览器浏览】
  • 指定发布目录
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(); #创建一个对象,实例化express模块
app.use(express.static('./public')); #发布静态资源的路径
app.usr(serverIndex('./public')); #浏览发布路径的文件

//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基础知识

image.png

1. 变量与类型

webRTC_第2张图片
image.png

webRTC_第3张图片
image.png

2. 基本运算

webRTC_第4张图片
image.png

webRTC_第5张图片
image.png

3. if else

webRTC_第6张图片
image.png

webRTC_第7张图片
image.png

webRTC_第8张图片
image.png

4. 循环

webRTC_第9张图片
image.png

webRTC_第10张图片
image.png

5. 函数

webRTC_第11张图片
image.png

webRTC_第12张图片
image.png

三、 webRTC设备管理

1. enumerateDevices

通过这个api 能获取到电脑的音频/视频设备

Promise是js中特有的对象
Promise中有一个重要的结构体MediaDevicesInfo

webRTC_第13张图片
image.png

背景:
JavaScript他是用单线程去处理整个逻辑,所以防止它被阻塞,大量使用了异步调用,Promise就是异步调用其中的一种方式
它的基本思想就是: 首先你在创建Promise的时候,要传给它一个handle函数,这个handle处理你的主要逻辑,那么处理完成之后,如果成功了,它就会调resolve这个函数,如果失败了它就调reject这个函数,这样就创建好了一个Promise;
Promise可以注册两个方法,一个是通过then,一个是通过catch;then就是当整个逻辑处理成功之后就会收到on_reolve事件,当收到事件的时候可以处理一些逻辑;catch就是当失败的时候收到on_reject处理一些失败的逻辑;
then成功的时候还可以返回一个Promise,你可以继续then。。。【链式】


webRTC_第14张图片
image.png

回到

var ePromise= navigator.mediaDevices.enumerateDevices();

在enumerateDevices()这个函数里它就new了一个Promise,中间给它注册了一个handle, 所以当这个函数执行的时候,它就返回一个Promise,在我们用的时候,拿到这个Promise,我们就给它注册两个函数,一个是then的方法,一个是catch的方法,如果成功调用then的方法做成功的逻辑,如果失败了调catch

2. 获取用户音频/视频设备实战

#首先进入上面发布的目录,在目录下写我们的程序,这样当我们写完它就发布出来了,我们通过浏览器就能看到相应的结果
cd  /opt/aladdin/nodejs/webserver/public 
#建一个子目录
mkdir device && cd device
vim index.html

  
    WebRTC get autio and video device
  

  
     
  

mkdir js && cd js
vim client.js
'use strict'

#首先看浏览器是否支持我们的方法(方法是否存在),支持则调用,不支持报错 
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);
}

如果结果lable为空,则浏览器应该用https打开,http是不显示设备名称的

3. 在页面上显示设备

vim index.html

  
    WebRTC get autio and video device
  

  
    
audio input device
audio output device
video input device
vim ./js/client.js
'use strict'

#首先获取到音频输入设备(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. 不同浏览器运行之间的差别

webRTC_第15张图片
image.png

四、 音视频采集API

webRTC_第16张图片
image.png
mkdir -p /opt/aladdin/nodejs/webserver/public && cd  mkdir -p /opt/aladdin/nodejs/webserver/public 
mkdir mediastream && cd mediastream

一个流(stream)可以包括多个轨(track)
每一条媒体轨就是一种媒体数据(音频/视频),可以有多个音频/视频组成一个stream

vim index.html

  
    WebRTC capture video and audio
  

  
     
     
  

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
  }
  navigator.mediaDevices.getUserMedia(constrants).then(gotMediaStream).catch(handleError);
}

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

function gotMediaStream(stream){
   #指定数据源
  videoplay.srcObject=stream;
}

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

1. getUserMedia适配

各个浏览器厂商对getUserMedia起的名字是不一样的


webRTC_第17张图片
image.png

image.png
cd /opt/aladdin/nodejs/webserver/public/mediastream
vim index.html

  
    WebRTC capture video and audio
  

  
     

     
     
  

2. 获取访问音频/适配设备的权限

为解决之前获取设备不同浏览器之间有差异问题:不同浏览器对权限获取的实现不同,导致有的能获取到设备,有的不能;

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,所以我们还可以继续进行then操作,去获取设备
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');

#当我拿到这个流,说明用户已经同意拿到音频/视频设备了
function gotMediaStream(stream){
  videoplay.srcObject=stream;
  #相当于将Promise返回回去。用于继续使用Promise的链式调用,继续then()
  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);    
    }
  });
}

3. 音频/视频采集约束

  • 视频

    设置具体值

    webRTC_第18张图片
    设置范围

  • 音频

    image.png

4. 切换采集设备

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

function(){ #将这一段设为一个函数
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia()) {
  console.log('getUserMedia is not supported!');
  return; #出错直接返回
}else{
  var deviceId=videoSource.value; #拿到设备的id
  var constrants:{
    video =true,
    audio=true
  }
  deviceId: deviceId?deviceId:undefind #deviceId为空,则为undefind;不为空则赋值

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

#当页面进来就调用这个函数
start();

#增加一个事件:当我们选择摄像头(标签)的时候,可以触发onchange事件,调用start()函数,重新进行初始化 
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. 浏览器视频特效

webRTC_第19张图片

image.png
webRTC_第20张图片
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

webRTC_第21张图片
image.png

webRTC_第22张图片
image.png

image.png

image.png

webRTC_第23张图片
image.png

六、 WebRTC录制媒体流

就是录制上面navigator.mediaDevices.getUserMedia()采集的数据


image.png

image.png

image.png

webRTC_第24张图片
image.png

image.png

webRTC_第25张图片
image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

webRTC_第26张图片
image.png
image.png

image.png

webRTC_第27张图片
image.png

webRTC_第28张图片
image.png

image.png

1. WebRTC捕获桌面

webRTC_第29张图片
image.png

image.png
vim ./js/client.js

将所有getUserMedia换为getDisplayMedia就ok 了


七、socket.io

1. 使用socket.io发送消息

webRTC_第30张图片
image.png

webRTC_第31张图片
image.png

image.png

image.png

2. WebRTC信令服务器

webRTC_第32张图片
image.png

webRTC_第33张图片
image.png

image.png

3. 通过socket.io实现信令服务器

webRTC_第34张图片
image.png
vim server.js
image.png
image.png

image.png

日志


image.png

webRTC_第35张图片
image.png

image.png

image.png

你可能感兴趣的:(webRTC)