OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // This file contains common utilities to find video/audio elements on a page | |
6 // and collect metrics for each. | |
7 | |
8 (function() { | |
9 // Media recorder class responsible for recording and performing actions on a | |
10 // media element. It attaches required event listeners in order to collect | |
11 // different metrics and allows the execution of different media actions. | |
12 function MediaRecorder(element) { | |
13 this.metrics = {}; | |
14 this.info = {}; | |
15 this.element = element; | |
16 this.isHTML5 = element instanceof HTMLMediaElement; | |
scherkus (not reviewing)
2013/06/14 20:50:45
when would we create a MediaRecorder for a non-med
shadi
2013/06/17 19:13:59
The plan is that we will add support for non HTML5
| |
17 if (this.isHTML5) | |
18 this.initHTML5(); | |
19 } | |
20 | |
21 MediaRecorder.prototype.play = function() { | |
22 if (this.isHTML5) { | |
23 this.ttp_timer = new Timer(); | |
24 this.element.play(); | |
25 } else { | |
26 // How to raise an error? log? | |
27 Error('We can not automatically play this type of element'); | |
28 } | |
29 }; | |
30 | |
31 MediaRecorder.prototype.initHTML5 = function() { | |
32 // Set the basic event handlers for HTML5 media element. | |
33 var recorder = this; | |
34 function onVideoLoad(event) { | |
35 // If a 'play' action is performed, then ttp_time != undefined. | |
36 if (recorder.ttp_timer == undefined) | |
37 recorder.ttp_timer = new Timer(); | |
38 } | |
39 // For the cases where autoplay=true, and without a 'play' action, we want | |
scherkus (not reviewing)
2013/06/14 20:50:45
are we going to have tests w/ autoplay or should w
shadi
2013/06/17 19:13:59
Videostack tests won't have autoplay, but media me
| |
40 // to start ttp_timer at 'play' or 'loadedmetadata' events. | |
41 this.element.addEventListener('play', onVideoLoad); | |
42 this.element.addEventListener('loadedmetadata', onVideoLoad); | |
43 this.element.addEventListener('playing', function(e) { | |
44 recorder.onPlaying(e); | |
45 }); | |
46 this.element.addEventListener('ended', function(e) { | |
47 recorder.onEnded(e); | |
48 }); | |
49 this.element.addEventListener('error', function(e) { | |
50 recorder.onError(e); | |
51 }); | |
52 this.element.addEventListener('abort', function(e) { | |
53 recorder.onError(e); | |
54 }); | |
55 }; | |
56 | |
57 MediaRecorder.prototype.onPlaying = function(event) { | |
58 this.playing = true; | |
59 this.metrics['ttp'] = [this.ttp_timer.stop(), 'sec']; | |
60 }; | |
61 | |
62 MediaRecorder.prototype.onEnded = function(event) { | |
63 this.ended = true; | |
64 this.metrics['playback_time'] = [this.ttp_timer.stop(), 'sec']; | |
65 }; | |
66 | |
67 MediaRecorder.prototype.onError = function(event) { | |
68 // TODO(shadi): communicate to Telemetry that an error has occured. | |
69 console.log('Media failed to play: ' + event.type); | |
70 }; | |
71 | |
72 MediaRecorder.prototype.getElementInfo = function() { | |
73 // Return information about the media element for this recorder. | |
74 if (this.element.id) | |
75 this.info['id'] = this.element.id; | |
76 if (this.isHTML5) { | |
77 // Add insteresting media specific tags. | |
78 if (this.element.src) | |
79 this.info['src'] = this.element.src; | |
80 } | |
81 return this.info; | |
82 }; | |
83 | |
84 MediaRecorder.prototype.getMetrics = function() { | |
85 // Returns all recorded metrics for the element | |
86 if (this.isHTML5) { | |
87 this.metrics['decoded_frame_count'] = | |
88 [this.element.webkitDecodedFrameCount, 'frames']; | |
89 this.metrics['dropped_frame_count'] = | |
90 [this.element.webkitDroppedFrameCount, 'frames']; | |
91 this.metrics['decoded_video_bytes'] = | |
92 [this.element.webkitVideoDecodedByteCount, 'bytes']; | |
93 this.metrics['decoded_audio_bytes'] = | |
94 [this.element.webkitAudioDecodedByteCount, 'bytes']; | |
95 } | |
96 return this.metrics; | |
97 }; | |
98 | |
99 MediaRecorder.prototype.getSummary = function() { | |
100 return { | |
101 'info': this.getElementInfo(), | |
102 'metrics': this.getMetrics() | |
103 }; | |
104 }; | |
105 | |
106 MediaRecorder.prototype.hasEventCompleted = function(event_name) { | |
107 return this[event_name] == true; | |
108 }; | |
109 | |
110 function Timer() { | |
111 this.start_ = getCurrentTime(); | |
112 this.times_ = []; | |
113 } | |
114 | |
115 Timer.prototype = { | |
116 start: function() { | |
117 this.start_ = getCurrentTime(); | |
118 }, | |
119 | |
120 stop: function() { | |
121 // Store time logs in secs. | |
122 var delta = (getCurrentTime() - this.start_) / 1000; | |
123 this.times_.push(delta); | |
124 return delta; | |
125 } | |
126 }; | |
127 | |
128 function CreateMediaRecorders() { | |
129 // Searches for all video and audio elements on the page and creates a | |
130 // corresponding media recorder instance for each. | |
131 var media = document.getElementsByTagName('video'); | |
132 for (var i = 0; i < media.length; i++) { | |
133 mediaRecorders.push(new MediaRecorder(media[i])); | |
134 } | |
135 media = document.getElementsByTagName('audio'); | |
136 for (var i = 0; i < media.length; i++) { | |
137 mediaRecorders.push(new MediaRecorder(media[i])); | |
138 } | |
139 } | |
140 | |
141 function getCurrentTime() { | |
142 if (window.performance) | |
143 return (performance.now || | |
144 performance.mozNow || | |
145 performance.msNow || | |
146 performance.oNow || | |
147 performance.webkitNow).call(window.performance); | |
148 else | |
149 return Date().getTime(); | |
150 } | |
151 | |
152 function getAllMetrics() { | |
153 // Returns a summary (info + metrics) for all media recorders. | |
154 var metrics = []; | |
155 for (var i = 0; i < mediaRecorders.length; i++) { | |
156 metrics.push(mediaRecorders[i].getSummary()); | |
157 } | |
158 return metrics; | |
159 } | |
160 | |
161 function findMediaRecorders(selector) { | |
162 // Returns element matching the selector, otherwise returns the first video | |
163 // or audio tag element that can be found. | |
164 // If selector == 'all', returns all media recorders. | |
165 if (selector == 'all') { | |
166 return mediaRecorders; | |
167 } | |
168 var mediaElement = null; | |
169 if (selector) { | |
170 mediaElement = document.querySelector(selector); | |
171 } else { | |
172 var media = document.getElementsByTagName('video'); | |
173 if (media.length > 0) { | |
174 mediaElement = media[0]; | |
175 } else { | |
176 media = document.getElementsByTagName('audio'); | |
177 if (media.length > 0) { | |
178 mediaElement = media[0]; | |
179 } | |
180 } | |
181 } | |
182 if (mediaElement) { | |
183 for (var i = 0; i < mediaRecorders.length; i++) { | |
184 if (mediaElement == mediaRecorders[i].element) | |
185 return [mediaRecorders[i]]; | |
186 } | |
187 } | |
188 return []; | |
189 } | |
190 | |
191 function playMedia(selector) { | |
192 // Performs the "Play" action on media satisfying selector. | |
193 var recorders = findMediaRecorders(selector); | |
194 for (var i = 0; i < recorders.length; i++) { | |
195 recorders[i].play(); | |
196 } | |
197 } | |
198 | |
199 function hasEventCompleted(selector, event_name) { | |
200 // Return true if the event_name fired for media satisfying the selector. | |
201 var recorders = findMediaRecorders(selector); | |
202 for (var i = 0; i < recorders.length; i++) { | |
203 if (!recorders[i].hasEventCompleted(event_name)) | |
204 return false; | |
205 } | |
206 return true; | |
207 } | |
208 | |
209 // Stores recorders for all media elements on the page upon loading. | |
210 var mediaRecorders = []; | |
211 window.__mediaRecorders = mediaRecorders; | |
212 window.__getAllMetrics = getAllMetrics; | |
213 window.__playMedia = playMedia; | |
214 window.__hasEventCompleted = hasEventCompleted; | |
215 CreateMediaRecorders(); | |
216 })(); | |
OLD | NEW |