# webrtc学习

# enumerateDevices 设备管理

navigator.mediaDevices.enumerateDevices():返回一个promise

'use strict'
var audioSource  = document.querySelector("select#audioSource");
var audioOutput  = document.querySelector("select#audioOutput");
var videoSource  = document.querySelector("select#videoSource");

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

function gotDevices(deviceInfos){
	deviceInfos.forEach( function(deviceInfo){
		console.log(deviceInfo.kind + ": label = " 
				+ deviceInfo.label + ": id = "
				+ deviceInfo.deviceId + ": groupId = "
				+ deviceInfo.groupId);	
		var option = document.createElement('option');
		option.text = deviceInfo.label;
		option.value = deviceInfo.deviceId;
		if(deviceInfo.kind === 'audioinput'){
			audioSource.appendChild(option);
		}else if(deviceInfo.kind === 'audiooutput'){
			audioOutput.appendChild(option);
		}else if(deviceInfo.kind === 'videoinput'){
			videoSource.appendChild(option);
		}
	});

}

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

# 音视频数据采集navigator.mediaDevices.getUserMedia

navigator.mediaDevices.getUserMedia(constraints):返回一个promise

//video,audio的配置可以是boolean也可以是配置
var constraints = {
	video : {
		width: 640,	
		height: 480,
		frameRate:15,
		facingMode: 'enviroment',
		deviceId : deviceId ? {exact:deviceId} : undefined 
	}, 
	audio : false 
}


navigator.mediaDevices.getUserMedia(constraints)
	.then(gotMediaStream)
	.then(gotDevices)
	.catch(handleError);
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<!-- 自动播放,不使用其他框架播放 -->
		<video autoplay playsinline id="player"></video>
	</body>
</html>
<script type="text/javascript">
	let player = document.querySelector("#player")
	function  success(stream){
		window.stream = stream;
		//给标签附上src的地址流
		player.srcObject=stream
	}
	function err(e){
		console.log(e)
	}
	if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){
		console.log("not support")
	}else{
		let container={
			video:true,
			audio:true
		}
		navigator.mediaDevices.getUserMedia(container)
							.then(success)
							.catch(err)
	}
</script>

本机是联想电脑测试,不知道什么时候开启了隐私模式,看不到视频,也不提示。

# webrtc浏览器设配

# 自己实现

navigator.getUserMedia  = navigator.getUserMedia ||
                          navigator.webkitGetUserMedia ||
                          navigator.mozGetUserMedia ||
                          navigator.msGetUserMedia;

if (navigator.getUserMedia) {
    // 支持
} else {
    // 不支持
}

# adapter.js适配

# webrtc权限控制

各个浏览器对于权限的控制是不一样的,设备名称等信息,谷歌浏览器某些版本可以支持取得,而苹果和火狐则需要在获取允许摄像头音频后才可以得到。

function gotDevices(deviceInfos){

	deviceInfos.forEach(function(deviceinfo){

		var option = document.createElement('option');
		option.text = deviceinfo.label;
		option.value = deviceinfo.deviceId;
	
		if(deviceinfo.kind === 'audioinput'){
			audioSource.appendChild(option);
		}else if(deviceinfo.kind === 'audiooutput'){
			audioOutput.appendChild(option);
		}else if(deviceinfo.kind === 'videoinput'){
			videoSource.appendChild(option);
		}
	})
}

function gotMediaStream(stream){

	var videoTrack = stream.getVideoTracks()[0];
	var videoConstraints = videoTrack.getSettings();
	
	divConstraints.textContent = JSON.stringify(videoConstraints, null, 2);

	window.stream = stream;
	videoplay.srcObject = stream;

	//audioplay.srcObject = stream;
	return navigator.mediaDevices.enumerateDevices();
}

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

# 视频约束

  • width
  • height
  • aspectRatio 一般不用
  • frameRate 帧率
  • facingMode 摄像头
    • user 前置摄像头
    • environment 后置
    • left 前左
    • right 前右
  • resizeMode 是否裁剪
let container={
		video:{
			width:600,
			height:400,
			frameRate:40
		},
		audio:true
	}

# 音频约束

  • colume 音量[0,1]
  • sampleRate 采样率
  • sampleSize 采样大小
  • echoCancellation 回音消除true和false
  • autoGainControl 在原有的音量上是否增加
  • noiseSuppression 降噪功能true和false
  • latency 延迟设置
  • channelCount 声道
  • deviceId 切换摄像头
  • groupId

# 浏览器视频特效

CSS filter 关联video和filter:可以设置一个select选择样式,然后改变css即可

# 从视频中获取图片【小功能】

利用canvas功能绘制出一帧:截图功能,【视频会议】中出现的重要内容需要截取

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<video autoplay playsinline id="player"></video>
		<button id="btn">截图</button>
		<canvas id="canvas"></canvas>
	</body>
</html>
<!-- <script src="https://cdn.bootcdn.net/ajax/libs/adapterjs/0.15.4/adapter.min.js"></script> -->
<script type="text/javascript">
	let player = document.querySelector("#player")
	let canvas=document.querySelector("#canvas")
	canvas.width=400;
	canvas.height=200
	let btn=document.querySelector("#btn")
	function  success(stream){
		window.stream = stream;
		player.srcObject=stream
	}
	function err(e){
		console.log(e)
	}
	if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){
		console.log("not support")
	}else{
		let container={
			video:{
				width:600,
				height:400,
				frameRate:40
			},
			audio:true
		}
		navigator.mediaDevices.getUserMedia(container)
							.then(success)
							.catch(err)
	}
	btn.onclick=function(){
		canvas.getContext("2d").drawImage(player,0,0,canvas.width,canvas.height)
	}
