Chromium Code Reviews| 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 |