1 | <!DOCTYPE html> |
2 | <html> |
3 | |
4 | <head> |
5 | |
6 | <meta charset="utf-8"> |
7 | <meta name="viewport" |
8 | content="width=device-width"> |
9 | |
10 | <title>Cámara</title> |
11 | |
12 | <style> |
13 | html { |
14 | color-scheme: light dark; |
15 | } |
16 | </style> |
17 | |
18 | </head> |
19 | |
20 | <body> |
21 | |
22 | <h1>Cámara</h1> |
23 | |
24 | <p> |
25 | Para grabar o capturar imagen, |
26 | cliquea |
27 | <strong>Inicia</strong>. |
28 | </p> |
29 | |
30 | <p> |
31 | Para grabar por 5 segundos |
32 | cliquea |
33 | <strong>Graba</strong> |
34 | y cliquea |
35 | <strong>Para</strong> para |
36 | detener. |
37 | </p> |
38 | |
39 | <p> |
40 | Para capturar una imagen de la |
41 | cámara, cliquea |
42 | <strong>Captura</strong>. |
43 | </p> |
44 | |
45 | <menu style="display: flex; |
46 | flex-wrap: wrap; |
47 | list-style: none;"> |
48 | <li> |
49 | <button type="button" |
50 | onclick="inicia()"> |
51 | Inicia |
52 | </button> |
53 | </li> |
54 | <li> |
55 | <button type="button" |
56 | onclick="graba()"> |
57 | Graba |
58 | </button> |
59 | </li> |
60 | <li> |
61 | <button type="button" |
62 | onclick="para();"> |
63 | Para |
64 | </button> |
65 | </li> |
66 | <li> |
67 | <button type="button" |
68 | onclick="captura()"> |
69 | Captura |
70 | </button> |
71 | </li> |
72 | </menu> |
73 | |
74 | <section |
75 | style="display: inline-block; |
76 | vertical-align: top;"> |
77 | |
78 | <h1>Preview</h1> |
79 | |
80 | <video id="preview" width="160" |
81 | height="120" autoplay |
82 | muted></video> |
83 | |
84 | </section> |
85 | |
86 | <section |
87 | style="display: inline-block; |
88 | vertical-align: top;"> |
89 | |
90 | <h1>Recording</h1> |
91 | |
92 | <video id="recording" width="160" |
93 | height="120" controls></video> |
94 | |
95 | <p> |
96 | <a id="descarga">Descarga</a> |
97 | </p> |
98 | |
99 | <div id="mensajes"></div> |
100 | |
101 | </section> |
102 | |
103 | <section |
104 | style="display: inline-block; |
105 | vertical-align: top;"> |
106 | |
107 | <h1>Imagen</h1> |
108 | |
109 | <canvas id="canvas" width="160" |
110 | height="120"></canvas> |
111 | |
112 | <p> |
113 | <a id="descargaImagen"> |
114 | Descarga</a> |
115 | </p> |
116 | |
117 | </section> |
118 | |
119 | <script> |
120 | |
121 | let stream = null |
122 | |
123 | let TIEMPO_DE_GRABACION = 5000 |
124 | |
125 | var context = |
126 | canvas.getContext('2d') |
127 | |
128 | async function inicia() { |
129 | try { |
130 | stream = await navigator |
131 | .mediaDevices.getUserMedia({ |
132 | video: true, |
133 | audio: true |
134 | }) |
135 | preview.srcObject = stream |
136 | descarga.href = stream |
137 | preview.captureStream = |
138 | preview.captureStream |
139 | || preview.mozCaptureStream |
140 | await new Promise( |
141 | resolve => |
142 | preview.onplaying = resolve) |
143 | } catch (e) { |
144 | log(e.message) |
145 | } |
146 | } |
147 | |
148 | async function graba() { |
149 | try { |
150 | const recordedChunks = |
151 | await grabaClip(stream, |
152 | TIEMPO_DE_GRABACION) |
153 | let recordedBlob = new Blob( |
154 | recordedChunks, |
155 | { type: "video/webm" }) |
156 | recording.src = |
157 | URL.createObjectURL( |
158 | recordedBlob) |
159 | descarga.href = recording.src |
160 | descarga.download = |
161 | "RecordedVideo.webm" |
162 | |
163 | log("Exitosamente grabados " |
164 | + recordedBlob.size |
165 | + " bytes de " |
166 | + recordedBlob.type |
167 | + " media.") |
168 | } catch (e) { |
169 | log(e.message) |
170 | } |
171 | } |
172 | |
173 | function para() { |
174 | detiene(preview.srcObject) |
175 | } |
176 | |
177 | function captura() { |
178 | context.drawImage(preview, |
179 | 0, 0, 160, 120) |
180 | descargaImagen.href = |
181 | canvas.toDataURL('image/jpeg') |
182 | descargaImagen.download = |
183 | "imagen.jpg" |
184 | } |
185 | |
186 | function grabaClip(stream, |
187 | milisegundos) { |
188 | let recorder = |
189 | new MediaRecorder(stream) |
190 | let data = [] |
191 | recorder.ondataavailable = |
192 | event => data.push(event.data) |
193 | recorder.start() |
194 | log(recorder.state |
195 | + " durante " |
196 | + (milisegundos / 1000) |
197 | + " segundos…") |
198 | let detenido = new Promise( |
199 | (resolve, reject) => { |
200 | recorder.onstop = resolve |
201 | recorder.onerror = |
202 | event => reject(event.name) |
203 | }) |
204 | let grabado = |
205 | espera(milisegundos) |
206 | .then(() => recorder.state |
207 | === "recording" |
208 | && recorder.stop() |
209 | ) |
210 | |
211 | return Promise.all([ |
212 | detenido, |
213 | grabado |
214 | ]) |
215 | .then(() => data) |
216 | } |
217 | |
218 | function detiene(stream) { |
219 | stream.getTracks().forEach( |
220 | track => track.stop()) |
221 | } |
222 | |
223 | function log(msg) { |
224 | mensajes.innerHTML += |
225 | msg + "<br>" |
226 | } |
227 | |
228 | function espera(milisegundos) { |
229 | return new Promise( |
230 | resolve => setTimeout( |
231 | resolve, milisegundos)) |
232 | } |
233 | |
234 | </script> |
235 | |
236 | </body> |
237 | |
238 | </html> |