|
3 | 3 |
|
4 | 4 | <head> |
5 | 5 | <meta charset="UTF-8"> |
6 | | - <title>Title</title> |
| 6 | + <title>摄像头画面web前端实时输出功能</title> |
7 | 7 | </head> |
| 8 | +<style> |
| 9 | + .wrapper { |
| 10 | + text-align: center; |
| 11 | + } |
| 12 | + |
| 13 | + button { |
| 14 | + display: block; |
| 15 | + line-height: 20px; |
| 16 | + font-size: 14px; |
| 17 | + text-align: center; |
| 18 | + color: #4c5161; |
| 19 | + border-radius: 4px; |
| 20 | + border: 1px solid #d0d0d5; |
| 21 | + padding: 10px 15px; |
| 22 | + min-width: 80px; |
| 23 | + background-color: #fff; |
| 24 | + background-repeat: no-repeat; |
| 25 | + background-position: center; |
| 26 | + text-decoration: none; |
| 27 | + box-sizing: border-box; |
| 28 | + transition: border-color .15s, box-shadow .15s, opacity .15s; |
| 29 | + font-family: inherit; |
| 30 | + cursor: pointer; |
| 31 | + overflow: visible; |
| 32 | + margin: 20px auto; |
| 33 | + } |
| 34 | + |
| 35 | + .video, |
| 36 | + .canvas { |
| 37 | + border: 1px solid #d0d0d5; |
| 38 | + } |
| 39 | +</style> |
8 | 40 |
|
9 | 41 | <body> |
10 | | - <input type="button" title="开启摄像头" value="开启摄像头" onclick="getMedia();" /><br /> |
| 42 | + <div class="wrapper"> |
| 43 | + <button id="openCamera" title="开启摄像头">开启摄像头</button> |
11 | 44 |
|
12 | | - <video height="400px" autoplay="autoplay" style='border: 1px solid #f00;'></video> |
13 | | - <hr /> |
| 45 | + <video width="700px" height="390px" class="video" autoplay="autoplay"></video> |
14 | 46 |
|
15 | | - <input type="button" title="拍照" value="拍照" onclick="getPhoto();" /><br /> |
16 | | - |
17 | | - <canvas id="canvas1" height="400px" style='border: 1px solid #f00;'></canvas> |
18 | | - <hr /> |
19 | | - |
20 | | - <input type="button" title="视频" value="视频" onclick="getVedio();" /><br /> |
21 | | - <canvas id="canvas2" height="400px" style='border: 1px solid #f00;'></canvas> |
| 47 | + <button title="拍照" id="photograph">拍照</button> |
22 | 48 |
|
| 49 | + <canvas id="canvas" class="canvas" width="700" height="530"></canvas> |
23 | 50 |
|
| 51 | + </div> |
24 | 52 | <script type="text/javascript"> |
25 | | - var videoHeight; |
26 | | - var videoWidth; |
27 | | - var video = document.querySelector('video'); |
28 | | - var audio, audioType; |
29 | | - var canvas1 = document.getElementById('canvas1'); |
30 | | - var context1 = canvas1.getContext('2d'); |
31 | | - var canvas2 = document.getElementById('canvas2'); |
32 | | - var context2 = canvas2.getContext('2d'); |
33 | | - |
34 | | - navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || |
35 | | - navigator.msGetUserMedia; |
| 53 | + let openCamera = document.querySelector("#openCamera") |
| 54 | + let photograph = document.querySelector("#photograph") |
| 55 | + window.isOpenCamera = false |
| 56 | + |
| 57 | + let video = document.querySelector('video'); |
| 58 | + let canvas = document.getElementById('canvas'); |
| 59 | + let context1 = canvas.getContext('2d'); |
36 | 60 | window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; |
37 | | - var exArray = []; //存储设备源ID |
38 | | - try { |
39 | | - MediaStreamTrack.getSources(function (sourceInfos) { |
40 | | - for (var i = 0; i != sourceInfos.length; ++i) { |
41 | | - var sourceInfo = sourceInfos[i]; |
42 | | - //这里会遍历audio,video,所以要加以区分 |
43 | | - if (sourceInfo.kind === 'video') { |
44 | | - exArray.push(sourceInfo.id); |
45 | | - } |
46 | | - } |
47 | | - }); |
48 | | - } catch (e) { |
49 | | - console.log(e) |
50 | | - } |
51 | 61 |
|
52 | | - function getMedia() { |
53 | | - if (navigator.getUserMedia) { |
54 | | - navigator.getUserMedia({ |
55 | | - 'video': { |
56 | | - 'optional': [{ |
57 | | - 'sourceId': exArray[0] //0为前置摄像头,1为后置 |
58 | | - }] |
59 | | - }, |
60 | | - 'audio': true |
61 | | - }, successFunc, errorFunc); //success是获取成功的回调函数 |
62 | | - } else { |
63 | | - /** |
64 | | - * 测试ios手机及笔记本,微信及浏览器, 有趣的是navigator有getUserMedia这个属性,但值为均为undefined |
65 | | - *时间 20191120 |
66 | | - * |
67 | | - * */ |
68 | | - alert('Native device media streaming (getUserMedia) not supported in this browser.'); |
| 62 | + // 开启摄像头 |
| 63 | + openCamera.addEventListener("click", () => { |
| 64 | + |
| 65 | + if (!navigator.mediaDevices.getUserMedia) return alert( |
| 66 | + 'Native device media streaming (getUserMedia) not supported in this browser.'); |
| 67 | + |
| 68 | + if (this.isOpenCamera) return console.log('[已经开启过摄像头的权限了]'); |
| 69 | + |
| 70 | + let exArray = getSources() |
| 71 | + const constraints = { |
| 72 | + 'video': { |
| 73 | + 'optional': [{ |
| 74 | + 'sourceId': exArray[0] //0为前置摄像头,1为后置 |
| 75 | + }] |
| 76 | + }, |
| 77 | + 'audio': true |
69 | 78 | } |
70 | | - } |
| 79 | + navigator.mediaDevices.getUserMedia(constraints).then((stream) => { |
| 80 | + successFunc(stream) |
| 81 | + this.isOpenCamera = true |
| 82 | + |
| 83 | + }).catch(function (err) { |
| 84 | + alert(err.name + ": " + err.message); |
| 85 | + }); // 总是在最后检查错误; |
| 86 | + }) |
71 | 87 |
|
| 88 | + // 摄像机成功回调 |
72 | 89 | function successFunc(stream) { |
73 | | - //alert('Succeed to get media!'); |
74 | | - console.log(stream); |
75 | 90 | if (video.mozSrcObject !== undefined) { |
76 | | - //Firefox中,video.mozSrcObject最初为null,而不是未定义的,我们可以靠这个来检测Firefox的支持 |
77 | 91 | video.mozSrcObject = stream; |
78 | 92 | } else { |
79 | 93 | try { |
80 | 94 | video.src = window.URL && window.URL.createObjectURL(stream) || stream; |
81 | 95 | } catch (e) { |
82 | | - /** |
83 | | - * 之前工作的网页调用摄像头获取视频并拍照的作品报错: |
84 | | - Failed to execute 'createObjectURL' on 'URL' |
85 | | - 原因是Chrome升级后,新版本的Chrome不再支持该用法。 |
86 | | - 其他主流浏览器也是如此,详细讨论请参考如下链接: |
87 | | - https://www.fxsitecompat.com/en-CA/docs/2017/url-createobjecturl-stream-has-been-deprecated/ |
88 | | - * */ |
89 | 96 | try { |
90 | 97 | video.srcObject = stream; |
91 | 98 | } catch (error) { |
92 | 99 | console.log(error) |
93 | 100 | } |
94 | 101 | } |
95 | | - |
96 | | - |
97 | | - } |
98 | | - //video.play(); |
99 | | - // 音频 |
100 | | - audio = new Audio(); |
101 | | - audioType = getAudioType(audio); |
102 | | - if (audioType) { |
103 | | - audio.src = 'polaroid.' + audioType; |
104 | | - audio.play(); |
105 | | - } |
106 | | - video.onplay = function (ev) { |
107 | | - videoHeight = video.videoHeight; |
108 | | - videoWidth = video.videoWidth; |
109 | | - console.log(videoHeight) |
110 | | - console.log(videoWidth) |
111 | | - canvas1.width = videoWidth; |
112 | | - canvas1.height = videoHeight; |
113 | | - canvas2.width = videoWidth; |
114 | | - canvas2.height = videoHeight; |
115 | 102 | } |
116 | | - |
117 | | - } |
118 | | - |
119 | | - function errorFunc(e) { |
120 | | - alert('Error!' + e); |
121 | 103 | } |
122 | 104 |
|
123 | | - var img = new Image(); |
124 | | - img.src = "./imgs/shabi.png"; |
125 | | - img.onload = function (ev) { |
126 | | - context1.drawImage(img, 0, 0); |
127 | | - context2.drawImage(img, 0, 0); |
| 105 | + // 获取存储设备源ID |
| 106 | + function getSources() { |
| 107 | + let exArray = []; //存储设备源ID |
| 108 | + try { |
| 109 | + MediaStreamTrack.getSources(function (sourceInfos) { |
| 110 | + for (let i = 0; i != sourceInfos.length; ++i) { |
| 111 | + let sourceInfo = sourceInfos[i]; |
| 112 | + //这里会遍历audio,video,所以要加以区分 |
| 113 | + if (sourceInfo.kind === 'video') { |
| 114 | + exArray.push(sourceInfo.id); |
| 115 | + } |
| 116 | + } |
| 117 | + }); |
| 118 | + } catch (e) { |
| 119 | + console.log('[获取存储设备源ID]', e) |
| 120 | + } |
| 121 | + return exArray |
128 | 122 | } |
129 | 123 |
|
130 | | - // 将视频帧绘制到Canvas对象上,Canvas每60ms切换帧,形成肉眼视频效果 |
131 | | - function drawVideoAtCanvas(video, context) { |
132 | | - window.setInterval(function () { |
133 | | - context.drawImage(video, 0, 0); |
134 | | - context.drawImage(img, 0, 0); |
135 | | - |
136 | | - }, 60); |
137 | | - } |
| 124 | + //拍照 |
| 125 | + photograph.addEventListener("click", () => { |
| 126 | + if (!this.isOpenCamera) return alert("请开启摄像机权限") |
138 | 127 |
|
139 | | - //获取音频格式 |
140 | | - function getAudioType(element) { |
141 | | - if (element.canPlayType) { |
142 | | - if (element.canPlayType('audio/mp4; codecs="mp4a.40.5"') !== '') { |
143 | | - return ('aac'); |
144 | | - } else if (element.canPlayType('audio/ogg; codecs="vorbis"') !== '') { |
145 | | - return ("ogg"); |
146 | | - } |
| 128 | + let img = new Image(); |
| 129 | + img.src = ""; |
| 130 | + img.onload = function (ev) { |
| 131 | + context1.drawImage(img, 0, 0); |
147 | 132 | } |
148 | | - return false; |
149 | | - } |
150 | 133 |
|
151 | | - // vedio播放时触发,绘制vedio帧图像到canvas |
152 | | - // video.addEventListener('play', function () { |
153 | | - // drawVideoAtCanvas(video, context2); |
154 | | - // }, false); |
155 | | - //拍照 |
156 | | - function getPhoto() { |
157 | 134 | context1.drawImage(video, 0, 0); //将video对象内指定的区域捕捉绘制到画布上指定的区域,实现拍照。 |
158 | 135 | context1.drawImage(img, 0, 0); |
159 | | - } |
160 | | - |
161 | | - //视频 |
162 | | - function getVedio() { |
163 | | - drawVideoAtCanvas(video, context2); |
164 | | - } |
| 136 | + }) |
165 | 137 | </script> |
166 | 138 | </body> |
167 | 139 |
|
|
0 commit comments