关于webRTC中video的使用实践
此次demo使用chrome49调试测试
前端在操作视频输入,音频输入,输出上一直是比较弱的,或者说很难进行相关的操作,经过我最近的一些研究发现,在PC上实际上是可以实现这一系列的功能的,其实现原理主要是得益于google的webRTC技术。
什么是webRTC
WebRTC,名称源自网页即时通讯(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的API。它于2011年6月1日开源并在Google、Mozilla、Opera支持下被纳入万维网联盟的W3C推荐标准[1][2][3]。(来自维基百科)
也就是说webRTC是让网页浏览器进行实时语音对话或者视频对话的一系列的解决方案。官方demo:https://github.com/webrtc/samples
这个demo里面实际上功能非常多,关于调用摄像头我们主要关心的是这个demo,可是当大家把代码拉下来之后发现非常多东西,我在这里给大家简单的总结一下,并自己写上几个简单的demo,当然想要关心具体实现功能,或者代码的也可以看源码。
如何在网页中调用摄像头
首先我们需要实现的就是在网页中调用到摄像头进行录制,假如没有摄像头的同学也可以去下载一个繁星伴奏,进行模拟,下载地址:http://fanxing.kugou.com/ac/accompany
我们这里需要用到navigator.getUserMedia这个API,当然在chrome下需要使用兼容前缀即navigator.webkitGetUserMedia
代码如下:
<body>
<video width="400" height="" id="video" autoplay="">
<source src="myvideo.mp4" type="video/mp4"></source>
<source src="myvideo.ogv" type="video/ogg"></source>
<source src="myvideo.webm" type="video/webm"></source>
<object width="" height="" type="application/x-shockwave-flash" data="myvideo.swf">
<param name="movie" value="myvideo.swf" />
<param name="flashvars" value="autostart=true&file=myvideo.swf" />
</object>
当前浏览器不支持 video直接播放,点击这里下载视频: <a href="myvideo.webm">下载视频</a>
</video>
</body>
<script type="text/javascript">
var constraints={video:true}; //设置参数LocalMediaStream
var video_element=document.getElementById("video"); //
if(navigator.getUserMedia){ //默认API
navigator.getUserMedia(constraints)
.then(function(stream) {
console.info(stream);
window.stream = stream;
video_element.srcObject = stream;
video_element.src=URL.createObjectURL(stream);
return navigator.mediaDevices.enumerateDevices();
})
.catch(errorCallback);
}else if(navigator.webkitGetUserMedia){ //chrome兼容
navigator.webkitGetUserMedia(constraints,function(stream){
//成功获取后回调
console.info(stream);
window.stream = stream;
video_element.srcObject = stream;
video_element.src=URL.createObjectURL(stream);
return navigator.mediaDevices.enumerateDevices();
},function(data){
//失败回调
console.info(data)
});
}
</script>
我们可以看下代码,HTML部分很简单 就是一个video标签,主要功能的实现在js中可以找到navigator.getUserMedia这个API,通过这个就可以调用当前摄像头,并且返回流信息。
关于navigator.getUserMedia
提示用户需要权限去使用像摄像头或麦克风之类的媒体设备,如果用户提供了这个权限。
语法
navigator.getUserMedia ( constraints, successCallback, errorCallback );
参数
- constraints :
successCallback中传入的 LocalMediaStream对象所支持的媒体类型。 - successCallback:
当应用中传递LocalMediaStream对象时触发的函数。 - errorCallback:
当调用媒体设备失败时触发的函数.
其中第一个和第二是都是必须的.
第一个需要传入想要调用哪种媒体类型,具体就像代码中定义:
var constraints={video:true};
当然也可以写多个类型,例如
var constraints={video:true, audio: true};
- 第二个参数则是调用成功后的回调函数,回调函数本身也有一个参数data返回的则是相关的音频视频信息。
通过这个对整个API的使用,把获取到的流信息stream传给video标签的src中即可将视频信息在网页中显示出来。
多个设备的处理
假设现在用户有多个摄像设备,那我们需要让用户进行选择,又应该如何做呢?
首先我们需要给用户提供他自己的设备名称让他进行选择,在这里我们需要使用navigator.mediaDevices.enumerateDevices()这个API 因此我们可以这样写:
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<button id="showInfo">显示设备信息</button>
<div>
<h2>视频输入设备</h2>
<div id="videoinputInfoBox"> </div>
</div>
</body>
<script type="text/javascript">
$(function(){
init();
});
function init(){
$("#showInfo").on("click",function(){
//获取设备信息
navigator.mediaDevices.enumerateDevices().then(gotDevices);
});
}
//获取设备信息后的处理
function gotDevices(data){
var videoinputhtml="";
for (var i = 0; i < data.length; i++) {
console.info(data[i]);
if(data[i].kind=="videoinput"){
videoinputhtml+="<span data-id='"+data[i].deviceId+"' data-info='"+data[i].label+"' class='infoBtn'>"+data[i].label+"</span><br>";
}
}
$("#videoinputInfoBox").html(videoinputhtml);
}
</script>
</html>
我们在页面添加了一个按钮showInfo,当点击这个按钮的时候调用了navigator.mediaDevices.enumerateDevices()这个方法,这个方法会把当前的设备以多个对象组成数组的形式全部返回,而我们对整个数组进行循环分类,从而得到其相关信息,其中
- MediaDeviceInfo.label
设备名称 - MediaDeviceInfo.deviceId
设备ID - MediaDeviceInfo.kind
设备类型,只会有三种类型“videoinput”,"audioinput" or "audiooutput" MediaDeviceInfo.groupId
设备系列ID(自己翻译的,理解就是同一个设备有不同的功能,比如麦克风和听筒,则公用同一个groupId 官方的说法是Returns a DOMString that is a group identifier. Two devices have the same group identifier if they belong to the same physical device; for example a monitor with both a built-in camera and microphone.
如有错误请指正)
根据上面的这4个字段的解析我们就可以得到用户关于音视频设备的各种信息了。
那接着我们需要做最后一步就是让用户点击某个视频设备则调用相应的视频设备进行相关的视频流信息输出
大概思路就是给每个设备名称绑定点击事件,然后获取到当前设备的deviceId,通过前面提到的constraints参数将设备ID传到navigator.getUserMedia方法中从而获取相关的设备信息。
代码如下:
<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title></title>
<script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
</head> <body>
<video width="400" height="" id="video" autoplay="">
<source src="myvideo.mp4" type="video/mp4"></source>
<source src="myvideo.ogv" type="video/ogg"></source>
<source src="myvideo.webm" type="video/webm"></source>
<object width="" height="" type="application/x-shockwave-flash" data="myvideo.swf">
<param name="movie" value="myvideo.swf" />
<param name="flashvars" value="autostart=true&file=myvideo.swf" />
</object>
当前浏览器不支持 video直接播放,点击这里下载视频: <a href="myvideo.webm">下载视频</a>
</video>
<button id="showInfo">显示设备信息</button>
<div>
<h2>视频输入设备</h2>
<div id="videoinputInfoBox"> </div>
</div>
</body>
<script type="text/javascript">
$(function() {
init();
start();
}); function init() {
$("#showInfo").on("click", function() {
//获取设备信息
navigator.mediaDevices.enumerateDevices().then(gotDevices);
});
}
//获取设备信息后的处理
function gotDevices(data) {
var videoinputhtml = "";
for (var i = 0; i < data.length; i++) {
console.info(data[i]);
if (data[i].kind == "videoinput") {
videoinputhtml += "<span data-id='" + data[i].deviceId + "' data-info='" + data[i].label + "' class='infoBtn'>" + data[i].label + "</span><br>";
}
}
$("#videoinputInfoBox").html(videoinputhtml);
}
//点击选择设备
function start() {
$("#videoinputInfoBox").on("click", ".infoBtn", function() {
//判断当前是否有其他的流信息,如果有则停止
if (window.stream) {
window.stream.getTracks().forEach(function(track) {
track.stop();
});
}
var $this = $(this);
var id = $this.attr("data-id");
//将设备id传入constraints
var constraints = {
video: {
sourceId: id
}
};
var video_element = document.getElementById('video');
errorCallback = function(error) {
console.log("Video capture error: ", error.code);
};
if (navigator.getUserMedia) {
navigator.getUserMedia(constraints)
.then(function(stream) {
window.stream = stream;
video_element.srcObject = stream;
video_element.src = URL.createObjectURL(stream);
return navigator.mediaDevices.enumerateDevices();
})
.then(gotDevices)
.catch(errorCallback);
} else if (navigator.webkitGetUserMedia) {
//webkit特别处理,将标准中的constraints格式转换成webkit所支持的
constraints.video = constraintsToChrome(constraints.video);
navigator.webkitGetUserMedia(constraints, function(stream) {
window.stream = stream;
video_element.srcObject = stream;
video_element.src = URL.createObjectURL(stream);
return navigator.mediaDevices.enumerateDevices();
}, function(data) {
console.info(data)
});
}
})
}
// getUserMedia constraints shim. 官方源码提供的方法,把传入的sourceId转换成navigator.webkitGetUserMedia锁需要的格式
var constraintsToChrome = function(c) {
if (typeof c !== 'object' || c.mandatory || c.optional) {
return c;
}
var cc = {};
Object.keys(c).forEach(function(key) {
if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
return;
}
var r = (typeof c[key] === 'object') ? c[key] : {
ideal: c[key]
};
if (r.exact !== undefined && typeof r.exact === 'number') {
r.min = r.max = r.exact;
}
var oldname = function(prefix, name) {
if (prefix) {
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
}
return (name === 'deviceId') ? 'sourceId' : name;
};
if (r.ideal !== undefined) {
cc.optional = cc.optional || [];
var oc = {};
if (typeof r.ideal === 'number') {
oc[oldname('min', key)] = r.ideal;
cc.optional.push(oc);
oc = {};
oc[oldname('max', key)] = r.ideal;
cc.optional.push(oc);
} else {
oc[oldname('', key)] = r.ideal;
cc.optional.push(oc);
}
}
if (r.exact !== undefined && typeof r.exact !== 'number') {
cc.mandatory = cc.mandatory || {};
cc.mandatory[oldname('', key)] = r.exact;
} else {
['min', 'max'].forEach(function(mix) {
if (r[mix] !== undefined) {
cc.mandatory = cc.mandatory || {};
cc.mandatory[oldname(mix, key)] = r[mix];
}
});
}
});
if (c.advanced) {
cc.optional = (cc.optional || []).concat(c.advanced);
}
return cc;
};
</script> </html>
我们将前面两段代码进行了结合,添加了一些方法,从而实现点击切换视频源
分析下上面的代码,其实关键点都有备注:
一
if (window.stream) { window.stream.getTracks().forEach(function(track) { track.stop(); }); }
因为我们每次都会将流信息stream放到window下面,因此每次选择新的视频源的时候都需要将旧的清除
二
var constraints = { video: { sourceId: id } };
我们在设置constraints参数的时候不再是使用constraints={video:true}了,而是将当前选择的这个设备id传入
三
constraints.video = constraintsToChrome(constraints.video);
这一段主要是针对webkit内核浏览器的,也就是chrome。
constraintsToChrome这个方法是我在webRTC的demo源码中找到的,应该是对传入的constraints进行格式化处理的
处理之前
var constraints = {
video: {
sourceId: id
}
};处理之后:
var constraints = {
video: {
optional:[
{sourceId: id}
]
}
};这里我们直接调用就好。
截取图片
进行了上面的这些调整之后,我们就可以实现多个设备之间的选择从而调用到相应的摄像头。
那获取的摄像数据后我们还可以在进行一些扩展,例如截图
我们在上面的代码中添加一个方法即可
function setPhoto(){
var video_element=document.getElementById('video');
//拍照按钮
var screenshotBtn=document.getElementById("screenshot");
//点击拍照
screenshotBtn.onclick=function(){
var canvas=document.createElement('canvas'); //动态创建画布对象
var ctx=canvas.getContext('2d');
var cw=$("#video").width();
var ch=$("#video").height();
canvas.width=cw;
canvas.height=ch;
ctx.fillStyle='#ffffff';
ctx.fillRect(0,0,cw,ch);
ctx.drawImage(video_element,0,0,cw,ch); //将video对象内指定的区域捕捉绘制到画布上指定的区域,可进行不等大不等位的绘制。
console.info(canvas);
//渲染canvas
$("body").append(canvas);
//canvas转换成base64位的数据的图片
var imageData=canvas.toDataURL();
//渲染图片
$("body").append('<img src="'+imageData+'" id="canvasImg">');
}
}
上面的方法基于canvas即可实现获取到屏幕中video部分的像素点生成一个canvas画布以及一张相关的图片。
关于webRTC的相关内容还有很多,这里只是简单的说明了PC端video部分的使用,另外还有audio部分的内容以及移动端的具体事件这里就不一一展开了,有兴趣的同学也可以进行研究。
关于webRTC中video的使用实践的更多相关文章
- Android IOS WebRTC 音视频开发总结(八十七)-- WebRTC中丢包重传NACK实现分析
本文主要介绍WebRTC中丢包重传NACK的实现,作者:weizhenwei ,文章最早发表在编风网,微信ID:befoio 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID ...
- Android IOS WebRTC 音视频开发总结(八十六)-- WebRTC中RTP/RTCP协议实现分析
本文主要介绍WebRTC中的RTP/RTCP协议,作者:weizhenwei ,文章最早发表在编风网,微信ID:befoio 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID ...
- WebRTC中的NetEQ
NetEQ使得WebRTC语音引擎能够快速且高解析度地适应不断变化的网络环境,确保了音质优美且缓冲延迟最小,其集成了自适应抖动控制以及丢包隐藏算法. WebRTC和NetEQ概述 WebRTC Web ...
- webRTC中语音降噪模块ANS细节详解(一)
ANS(adaptive noise suppression) 是webRTC中音频相关的核心模块之一,为众多公司所使用.从2015年开始,我在几个产品中使用了webRTC的3A(AEC/ANS/AG ...
- webrtc中APM(AudioProcessing module)的使用
一,实例化和配置 AudioProcessing* apm = AudioProcessing::Create(0); //这里的0指的是channelID,只是一个标注那个通道的表示 apm-> ...
- Redis在WEB开发中的应用与实践
Redis在WEB开发中的应用与实践 一.Redis概述: Redis是一个功能强大.性能高效的开源数据结构服务器,Redis最典型的应用是NoSQL.但事实上Redis除了作为NoSQL数据库使用之 ...
- webrtc中的带宽自适应算法
转自:http://www.xuebuyuan.com/1248366.html webrtc中的带宽自适应算法分为两种: 1, 发端带宽控制, 原理是由rtcp中的丢包统计来动态的增加或减少带宽,在 ...
- NET中异常处理的最佳实践
NET中异常处理的最佳实践 本文翻译自CodeProject上的一篇文章,原文地址. 目录 介绍 做最坏的打算 提前检查 不要信任外部数据 可信任的设备:摄像头.鼠标以及键盘 “写操作”同样可能失效 ...
- Lazy<T>在Entity Framework中的性能优化实践
Lazy<T>在Entity Framework中的性能优化实践(附源码) 2013-10-27 18:12 by JustRun, 328 阅读, 4 评论, 收藏, 编辑 在使用EF的 ...
随机推荐
- C#基础视频教程5.3 如何编写简单的超级热键
跟前面一章讲解计算器一样,到最后一小节,我们总是要把代码规整好,让整个程序显得非常简洁,先做个文件夹把我们自定义的类库都放进去 然后我们开始整理Form1里面的代码,为了实现超级热键的功能,我们应 ...
- .NET破解之爱奇迪(三)
本教程只能用于学习研究,不可进行任何商业用途.如有使用,请购买正版,尊重他人劳动成果和知识产权! .NET破解之爱奇迪(一) .NET破解之爱奇迪(二) 一打开软件,就看到各种注册和未注册提示信息,就 ...
- hdu 1035 Robot Motion(dfs)
虽然做出来了,还是很失望的!!! 加油!!!还是慢慢来吧!!! >>>>>>>>>>>>>>>>> ...
- VMware - "Determining IP Information for eth0...Failed
Linux ifup eth0 出现错误: Dertermining IP information for eth0....failed - no link present check cable D ...
- Asp.net MVC中Html.Partial, RenderPartial, Action,RenderAction 区别和用法【转发】
Html.partial和RenderPartial的用法与区别Html.partial和RenderPartial都是输出html片段,区别在于Partial是将视图内容直接生成一个字符串并返回(相 ...
- js 获取iframe页面元素
js 获取iframe页面元素 CreationTime--2018年8月16日18点00分 Author:Marydon <!-- chart图表 --> <iframe id ...
- jquery 获取css position的值
jquery 获取css position的值 CreateTime--2018年5月28日14:03:12 Author:Marydon 1.情景展示 <div id="aa&q ...
- webservice调用的四种方式
因为数据在网络上传输都是通过xml形式的,本质都是把数据封装然后通过xml传输,接收到的也是xml文件,1 和 4 让程序员屏蔽了处理xml文件,而2 和3需要程序员自己写请求体 ,还要处理返回的xm ...
- Java类载入器 ClassLoader的解析
//參考 : http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 类载入器基本概念 类载入器是 Java 语言的一个创新,也是 Ja ...
- Node.js开发入门—使用cookie保持登录
这次来做一个站点登录的小样例,后面会用到. 这个演示样例会用到Cookie.HTML表单.POST数据体(body)解析. 第一个版本号,我们的用户数据就写死在js文件中. 第二个版本号会引入Mong ...