网站项目基于HTML5+AngularJs开发,正在做一个由HTML5调用摄像头拍照,从而实现修改头像的功能。起初觉得HTML5拍照很简单,但是做的时候才发现HTML5获取摄像头并不是那么容易。
2. 如何调用摄像头
$scope.photoErr = false
$scope.photoBtnDiable = true
var mediaStream = null,track = null
navigator.getMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
navigator.msGetUserMedia)
if (navigator.getMedia) {
navigator.getMedia(
{
video: true
},
// successCallback
function (stream) {
var s = window.URL.createObjectURL(stream)
var video = document.getElementById('video')
video.src = window.URL.createObjectURL(stream)
mediaStream = stream
track = stream.getTracks()[0]
$scope.photoBtnDiable = false $scope.$apply()
},
// errorCallback
function (err) {
$scope.errorPhoto()
console.log("The following error occured:" + err)
})
} else {
$scope.errorPhoto()
}
代码解析:
navigator为浏览器对象,包含浏览器的信息,这里就是用这个对象打开摄像头。$scope为AndularJs语法。第一步声明navigator.getMedia来调用浏览器不同的打开摄像头函数,目前仅有getUserMedia、webkitGetUserMedia、mozGetUserMedia、msGetUserMedia四种方式分别对应通用浏览器、Google浏览器、火狐浏览器和IE浏览器,浏览器会自动判断调用哪一个函数。第二步是调用打开浏览器,包含三个参数,分别为需要使用的多媒体类型、获取成功返回的流数据处理函数以及操作失败返回错误消息处理函数。其中,使用时不仅可以设置视频还能设置使用麦克风,设置方式为:
{
video: true,
audio: true
}
调用成功即打开摄像头后返回视频流数据,我们可以将流数据设置到video标签在界面上实时显示图像。mediaStream用来记录获取到的流数据,track在Chrome浏览器中用来跟踪摄像头状态,这两个变量都能用来关闭摄像头。
3. 拍照
$scope.snap = function () {
var canvas = document.createElement('canvas')
canvas.width = "400"
canvas.height = "304"
var ctx = canvas.getContext('2d')
ctx.drawImage(video, 0, 0, 400, 304)
$scope.closeCamera()
$uibModalInstance.close(canvas.toDataURL("image/png"))
}
拍照时需要使用到canvas标签,创建一个canvas标签,设置我们需要拍照的尺寸大小,通过drawImage函数将video当前的图像保存到canvas标签,最后将图像数据转换为base64数据返回并关闭摄像头,这样就完成了我们的拍照功能。这里的$uibModalInstance对象是我们项目中打开弹出层的一个对象,用来控制弹出层的显示。
4. 如何关闭摄像头
$scope.closeCamera = function () {
if (mediaStream != null) {
if (mediaStream.stop) {
mediaStream.stop()
}
$scope.videosrc = ""
}
if (track != null) {
if (track.stop) {
track.stop()
}
}
}
正如前面所说,关闭摄像头的方式是通过mediaStream和track变量,只不过,track只能关闭Chrome浏览器中的摄像头,这也是Chrome 45版本以上关闭摄像头的方式。
5. 集成到AndularJs
事实上,前面所说的都是在AndularJs中实现的,当然,这里只是实现了拍照并返回拍照数据,我们想要在其他地方也使用,就需要将这部分独立出来,这里我们用到了AngularJs中的service机制,将这部分单独做成一个service并在项目中注入,然后就可以在其他地方调用了。
service注册:
app().registerService("h5TakePhotoService", function ($q, $uibModal) {
this.photo = function () {
var deferred = $q.defer()
require([config.server + "/com/controllers/photo.js"], function () {
$uibModal.open({
templateUrl: config.server + "/com/views/modal_take_photo.html",
controller: "photoModalController",
windowClass: "modal-photo"
}).result.then(function (e) {
deferred.resolve(e)
})
})
return deferred.promise
}
})
调用方式:
$scope.takePhoto = function () {
h5TakePhotoService.photo().then(function (res) {
if (res != null &&res != "") {
$scope.myImage = res
}
})
}
h5TakePhotoService为控制器中注入的拍照service对象,最后处理返回的图像数据,设置数据显示到界面上。
6. 兼容问题
主要存在Chrome浏览器中,本地测试时,Chrome浏览器中能够正常使用,但是部署到服务器后就不能正常使用,报错消息为 [object NavigatorUserMediaError],这是因为Chrome浏览器在使用摄像头时只支持安全源访问,所以只能通过https访问才能正常使用。
最后需要说一下,测试时只能通过http://url访问才能使用,不能通过file://url方式访问,即我们需要将代码部署才能访问,可以在Visual Studio、 java web、php中完成。
html5中的video这个标签是引入视频的,通过navigator.getUserMedia去获取摄像头的视频流,所以要在事件里用关闭的代码都不能执行关闭摄像头,只有关闭网页,摄像头才关闭。html5打开摄像头代码:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta content="text/htmlcharset=UTF-8" http-equiv="content-type">
<title>Smart Home - Camera</title>
<link href="css/main.css" rel="stylesheet" type="text/css">
<script src="js/jq.js"></script>
<script type="text/javascript">
/*
*/
function init(t){
accessLocalWebCam("camera_box")
}
// Normalizes window.URL
window.URL = window.URL || window.webkitURL || window.msURL || window.oURL
// Normalizes navigator.getUserMedia
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia|| navigator.mozGetUserMedia ||
navigator.msGetUserMedia
function isChromiumVersionLower() {
var ua = navigator.userAgent
var testChromium = ua.match(/AppleWebKit\/.* Chrome\/([\d.]+).* Safari\//)
return (testChromium &&(parseInt(testChromium[1].split('.')[0]) <19))
}
function successsCallback(stream) {
document.getElementById('camera_errbox').style.display = 'none'
document.getElementById('camera_box').src = (window.URL
&&window.URL.createObjectURL) ?
window.URL.createObjectURL(stream) : stream
}
function errorCallback(err) {
}
function accessLocalWebCam(id) {
try {
// Tries it with spec syntax
navigator.getUserMedia({ video: true }, successsCallback, errorCallback)
} catch (err) {
// Tries it with old spec of string syntax
navigator.getUserMedia('video', successsCallback, errorCallback)
}
}
</script>
<style type="text/css">
#camera_errbox{
width:320pxheight:autoborder:1px solid #333333padding:10px
color:#ffftext-align:leftmargin:20px auto
font-size:14px
}
#camera_errbox b{
padding-bottom:15px
}
</style>
</head>
<body onLoad="init(this)" oncontextmenu="return false" onselectstart="return false">
<div class="Screen_outer">
<div id="mainbox" class="Screen_inner">
<div id="bt_goback"></div>
<div class="logobox"></div><div id="t_iconbox"
class="icon_12"></div><div id="t_text">
<div id="el_title" class="font_h2">Camera</div>
<div id="el_descr" class="font_2"></div>
</div>
<div class="t_descri_bt"></div>
<div class="sp_title"><span
class="sp_title_text">Camera</span><div class="sp_oc
sp_oc_1"></div></div>
<dl id="el_actionbox" class="menu_btbox" style="text-align:center">
<video id="camera_box" autoplay="" src=""></video>
<div id="camera_errbox">
<b>请点击“允许”按钮,授权网页访问您的摄像头!</b>
<div>若您并未看到任何授权提示,则表示您的浏览器不支持Media Capture或您的机器没有连接摄像头设备。</div>
</div>
</dl>
</div>
</div>
</body>
</html>
html5需要使用接口chrome30+ for android 已经实现了利用webcam,调用手机后置摄像头,代码如下:<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>HTML5 GetUserMedia Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
</head>
<body>
<input type="button" title="开启摄像头" value="开启摄像头" onclick="getMedia()" /><br />
<video height="120px" autoplay="autoplay"></video><hr />
<input type="button" title="拍照" value="拍照" onclick="getPhoto()" /><br />
<canvas id="canvas1" height="120px" ></canvas><hr />
<input type="button" title="视频" value="视频" onclick="getVedio()" /><br />
<canvas id="canvas2" height="120px"></canvas>
<script type="text/javascript">
var video = document.querySelector('video')
var audio, audioType
var canvas1 = document.getElementById('canvas1')
var context1 = canvas1.getContext('2d')
var canvas2 = document.getElementById('canvas2')
var context2 = canvas2.getContext('2d')
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL
var exArray = []//存储设备源ID
MediaStreamTrack.getSources(function (sourceInfos) {
for (var i = 0i != sourceInfos.length++i) {
var sourceInfo = sourceInfos[i]
//这里会遍历audio,video,所以要加以区分
if (sourceInfo.kind === 'video') {
exArray.push(sourceInfo.id)
}
}
})
function getMedia() {
if (navigator.getUserMedia) {
navigator.getUserMedia({
'video': {
'optional': [{
'sourceId': exArray[1] //0为前置摄像头,1为后置
}]
},
'audio':true
}, successFunc, errorFunc) //success是获取成功的<a href="https://www.baidu.com/s?wd=%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1d-rHc3rjckuyf4nH0YPHPW0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6KdThsqpZwYTjCEQLGCpyw9Uz4Bmy-bIi4WUvYETgN-TLwGUv3EnWDYnHndPjm3PH6vPWb1rjcvr0" target="_blank" class="baidu-highlight">回调函数</a>
}
else {
alert('Native device media streaming (getUserMedia) not supported in this browser.')
}
}
function successFunc(stream) {
//alert('Succeed to get media!')
if (video.mozSrcObject !== undefined) {
//Firefox中,video.mozSrcObject最初为null,而不是未定义的,我们可以靠这个来检测Firefox的支持
video.mozSrcObject = stream
}
else {
video.src = window.URL &&window.URL.createObjectURL(stream) || stream
}
//video.play()
// 音频
audio = new Audio()
audioType = getAudioType(audio)
if (audioType) {
audio.src = 'polaroid.' + audioType
audio.play()
}
}
function errorFunc(e) {
alert('Error!'+e)
}
// 将视频帧绘制到Canvas对象上,Canvas每60ms切换帧,形成肉眼视频效果
function drawVideoAtCanvas(video,context) {
window.setInterval(function () {
context.drawImage(video, 0, 0,90,120)
}, 60)
}
//获取音频格式
function getAudioType(element) {
if (element.canPlayType) {
if (element.canPlayType('audio/mp4codecs="mp4a.40.5"') !== '') {
return ('aac')
} else if (element.canPlayType('audio/oggcodecs="vorbis"') !== '') {
return ("ogg")
}
}
return false
}
// vedio播放时触发,绘制vedio帧图像到canvas
//video.addEventListener('play', function () {
//drawVideoAtCanvas(video, context2)
//}, false)
//拍照
function getPhoto() {
context1.drawImage(video, 0, 0,90,120)//将video对象内指定的区域捕捉绘制到画布上指定的区域,实现拍照。
}
//视频
function getVedio() {
drawVideoAtCanvas(video, context2)
}
</script>
</body>
</html>