</script>

# mediastream的方法和事件

  • MediaStream.addTrack() 添加轨
  • MediaStream.removeTrack() 移除轨
  • MediaStream.getVideoTracks() 获取所有视频轨
  • MediaStream.getAudioTracks() 获取所有音频轨
  • MediaStream.onaddtrack
  • MediaStream.onremovetrack
  • MediaStream.onended
//可以获取视频约束的具体值
var videoTrack = stream.getVideoTracks()[0];
//获取设置
var videoConstraints = videoTrack.getSettings();
divConstraints.textContent = JSON.stringify(videoConstraints, null, 2);

window.stream = stream;
videoplay.srcObject = stream;

# MediaRecoder

var mediaRecoder =new MediaRecoder(strem[,options]);
  • stream:媒体流,可从getUserMedia,video,audio,canvas获取
  • options:限制选项

# MideaRecoder API

  • MideaRecoder.start(timeslice)开始录制视频,timeslice可选,设置了会按时间切片存储
  • MideaRecoder.stop()停止录制,会触发包括最终Blob数据的dataavailable事件
  • MideaRecoder.pasue()
  • MideaRecoder.resume()
  • MideaRecoder.isTypeSupported()
  • MideaRecoder.ondataavilable数据有效触发
  • MideaRecoder.onerror

在js中的存储:字符串、blob、arraybuffer、arraybufferview

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<video autoplay playsinline id="player"></video>
		<video id="record"></video>	
		<button id="go" class="start">start</button>
		<button class="play">play</button>
		<button class="downing">downing</button>
	</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/adapterjs/0.15.4/adapter.min.js"></script>
<script type="text/javascript">
	let player = document.querySelector("#player")
	let record =document.querySelector("#record");
	let go =document.querySelector("#go");
	let play =document.querySelector(".play");
	let downing =document.querySelector(".downing");
	let buffer;
	let mediaRecorder;
	
	function  success(stream){
		window.stream = stream;
		player.srcObject=stream
	}
	function err(e){
		console.log(e)
	}
	if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){
		console.log("not support")
	}else{
		let container={
			video:{
				width:600,
				height:400,
				frameRate:40
			},
			audio:true
		}
		navigator.mediaDevices.getUserMedia(container)
							.then(success)
							.catch(err)
	}
	go.onclick=function(){
		if(this.className=="start"){
			this.className="stop";
			start()
		}else{
			this.className="start"
			stop()
		}
	}
    function start(){
		buffer = [];
		let options={
			mimeType:"video/webm"
		}
		if(!MediaRecorder.isTypeSupported(options.mimeType)){
			console.error(`${options.mimeType} is not supported!`);
			return;	
		}
		try{
			mediaRecorder=new MediaRecorder(window.stream,options)
		}catch(e){
			console.error("fail",e)
			return 
		}
		
		mediaRecorder.ondataavailable = handleDataAvailable;
		mediaRecorder.start(10);
		
	}
	function stop(){
		mediaRecorder.stop()
	}
	
	function handleDataAvailable(e){
		console.log(e.data)
		if(e&&e.data&&e.data.size){
			buffer.push(e.data)
		}
	}
	play.onclick=function(){
		var blob = new Blob(buffer, {type: 'video/webm'});
		record.src = window.URL.createObjectURL(blob);
		record.srcObject = null;
		record.controls = true;
		record.play();
	}
	downing.onclick=()=>{
		var blob = new Blob(buffer, {type: 'video/webm'});
		var url = window.URL.createObjectURL(blob);
		var a = document.createElement('a');
		
		a.href = url;
		a.style.display = 'none';
		a.download = 'aaa.webm';
		a.click();
	}
	
	
</script>

# blob

Blob() 构造函数返回一个新的 Blob 对象。 blob的内容由参数数组中给出的值的串联组成

var aBlob = new Blob( array, options );
  • array 是一个由ArrayBuffer, ArrayBufferView, Blob, DOMString 等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob。DOMStrings会被编码为UTF-8。
  • options 是一个可选的BlobPropertyBag字典,它可能会指定如下两个属性:
    • type,默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型。
    • endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。 它是以下两个值中的一个: "native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持blob中保存的结束符不变

# URL.createObjectURL()

URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。

# srcObject

HTMLMediaElement 接口的 srcObject 属性设定或返回一个对象,这个对象提供了一个与HTMLMediaElement关联的媒体源,这个对象通常是 MediaStream ,但根据规范可以是 MediaSource, Blob 或者 File。这个名称可能有兼容性问题,需要适配。

var mediaStream = HTMLMediaElement.srcObject
HTMLMediaElement.srcObject = mediaStream

# getDisplayMedia 录制桌面

目前是实验性功能在谷歌浏览器需在地址栏打开chrome://flags/#enable-experimental-web-platform-features选择true,其他的和getUserMedia一致

var promise=navigator.mediaDevices.getUserMedia(constraints)
最后更新: 6/1/2020, 10:33:16 AM