概述
随着 Firefox 添加了对 getUserMedia 的支持,三大桌面浏览器现在都能够在无需插件的情况下获取摄像头数据。然而,由于这项技术仍处于早期阶段,不同浏览器之间的实现略有差异。下面是一个解决这些差异的示例,以及一个为您完成繁重工作的脚本。但在开始之前,让我们先概述一下这三种浏览器的比较。
Firefox 18 | Opera 12 | Chrome 24 | |
---|---|---|---|
是否需要供应商前缀 | 是 (moz) | 否 | 是 (webkit) |
是否通过 `autoplay` 属性触发 | 否 | 是 | 是 |
是否需要用户启用 | 是 1 | 否 | 否 |
是否触发 `playing` 事件 | 重复触发 | 触发一次 | 触发一次 |
是否支持 `file://` 协议 | 是 | 是 | 否 |
标签播放通知 | 无 | 图标 | 动画图标 |
权限请求 | 每次页面加载 | 仅第一次页面加载 | 每次页面加载 |
在 Firefox 中,需要通过将 about:config 中的 `media.peerconnection.enabled` 选项设置为 `true` 来启用 getUserMedia。
在开始编写代码后,我们还会发现一些其他的差异,所以让我们逐步讲解。我们将把实现 getUserMedia 的方法分解成以下几个简单的步骤:
深呼吸 - 我们开始吧…
一份 HTML5
在本教程中,我们的主要任务只是在页面上显示一个动态图像。在这方面,它与普通的视频没有什么不同,所以第一步是在 HTML 中使用一个简单的 `<video>` 元素。
就是这样。没有 `controls`,没有 `src`,什么也没有。
接下来是 JavaScript 部分。显然,我们需要获取 `<video>` 元素的引用,我们可以像这样(或者使用 `id`)来实现:
var video = document.querySelector('video');
一勺特性检测
现在开始变得有趣起来,因为我们要检查 getUserMedia 是否受支持。我们肯定不会使用不可靠的用户代理嗅探来实现这一点 - 不,我们将采用简单的方法,即检查 `navigator.getUserMedia` 对象是否存在。在 Firefox 和 Chrome 中,它需要使用前缀,因此首先将它分配给所有浏览器通用的对象会很方便。同时,我们也为稍后将要使用的 `window.URL` 对象做同样的事情。
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
接下来,进行实际的存在性检查。
if (navigator.getUserMedia) {
// Call the getUserMedia method here
} else {
console.log('Native device media streaming (getUserMedia) not supported in this browser.');
// Display a friendly "sorry" message to the user.
}
如果支持 getUserMedia,我们需要传递三个参数 - 一个选项对象、一个成功回调函数和一个错误回调函数。请注意,Firefox 中需要错误回调函数,而在 Opera 和 Chrome 中则可选。选项参数是一个 JSON 样式的对象,用于指定要使用音频、视频还是两者。以下示例代码仅用于视频:
navigator.getUserMedia({video: true}, successCallback, errorCallback);
请求摄像头访问权限的对话框



一勺流媒体
到目前为止一切顺利,所以让我们定义接下来会发生什么。成功回调函数接收一个包含来自摄像头的视频流的参数,我们希望将该流发送到我们的 `<video>` 元素。我们通过设置其 `src` 属性来实现这一点,但需要记住以下几点:
- Firefox 使用 `mozSrcObject` 属性,而 Opera 和 Chrome 使用 `src`。
- Chrome 使用 `createObjectURL` 方法,而 Firefox 和 Opera 直接发送流。
在 Firefox 中,`video.mozSrcObject` 最初为 null 而不是 undefined,因此我们可以依靠它来检测 Firefox 的支持(感谢 Florent 的提示)。一旦流知道要发送到哪里,我们就可以告诉视频流开始播放。
function successCallback(stream) {
if (video.mozSrcObject !== undefined) {
video.mozSrcObject = stream;
} else {
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
};
video.play();
}
准备上菜
就是这样。添加一个简单的错误回调函数,我们就得到了一个可工作的跨浏览器脚本,它看起来像这样:
window.addEventListener('DOMContentLoaded', function() {
'use strict';
var video = document.querySelector('video');
function successCallback(stream) {
// Set the source of the video element with the stream from the camera
if (video.mozSrcObject !== undefined) {
video.mozSrcObject = stream;
} else {
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
}
video.play();
}
function errorCallback(error) {
console.error('An error occurred: [CODE ' + error.code + ']');
// Display a friendly "sorry" message to the user
}
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
// Call the getUserMedia method with our callback functions
if (navigator.getUserMedia) {
navigator.getUserMedia({video: true}, successCallback, errorCallback);
} else {
console.log('Native web camera streaming (getUserMedia) not supported in this browser.');
// Display a friendly "sorry" message to the user
}
}, false);
GitHub 上可用
要开始以跨浏览器的方式访问 getUserMedia,我们还在 GitHub 上提供了一个可工作的示例:GumWrapper。
最后一个小贴士
如果您想对摄像头的流进行一些高级操作,例如捕获静态图像或添加特殊效果,您可能需要将其数据发送到画布上下文。您可以使用`drawImage()` 来实现这一点,在这种情况下,您需要视频的尺寸。这些尺寸可以通过 `video.videoWidth` 和 `video.videoHeight` 属性获得,但请注意,只有在浏览器拥有有关流的信息时才会设置这些属性。这意味着您必须在获取这些属性之前监听某些事件。有一些相关的事件,总是按照以下顺序触发:
play
loadedmetadata
loadeddata
playing
`play` 事件在调用 `video.play()` 方法后触发,但在视频真正开始播放之前可能会有短暂的延迟。这就是 `playing` 事件的作用,但请注意,在 Firefox 中,只要流或视频正在播放,它就会重复触发。在此之前,还有几个数据事件,第一个事件仅用于元数据,但在 Firefox 中,它不包含视频尺寸。因此,最可靠的事件是 `loadeddata` 事件 - 然后您可以确定知道视频流的宽度和高度。您可以像这样编写代码:
video.addEventListener('loadeddata', function() {
console.log('Video dimensions: ' + video.videoWidth + ' x ' + video.videoHeight);
}, false);
顺便说一句,您还可以使用流的尺寸作为进一步的错误检查,例如检查宽度和高度是否大于 0。这将避免诸如用户网络摄像头损坏或未连接等问题。
就是这样。我相信随着技术的成熟,浏览器之间的差异会逐渐消失,但目前,以上代码应该可以帮助您入门。
关于 Daniel Davis
@ourmaninjapan Daniel 的工作经验包括在 Opera Software 担任开发者布道师,以及在英国和日本从事 Web 开发、IT 培训和项目管理。目前是日本“html5j”开发者社区的成员,喜欢尤克里里。
关于 Robert Nyman [荣誉编辑]
Mozilla Hacks 的技术布道师和编辑。发表关于 HTML5、JavaScript 和开放 Web 的演讲和博客文章。Robert 是 HTML5 和开放 Web 的坚定支持者,自 1999 年以来一直从事 Web 前端开发工作 - 在瑞典和纽约市。他还定期在http://robertnyman.com 上发表博客文章,并且喜欢旅行和结识新朋友。
25 条评论