简介
长久以来,音频/视频捕获都是网络开发中的“圣杯”。多年来,我们总是依赖于浏览器插件(Flash
现在轮到 HTML5 大显身手了。也许看起来不是很显眼,但是 HTML5 的崛起引发了对设备硬件访问的激增。地理位置
本教程介绍了一种新 API:navigator.getUserMedia()
,可让网络应用访问用户的相机和麦克风。
getUserMedia() 的历史
如果您还不知道,getUserMedia()
过去几年中出现过好几种“Media Capture API”的变体。很多人意识到,需要能够在网络上访问本地设备,但这要所有人合力开发出一种新的规范。局面一片混乱,以至于 W3C 最终决定成立一个工作组。他们只有一个目的:理清混乱的局面!设备 API 政策 (DAP) 工作组负责对过剩的提议进行统一和标准化。
我会试着总结一下 2011 所发生的事情...
第 1 轮:HTML 媒体捕获
HTML 媒体捕获是 DAP 在网络媒体捕获标准化上迈出的第一步。具体方法是超载
accept
如果您要让用户通过网络摄像头拍摄自己的快照,就可以使用 capture=camera
:
type="file" accept="image/*;capture=camera">
录制视频或音频也是类似的:
type="file" accept="video/*;capture=camcorder">
type="file" accept="audio/*;capture=microphone">
挺不错吧?它可以重复使用文件输入,这点我特别喜欢。这在语义上非常有意义。这种特定“API”的不足之处在于,无法处理即时效果(例如将实时网络摄像头数据呈现到
支持:
- Android 3.0 浏览器
- 首次实施的一个例子。请观看此视频,了解其实际使用情况。 - Android 版 Chrome 浏览器 (0.16)
除非您使用的是以上某个移动浏览器,否则我建议您不要使用该 API。供应商纷纷转向 getUserMedia()
。其他任何人都不太可能会长期实施 HTML 媒体捕获。
第 2 轮:设备元素
很多人认为 HTML 媒体捕获的局限性太大,因此一种新的规范应运而生,可以支持任何类型的(未来)设备。不出意料地,该设计需要新的 getUserMedia()
Opera 是第一批根据 navigator.getUserMedia()
getUserMedia()
type="media" onchange="update(this.data)">
autoplay>
function update(stream) {
document.querySelector('video').src = stream.url;
}
支持:
很遗憾,已发布的浏览器中没有任何一款曾经包含
现在深吸一口气。这玩意儿速度飞快!
第 3 轮:WebRTC
依靠
getUserMedia()
支持:
在 Chrome 浏览器 18.0.1008 和更高版本中,可在 about:flags
使用入门
利用 navigator.getUserMedia()
,我们最终实现了在没有插件的情况下访问网络摄像头和麦克风输入内容。相机访问权限现在和调用有关,而不是和安装有关。它直接内嵌在浏览器中。感到兴奋了吗?
启用
getUserMedia()
about:flags

about:flags
getUserMedia()
。
对于 Opera,请下载某个实验性
功能检测
功能检测是简单地检查是否存在 navigator.getUserMedia
:
function hasGetUserMedia() {
// Note: Opera builds are unprefixed.
return !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia);
}
if (hasGetUserMedia()) {
// Good to go!
} else {
alert('getUserMedia() is not supported in your browser');
}
获取输入设备的访问权限:
要使用网络摄像头或麦克风,我们需要请求权限。getUserMedia()
"video"
。要同时使用麦克风和相机,则传递 "video, audio"
:
autoplay>
var onFailSoHard = function(e) {
console.log('Reeeejected!', e);
};
// Not showing vendor prefixes.
navigator.getUserMedia('video, audio', function(localMediaStream) {
var video = document.querySelector('video');
video.src = window.URL.createObjectURL(localMediaStream);
// Note: onloadedmetadata doesn't fire in Chrome when using it with getUserMedia.
// See crbug.com/110938.
video.onloadedmetadata = function(e) {
// Ready to go. Do some stuff.
};
}, onFailSoHard);
好吧,这到底是怎么一回事呢?媒体捕获是各种新 HTML5 API 进行协作的绝佳示例。参与协作的还有其他一些 HTML 元素,例如 src
LocalMediaStream
我还会将 autoplay
,否则它会停在第一帧。添加 controls
请注意:在 Chrome 浏览器中存在一个错误,导致仅仅传递“audio”无效:crbug.com/112367。我也无法在 Opera 中正常使用
Opera 和 Chrome 浏览器实施的是该规范的不同版本。这导致实际使用起来要比预期的更有“挑战性”。
在 Chrome 浏览器中:
该代码段适用于 about:flags
navigator.webkitGetUserMedia('audio, video', function(localMediaStream) {
var video = document.querySelector('video');
video.src = window.webkitURL.createObjectURL(localMediaStream);
}, onFailSoHard);
在 Opera 中:
Opera 开发人员版本不支持该规范的更新版本。该代码段适用于
navigator.getUserMedia({audio: true, video: true}, function(localMediaStream) {
video.src = localMediaStream;
}, onFailSoHard);
关键的区别之处在于:
getUserMedia()
是无前缀的。 - 对象作为第一个参数而不是字符串列表进行传递。
- 将
video.src
直接设置为 LocalMediaStream
对象,而不是 Blob 网址。据我所知,Opera 最终会更新此设置,改为要求 Blob 网址。
对于这两者:
如果您希望能跨浏览器通用(但是这样很容易出问题),请尝试如下方法:
var video = document.querySelector('video');
if (navigator.getUserMedia) {
navigator.getUserMedia({audio: true, video: true}, function(stream) {
video.src = stream;
}, onFailSoHard);
} else if (navigator.webkitGetUserMedia) {
navigator.webkitGetUserMedia('audio, video', function(stream) {
video.src = window.webkitURL.createObjectURL(stream);
}, onFailSoHard);
} else {
video.src = 'somevideo.webm'; // fallback.
}
请务必查看
安全
将来,浏览器在调用 getUserMedia()
提供回退
对于无法获得 getUserMedia()
// Not showing vendor prefixes or code that works cross-browser:
function fallback(e) {
video.src = 'fallbackvideo.webm';
}
function success(stream) {
video.src = window.URL.createObjectURL(stream);
}
if (!navigator.getUserMedia) {
fallback();
} else {
navigator.getUserMedia({video: true}, success, fallback);
}
基本演示
请点击这里去查看截取屏幕截图:
ctx.drawImage(video, 0, 0)
getUserMedia()
autoplay>
src="">
style="display:none;">
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" works in Chrome 18. In other browsers, this will fall back to image/png.
document.querySelector('img').src = canvas.toDataURL('image/webp');
}
}
video.addEventListener('click', snapshot, false);
// Not showing vendor prefixes or code that works cross-browser.
navigator.getUserMedia({video: true}, function(stream) {
video.src = window.URL.createObjectURL(stream);
localMediaStream = stream;
}, onFailSoHard);
请点击这里去查看
应用效果
CSS 过滤器
目前,WebKit Nightlies 版以及 Chrome 浏览器 18 和更高版本支持 CSS 过滤器。
使用
video {
width: 307px;
height: 250px;
background: rgba(255,255,255,0.5);
border: 1px solid #ccc;
}
.grayscale {
+filter: sepia(1);
}
.blur {
+filter: blur(3px);
}
...
autoplay>
var idx = 0;
var filters = ['grayscale', 'sepia', 'blur', 'brightness', 'contrast', 'hue-rotate',
'hue-rotate2', 'hue-rotate3', 'saturate', 'invert', ''];
function changeFilter(e) {
var el = e.target;
el.className = '';
var effect = filters[idx++ % filters.length]; // loop through filters.
if (effect) {
el.classList.add(effect);
}
}
document.querySelector('video').addEventListener('click', changeFilter, false);
请点击这里去查看
WebGL 纹理
视频捕获的一个精彩用例就是以 WebGL 纹理的形式呈现实时输入。由于我对 WebGL 一无所知(除了知道它很好),我建议您看看杰罗姆·艾蒂安 (Jerome Etienne) 的教程和演示。其中介绍了如何使用 getUserMedia()
通过 Web Audio API 使用 getUserMedia
这部分介绍了当前 API 可能在未来做出的改进和扩展。
我有一个梦想,就是只通过开放网络技术在浏览器中构建 AutoTune!这个梦想很快就要实现了。对于麦克风输入,我们已经有了 getUserMedia()
。通过
有朝一日,将麦克风输入输送到 Web Audio API 可能会是这样的:
var context = new window.webkitAudioContext();
navigator.webkitGetUserMedia({audio: true}, function(stream) {
var microphone = context.createMediaStreamSource(stream);
var filter = context.createBiquadFilter();
// microphone -> filter -> destination.
microphone.connect(filter);
filter.connect(context.destination);
}, onFailSoHard);
如果您希望将 getUserMedia()
总结
总体而言,网络上的设备访问向来是一大难题。很多人曾经尝试过,但是没什么人取得成功。大多数早期的思路从未在专有环境之外占据主导地位,也从未广泛采用过。
真正的问题在于,网络的安全模式与本地系统有天壤之别。例如,我可能不希望随便什么网站都有权访问我的摄像机,但是这个问题很难解决。
PhoneGap
getUserMedia()
【博文来自http://blog.csdn.net/renfufei/article/details/21168239】