OLD | NEW |
1 <html> | 1 <html> |
2 <head> | 2 <head> |
3 <script type="text/javascript" src="webrtc_test_utilities.js"></script> | 3 <script type="text/javascript" src="webrtc_test_utilities.js"></script> |
4 <script type="text/javascript"> | 4 <script type="text/javascript"> |
5 $ = function(id) { | 5 $ = function(id) { |
6 return document.getElementById(id); | 6 return document.getElementById(id); |
7 }; | 7 }; |
8 | 8 |
9 var gLocalStream = null; | |
10 | |
11 setAllEventsOccuredHandler(function() { | 9 setAllEventsOccuredHandler(function() { |
12 gLocalStream.stop(); | |
13 reportTestSuccess(); | 10 reportTestSuccess(); |
14 }); | 11 }); |
15 | 12 |
16 function getSources() { | 13 function getSources() { |
17 MediaStreamTrack.getSources(function(devices) { | 14 MediaStreamTrack.getSources(function(devices) { |
18 document.title = 'Media devices available'; | 15 document.title = 'Media devices available'; |
19 sendValueToTest(JSON.stringify(devices)); | 16 sendValueToTest(JSON.stringify(devices)); |
20 }); | 17 }); |
21 } | 18 } |
22 | 19 |
23 // Creates a MediaStream and renders it locally. When the video is detected to | 20 // Creates a MediaStream and renders it locally. When the video is detected to |
24 // be rolling, the stream should be stopped. | 21 // be rolling, the stream should be stopped. |
25 function getUserMediaAndStop(constraints) { | 22 function getUserMediaAndStop(constraints) { |
26 console.log('Calling getUserMediaAndStop.'); | 23 console.log('Calling getUserMediaAndStop.'); |
27 navigator.webkitGetUserMedia( | 24 navigator.webkitGetUserMedia( |
28 constraints, | 25 constraints, |
29 function(stream) { displayAndDetectVideo(stream, stopVideoTrack); }, | 26 function(stream) { |
| 27 detectVideoInLocalView1(stream, function() { |
| 28 stream.getVideoTracks()[0].stop(); |
| 29 waitForVideoToStop('local-view-1'); |
| 30 }); |
| 31 }, |
30 failedCallback); | 32 failedCallback); |
31 } | 33 } |
32 | 34 |
33 // Requests getusermedia and expects it to fail. The error name is returned | 35 // Requests getusermedia and expects it to fail. The error name is returned |
34 // to the test. | 36 // to the test. |
35 function getUserMediaAndExpectFailure(constraints) { | 37 function getUserMediaAndExpectFailure(constraints) { |
36 console.log('Calling getUserMediaAndExpectFailure.'); | 38 console.log('Calling getUserMediaAndExpectFailure.'); |
37 navigator.webkitGetUserMedia( | 39 navigator.webkitGetUserMedia( |
38 constraints, | 40 constraints, |
39 function(stream) { failTest('Unexpectedly succeeded getUserMedia.'); }, | 41 function(stream) { failTest('Unexpectedly succeeded getUserMedia.'); }, |
40 function(error) { sendValueToTest(error.name); }); | 42 function(error) { sendValueToTest(error.name); }); |
41 } | 43 } |
42 | 44 |
43 function renderClonedMediastreamAndStop(constraints, waitTimeInSeconds) { | 45 function renderClonedMediastreamAndStop(constraints, waitTimeInSeconds) { |
44 console.log('Calling renderClonedMediastreamAndStop.'); | 46 console.log('Calling renderClonedMediastreamAndStop.'); |
45 navigator.webkitGetUserMedia( | 47 navigator.webkitGetUserMedia( |
46 constraints, | 48 constraints, |
47 function(stream) { | 49 function(stream) { |
48 var s = stream.clone(); | 50 var duplicate = stream.clone(); |
49 assertEquals(stream.getVideoTracks().length, 1); | 51 assertEquals(stream.getVideoTracks().length, 1); |
50 assertEquals(s.getVideoTracks().length, 1); | 52 assertEquals(duplicate.getVideoTracks().length, 1); |
51 assertNotEquals(stream.getVideoTracks()[0].id, | 53 assertNotEquals(stream.getVideoTracks()[0].id, |
52 s.getVideoTracks()[0].id); | 54 duplicate.getVideoTracks()[0].id); |
53 displayAndDetectVideo( | 55 detectVideoInLocalView1( |
54 s, | 56 stream, |
55 function() { | 57 function() { |
56 reportTestSuccess(); | 58 reportTestSuccess(); |
57 }); | 59 }); |
58 }, | 60 }, |
59 failedCallback); | 61 failedCallback); |
60 } | 62 } |
61 | 63 |
62 function renderDuplicatedMediastreamAndStop(constraints, waitTimeInSeconds) { | 64 function renderDuplicatedMediastreamAndStop(constraints, waitTimeInSeconds) { |
63 console.log('Calling renderDuplicateMediastreamAndStop.'); | 65 console.log('Calling renderDuplicateMediastreamAndStop.'); |
64 navigator.webkitGetUserMedia( | 66 navigator.webkitGetUserMedia( |
65 constraints, | 67 constraints, |
66 function(stream) { | 68 function(stream) { |
67 s = new webkitMediaStream(stream); | 69 var duplicate = new webkitMediaStream(stream); |
68 assertEquals(stream.getVideoTracks().length, 1); | 70 assertEquals(stream.getVideoTracks().length, 1); |
69 assertEquals(s.getVideoTracks().length, 1); | 71 assertEquals(duplicate.getVideoTracks().length, 1); |
70 assertEquals(stream.getVideoTracks()[0].id, | 72 assertEquals(stream.getVideoTracks()[0].id, |
71 s.getVideoTracks()[0].id); | 73 duplicate.getVideoTracks()[0].id); |
72 displayAndDetectVideo( | 74 detectVideoInLocalView1( |
73 s, | 75 duplicate, |
74 function() { | 76 function() { |
75 reportTestSuccess(); | 77 reportTestSuccess(); |
76 }); | 78 }); |
77 }, | 79 }, |
78 failedCallback); | 80 failedCallback); |
79 } | 81 } |
80 | 82 |
81 function renderSameTrackMediastreamAndStop(constraints, waitTimeInSeconds) { | 83 function renderSameTrackMediastreamAndStop(constraints, waitTimeInSeconds) { |
82 console.log('Calling renderSameTrackMediastreamAndStop.'); | 84 console.log('Calling renderSameTrackMediastreamAndStop.'); |
83 navigator.webkitGetUserMedia( | 85 navigator.webkitGetUserMedia( |
84 constraints, | 86 constraints, |
85 function(stream) { | 87 function(stream) { |
86 s = new webkitMediaStream(); | 88 var duplicate = new webkitMediaStream(); |
87 s.addTrack(stream.getVideoTracks()[0]); | 89 duplicate.addTrack(stream.getVideoTracks()[0]); |
88 assertEquals(s.getVideoTracks().length, 1); | 90 assertEquals(duplicate.getVideoTracks().length, 1); |
89 assertEquals(s.getVideoTracks().length, 1); | 91 assertEquals(duplicate.getVideoTracks().length, 1); |
90 assertEquals(stream.getVideoTracks()[0].id, s.getVideoTracks()[0].id); | 92 assertEquals(stream.getVideoTracks()[0].id, |
91 displayAndDetectVideo( | 93 duplicate.getVideoTracks()[0].id); |
92 s, | 94 detectVideoInLocalView1( |
| 95 duplicate, |
93 function() { | 96 function() { |
94 reportTestSuccess(); | 97 reportTestSuccess(); |
95 }); | 98 }); |
96 }, | 99 }, |
97 failedCallback); | 100 failedCallback); |
98 } | 101 } |
99 | 102 |
100 function renderClonedTrackMediastreamAndStop(constraints, waitTimeInSeconds) { | 103 function renderClonedTrackMediastreamAndStop(constraints, waitTimeInSeconds) { |
101 console.log('Calling renderClonedTrackMediastreamAndStop.'); | 104 console.log('Calling renderClonedTrackMediastreamAndStop.'); |
102 navigator.webkitGetUserMedia( | 105 navigator.webkitGetUserMedia( |
103 constraints, | 106 constraints, |
104 function(stream) { | 107 function(stream) { |
105 s = new webkitMediaStream(); | 108 var duplicate = new webkitMediaStream(); |
106 s.addTrack(stream.getVideoTracks()[0].clone()); | 109 duplicate.addTrack(stream.getVideoTracks()[0].clone()); |
107 assertEquals(s.getVideoTracks().length, 1); | 110 assertEquals(duplicate.getVideoTracks().length, 1); |
108 assertEquals(s.getVideoTracks().length, 1); | 111 assertEquals(duplicate.getVideoTracks().length, 1); |
109 assertNotEquals(stream.getVideoTracks()[0].id, | 112 assertNotEquals(stream.getVideoTracks()[0].id, |
110 s.getVideoTracks()[0].id) | 113 duplicate.getVideoTracks()[0].id) |
111 displayAndDetectVideo( | 114 detectVideoInLocalView1( |
112 s, | 115 duplicate, |
113 function() { | 116 function() { |
114 reportTestSuccess(); | 117 reportTestSuccess(); |
115 }); | 118 }); |
116 }, | 119 }, |
117 failedCallback); | 120 failedCallback); |
118 } | 121 } |
119 | 122 |
120 // Creates a MediaStream and renders it locally. When the video is detected to | 123 // Creates a MediaStream and renders it locally. When the video is detected to |
121 // be rolling we return ok-stream-running through the automation controller. | 124 // be rolling we report success. The acquired stream is stored in window |
122 function getUserMediaAndGetStreamUp(constraints, waitTimeInSeconds) { | 125 // under the name |streamName|. |
| 126 function getUserMediaAndGetStreamUp(constraints, streamName) { |
123 console.log('Calling getUserMediaAndGetStreamUp.'); | 127 console.log('Calling getUserMediaAndGetStreamUp.'); |
124 navigator.webkitGetUserMedia( | 128 navigator.webkitGetUserMedia( |
125 constraints, | 129 constraints, |
126 function(stream) { | 130 function(stream) { |
127 displayAndDetectVideo( | 131 window[streamName] = stream; |
| 132 detectVideoInLocalView1( |
128 stream, | 133 stream, |
129 function() { | 134 function() { |
130 reportTestSuccess(); | 135 reportTestSuccess(); |
131 }); | 136 }); |
132 }, | 137 }, |
133 failedCallback); | 138 failedCallback); |
134 } | 139 } |
135 | 140 |
136 function getUserMediaAndRenderInSeveralVideoTags() { | 141 function getUserMediaAndRenderInSeveralVideoTags() { |
137 navigator.webkitGetUserMedia( | 142 navigator.webkitGetUserMedia( |
(...skipping 15 matching lines...) Expand all Loading... |
153 function getUserMediaAndClone() { | 158 function getUserMediaAndClone() { |
154 console.log('Calling getUserMediaAndClone.'); | 159 console.log('Calling getUserMediaAndClone.'); |
155 navigator.webkitGetUserMedia({video: true, audio: true}, | 160 navigator.webkitGetUserMedia({video: true, audio: true}, |
156 createAndRenderClone, failedCallback); | 161 createAndRenderClone, failedCallback); |
157 } | 162 } |
158 | 163 |
159 // Creates two MediaStream and renders them locally. When the video of both | 164 // Creates two MediaStream and renders them locally. When the video of both |
160 // streams are detected to be rolling, we stop the local video tracks one at | 165 // streams are detected to be rolling, we stop the local video tracks one at |
161 // the time. | 166 // the time. |
162 function twoGetUserMediaAndStop(constraints) { | 167 function twoGetUserMediaAndStop(constraints) { |
163 console.log('Calling Two GetUserMedia'); | 168 // TODO(phoglund): make gUM requests in parallel; this test is too slow |
| 169 // and flakes on slow bots (http://crbug.com/417756). The current cycle of |
| 170 // detect - gum - detect - gum - stop - detect - stop - detect contains too |
| 171 // many detection phases. On bots with GPU emulation this looks to be really |
| 172 // slow, so I was thinking we could at least get video up for both streams |
| 173 // simultaneously and thereby run the first two detects in parallel. |
| 174 var stream1 = null; |
| 175 var stream2 = null; |
164 navigator.webkitGetUserMedia( | 176 navigator.webkitGetUserMedia( |
165 constraints, | 177 constraints, |
166 function(stream) { | 178 function(stream) { |
167 displayAndDetectVideo(stream, requestSecondGetUserMedia); | 179 stream1 = stream; |
| 180 detectVideoInLocalView1(stream, requestSecondGetUserMedia); |
168 }, | 181 }, |
169 failedCallback); | 182 failedCallback); |
170 var requestSecondGetUserMedia = function() { | 183 var requestSecondGetUserMedia = function() { |
171 navigator.webkitGetUserMedia( | 184 navigator.webkitGetUserMedia( |
172 constraints, | 185 constraints, |
173 function(stream) { | 186 function(stream) { |
174 displayIntoVideoElement(stream, | 187 stream2 = stream; |
175 function() { | 188 attachMediaStream(stream, 'local-view-2'); |
176 stopBothVideoTracksAndVerify(stream); | 189 detectVideoPlaying('local-view-2', stopBothVideoTracksAndVerify); |
177 }, | |
178 'local-view-2'); | |
179 }, | 190 }, |
180 failedCallback); | 191 failedCallback); |
181 }; | 192 }; |
182 | 193 |
183 var stopBothVideoTracksAndVerify = function(streamPlayingInLocalView2) { | 194 var stopBothVideoTracksAndVerify = function() { |
184 streamPlayingInLocalView2.getVideoTracks()[0].stop(); | 195 // Stop track 2, ensure that stops track 2 but not track 1, then stop |
| 196 // track 1. |
| 197 stream2.getVideoTracks()[0].stop(); |
| 198 waitForVideoToStop('local-view-1'); |
185 waitForVideoToStop('local-view-2'); | 199 waitForVideoToStop('local-view-2'); |
186 // Make sure the video track in gLocalStream is still playing in | 200 detectVideoInLocalView1(stream1, function() { |
187 // 'local-view1' and then stop it. | 201 stream1.getVideoTracks()[0].stop(); |
188 displayAndDetectVideo(gLocalStream, stopVideoTrack); | 202 }); |
189 }; | 203 }; |
190 } | 204 } |
191 | 205 |
192 function twoGetUserMedia(constraints1, | 206 function twoGetUserMedia(constraints1, |
193 constraints2) { | 207 constraints2) { |
| 208 // TODO(phoglund): see TODO on twoGetUserMediaAndStop. |
194 var result=""; | 209 var result=""; |
195 navigator.webkitGetUserMedia( | 210 navigator.webkitGetUserMedia( |
196 constraints1, | 211 constraints1, |
197 function(stream) { | 212 function(stream) { |
198 displayDetectAndAnalyzeVideoInElement( | 213 displayDetectAndAnalyzeVideoInElement( |
199 stream, | 214 stream, |
200 function(aspectRatio) { | 215 function(aspectRatio) { |
201 result = aspectRatio; | 216 result = aspectRatio; |
202 requestSecondGetUserMedia(); | 217 requestSecondGetUserMedia(); |
203 }, | 218 }, |
204 'local-view'); | 219 'local-view-1'); |
205 }, | 220 }, |
206 failedCallback); | 221 failedCallback); |
207 var requestSecondGetUserMedia = function() { | 222 var requestSecondGetUserMedia = function() { |
208 navigator.webkitGetUserMedia( | 223 navigator.webkitGetUserMedia( |
209 constraints2, | 224 constraints2, |
210 function(stream) { | 225 function(stream) { |
211 displayDetectAndAnalyzeVideoInElement( | 226 displayDetectAndAnalyzeVideoInElement( |
212 stream, | 227 stream, |
213 function(aspectRatio) { | 228 function(aspectRatio) { |
214 result = result + '-' + aspectRatio; | 229 result = result + '-' + aspectRatio; |
215 sendValueToTest(result); | 230 sendValueToTest(result); |
216 }, | 231 }, |
217 'local-view-2'); | 232 'local-view-2'); |
218 }, | 233 }, |
219 failedCallback); | 234 failedCallback); |
220 } | 235 } |
221 } | 236 } |
222 | 237 |
223 // Calls GetUserMedia twice and verify that the frame rate is as expected for | 238 // Calls GetUserMedia twice and verify that the frame rate is as expected for |
224 // both streams. | 239 // both streams. |
225 function twoGetUserMediaAndVerifyFrameRate(constraints1, | 240 function twoGetUserMediaAndVerifyFrameRate(constraints1, |
226 constraints2, | 241 constraints2, |
227 expected_frame_rate1, | 242 expected_frame_rate1, |
228 expected_frame_rate2) { | 243 expected_frame_rate2) { |
| 244 // TODO(phoglund): see TODO on twoGetUserMediaAndStop. |
229 addExpectedEvent(); | 245 addExpectedEvent(); |
230 addExpectedEvent(); | 246 addExpectedEvent(); |
231 var validateFrameRateCallback = function (success) { | 247 var validateFrameRateCallback = function (success) { |
232 if (!success) | 248 if (!success) |
233 failTest("Failed to validate frameRate."); | 249 failTest("Failed to validate frameRate."); |
234 eventOccured(); | 250 eventOccured(); |
235 }; | 251 }; |
236 | 252 |
237 navigator.webkitGetUserMedia( | 253 navigator.webkitGetUserMedia( |
238 constraints1, | 254 constraints1, |
239 function(stream) { | 255 function(stream) { |
240 requestSecondGetUserMedia(); | 256 requestSecondGetUserMedia(); |
241 plugStreamIntoVideoElement(stream, 'local-view'); | 257 attachMediaStream(stream, 'local-view-1'); |
242 detectVideoPlaying('local-view', | 258 detectVideoPlaying('local-view-1', |
243 function() { | 259 function() { |
244 validateFrameRate('local-view', expected_frame_rate1, | 260 validateFrameRate('local-view-1', expected_frame_rate1, |
245 validateFrameRateCallback); | 261 validateFrameRateCallback); |
246 }); | 262 }); |
247 }, | 263 }, |
248 failedCallback); | 264 failedCallback); |
249 var requestSecondGetUserMedia = function() { | 265 var requestSecondGetUserMedia = function() { |
250 navigator.webkitGetUserMedia( | 266 navigator.webkitGetUserMedia( |
251 constraints2, | 267 constraints2, |
252 function(stream) { | 268 function(stream) { |
253 plugStreamIntoVideoElement(stream, 'local-view-2'); | 269 attachMediaStream(stream, 'local-view-2'); |
254 detectVideoPlaying('local-view-2', | 270 detectVideoPlaying('local-view-2', |
255 function() { | 271 function() { |
256 validateFrameRate('local-view-2', expected_frame_rate2, | 272 validateFrameRate('local-view-2', expected_frame_rate2, |
257 validateFrameRateCallback); | 273 validateFrameRateCallback); |
258 }); | 274 }); |
259 }, | 275 }, |
260 failedCallback); | 276 failedCallback); |
261 } | 277 } |
262 } | 278 } |
263 | 279 |
264 function failedCallback(error) { | 280 function failedCallback(error) { |
265 failTest('GetUserMedia call failed with code ' + error.code); | 281 failTest('GetUserMedia call failed with code ' + error.code); |
266 } | 282 } |
267 | 283 |
268 function plugStreamIntoVideoElement(stream, videoElement) { | 284 function attachMediaStream(stream, videoElement) { |
269 gLocalStream = stream; | |
270 var localStreamUrl = URL.createObjectURL(stream); | 285 var localStreamUrl = URL.createObjectURL(stream); |
271 $(videoElement).src = localStreamUrl; | 286 $(videoElement).src = localStreamUrl; |
272 } | 287 } |
273 | 288 |
274 function displayIntoVideoElement(stream, callback, videoElement) { | 289 function detectVideoInLocalView1(stream, callback) { |
275 plugStreamIntoVideoElement(stream, videoElement); | 290 attachMediaStream(stream, 'local-view-1'); |
276 detectVideoPlaying(videoElement, callback); | 291 detectVideoPlaying('local-view-1', callback); |
277 } | |
278 | |
279 function displayAndDetectVideo(stream, callback) { | |
280 displayIntoVideoElement(stream, callback, 'local-view'); | |
281 } | 292 } |
282 | 293 |
283 function displayDetectAndAnalyzeVideo(stream) { | 294 function displayDetectAndAnalyzeVideo(stream) { |
284 displayDetectAndAnalyzeVideoInElement(stream, | 295 displayDetectAndAnalyzeVideoInElement(stream, |
285 function(aspectRatio) { | 296 function(aspectRatio) { |
286 sendValueToTest(aspectRatio); | 297 sendValueToTest(aspectRatio); |
287 }, | 298 }, |
288 'local-view'); | 299 'local-view-1'); |
289 } | 300 } |
290 | 301 |
291 function displayDetectAndAnalyzeVideoInElement( | 302 function displayDetectAndAnalyzeVideoInElement( |
292 stream, callback, videoElement) { | 303 stream, callback, videoElement) { |
293 plugStreamIntoVideoElement(stream, videoElement); | 304 attachMediaStream(stream, videoElement); |
294 detectAspectRatio(callback, videoElement); | 305 detectAspectRatio(callback, videoElement); |
295 } | 306 } |
296 | 307 |
297 function createAndRenderClone(stream) { | 308 function createAndRenderClone(stream) { |
298 gLocalStream = stream; | |
299 // TODO(perkj): --use-fake-device-for-media-stream do not currently | 309 // TODO(perkj): --use-fake-device-for-media-stream do not currently |
300 // work with audio devices and not all bots has a microphone. | 310 // work with audio devices and not all bots has a microphone. |
301 new_stream = new webkitMediaStream(); | 311 newStream = new webkitMediaStream(); |
302 new_stream.addTrack(stream.getVideoTracks()[0]); | 312 newStream.addTrack(stream.getVideoTracks()[0]); |
303 assertEquals(new_stream.getVideoTracks().length, 1); | 313 assertEquals(newStream.getVideoTracks().length, 1); |
304 if (stream.getAudioTracks().length > 0) { | 314 if (stream.getAudioTracks().length > 0) { |
305 new_stream.addTrack(stream.getAudioTracks()[0]); | 315 newStream.addTrack(stream.getAudioTracks()[0]); |
306 assertEquals(new_stream.getAudioTracks().length, 1); | 316 assertEquals(newStream.getAudioTracks().length, 1); |
307 new_stream.removeTrack(new_stream.getAudioTracks()[0]); | 317 newStream.removeTrack(newStream.getAudioTracks()[0]); |
308 assertEquals(new_stream.getAudioTracks().length, 0); | 318 assertEquals(newStream.getAudioTracks().length, 0); |
309 } | 319 } |
310 | 320 |
311 var newStreamUrl = URL.createObjectURL(new_stream); | 321 detectVideoInLocalView1(newStream, reportTestSuccess); |
312 $('local-view').src = newStreamUrl; | |
313 waitForVideo('local-view'); | |
314 } | 322 } |
315 | 323 |
316 function stopVideoTrack() { | 324 // Calls stop on |stream|'s video track after a delay and reports success. |
317 gLocalStream.getVideoTracks()[0].stop(); | 325 function waitAndStopVideoTrack(stream, waitTimeInSeconds) { |
318 waitForVideoToStop('local-view'); | 326 setTimeout(function() { |
319 } | 327 stream.getVideoTracks()[0].stop(); |
320 | 328 reportTestSuccess(); |
321 function waitAndStopVideoTrack(waitTimeInSeconds) { | 329 }, waitTimeInSeconds * 1000); |
322 setTimeout(stopVideoTrack, waitTimeInSeconds * 1000); | |
323 } | 330 } |
324 | 331 |
325 // This test make sure multiple video renderers can be created for the same | 332 // This test make sure multiple video renderers can be created for the same |
326 // local video track and make sure a renderer can still render if other | 333 // local video track and make sure a renderer can still render if other |
327 // renderers are paused. See http://crbug/352619. | 334 // renderers are paused. See http://crbug/352619. |
328 function createMultipleVideoRenderersAndPause(stream) { | 335 function createMultipleVideoRenderersAndPause(stream) { |
329 function createDetectableRenderer(stream, id) { | 336 function createDetectableRenderer(stream, id) { |
330 var video = document.createElement('video'); | 337 var video = document.createElement('video'); |
331 document.body.appendChild(video); | 338 document.body.appendChild(video); |
332 var localStreamUrl = URL.createObjectURL(stream); | 339 var localStreamUrl = URL.createObjectURL(stream); |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
441 console.log(result); | 448 console.log(result); |
442 callback(result); | 449 callback(result); |
443 } | 450 } |
444 } | 451 } |
445 var detectorInterval = setInterval(detectorFunction, 50); | 452 var detectorInterval = setInterval(detectorFunction, 50); |
446 } | 453 } |
447 </script> | 454 </script> |
448 </head> | 455 </head> |
449 <body> | 456 <body> |
450 <table border="0"> | 457 <table border="0"> |
| 458 <!-- Canvases are named after their corresponding video elements. --> |
451 <tr> | 459 <tr> |
452 <td><video width="320" height="240" id="local-view" | 460 <td><video id="local-view-1" width="320" height="240" autoplay |
453 autoplay="autoplay" style="display:none"></video></td> | 461 style="display:none"></video></td> |
454 <td><canvas id="local-view-canvas" | 462 <td><canvas id="local-view-1-canvas" width="320" height="240" |
455 style="display:none"></canvas></td> | 463 style="display:none"></canvas></td> |
456 </tr> | 464 </tr> |
457 <tr> | 465 <tr> |
458 <td><video width="320" height="240" id="local-view-2" | 466 <td><video id="local-view-2" width="320" height="240" autoplay |
459 autoplay style="display:none"></video></td> | 467 style="display:none"></video></td> |
460 <!-- Canvases are named after their corresponding video elements. --> | 468 <td><canvas id="local-view-2-canvas" width="320" height="240" |
461 <td><canvas width="320" height="240" id="local-view-2-canvas" | |
462 style="display:none"></canvas></td> | 469 style="display:none"></canvas></td> |
463 </tr> | 470 </tr> |
464 </table> | 471 </table> |
465 </body> | 472 </body> |
466 </html> | 473 </html> |
OLD | NEW |