Re-land: C++ readability review
[chromium-blink-merge.git] / tools / perf / metrics / media.js
blobb99b5b3957603208ad9bd3cf2ea2567366eda918
1 // Copyright 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.
5 // This file contains common utilities to find video/audio elements on a page
6 // and collect metrics for each.
8 (function() {
9 // MediaMetric class responsible for collecting metrics on a media element.
10 // It attaches required event listeners in order to collect different metrics.
11 function MediaMetricBase(element) {
12 checkElementIsNotBound(element);
13 this.metrics = {};
14 this.id = '';
15 this.element = element;
18 MediaMetricBase.prototype.getMetrics = function() {
19 return this.metrics;
22 MediaMetricBase.prototype.getSummary = function() {
23 return {
24 'id': this.id,
25 'metrics': this.getMetrics()
29 function HTMLMediaMetric(element) {
30 MediaMetricBase.prototype.constructor.call(this, element);
31 // Set the basic event handlers for HTML5 media element.
32 var metric = this;
33 function onVideoLoad(event) {
34 // If a 'Play' action is performed, then playback_timer != undefined.
35 if (metric.playbackTimer == undefined)
36 metric.playbackTimer = new Timer();
38 // For the cases where autoplay=true, and without a 'play' action, we want
39 // to start playbackTimer at 'play' or 'loadedmetadata' events.
40 this.element.addEventListener('play', onVideoLoad);
41 this.element.addEventListener('loadedmetadata', onVideoLoad);
42 this.element.addEventListener('playing', function(e) {
43 metric.onPlaying(e);
44 });
45 this.element.addEventListener('ended', function(e) {
46 metric.onEnded(e);
47 });
48 this.setID();
50 // Listen to when a Telemetry actions gets called.
51 this.element.addEventListener('willPlay', function (e) {
52 metric.onWillPlay(e);
53 }, false);
54 this.element.addEventListener('willSeek', function (e) {
55 metric.onWillSeek(e);
56 }, false);
57 this.element.addEventListener('willLoop', function (e) {
58 metric.onWillLoop(e);
59 }, false);
62 HTMLMediaMetric.prototype = new MediaMetricBase();
63 HTMLMediaMetric.prototype.constructor = HTMLMediaMetric;
65 HTMLMediaMetric.prototype.setID = function() {
66 if (this.element.id)
67 this.id = this.element.id;
68 else if (this.element.src)
69 this.id = this.element.src.substring(this.element.src.lastIndexOf("/")+1);
70 else
71 this.id = 'media_' + window.__globalCounter++;
74 HTMLMediaMetric.prototype.onWillPlay = function(e) {
75 this.playbackTimer = new Timer();
78 HTMLMediaMetric.prototype.onWillSeek = function(e) {
79 var seekLabel = '';
80 if (e.seekLabel)
81 seekLabel = '_' + e.seekLabel;
82 var metric = this;
83 var onSeeked = function(e) {
84 metric.appendMetric('seek' + seekLabel, metric.seekTimer.stop())
85 e.target.removeEventListener('seeked', onSeeked);
87 this.seekTimer = new Timer();
88 this.element.addEventListener('seeked', onSeeked);
91 HTMLMediaMetric.prototype.onWillLoop = function(e) {
92 var loopTimer = new Timer();
93 var metric = this;
94 var loopCount = e.loopCount;
95 var onEndLoop = function(e) {
96 var actualDuration = loopTimer.stop();
97 var idealDuration = metric.element.duration * loopCount;
98 var avg_loop_time = (actualDuration - idealDuration) / loopCount;
99 metric.metrics['avg_loop_time'] =
100 Math.round(avg_loop_time * 1000) / 1000;
101 e.target.removeEventListener('endLoop', onEndLoop);
103 this.element.addEventListener('endLoop', onEndLoop);
106 HTMLMediaMetric.prototype.appendMetric = function(metric, value) {
107 if (!this.metrics[metric])
108 this.metrics[metric] = [];
109 this.metrics[metric].push(value);
112 HTMLMediaMetric.prototype.onPlaying = function(event) {
113 // Playing event can fire more than once if seeking.
114 if (!this.metrics['time_to_play'] && this.playbackTimer)
115 this.metrics['time_to_play'] = this.playbackTimer.stop();
118 HTMLMediaMetric.prototype.onEnded = function(event) {
119 var time_to_end = this.playbackTimer.stop() - this.metrics['time_to_play'];
120 // TODO(shadi): Measure buffering time more accurately using events such as
121 // stalled, waiting, progress, etc. This works only when continuous playback
122 // is used.
123 this.metrics['buffering_time'] = time_to_end - this.element.duration * 1000;
126 HTMLMediaMetric.prototype.getMetrics = function() {
127 var decodedFrames = this.element.webkitDecodedFrameCount;
128 var droppedFrames = this.element.webkitDroppedFrameCount;
129 // Audio media does not report decoded/dropped frame count
130 if (decodedFrames != undefined)
131 this.metrics['decoded_frame_count'] = decodedFrames;
132 if (droppedFrames != undefined)
133 this.metrics['dropped_frame_count'] = droppedFrames;
134 this.metrics['decoded_video_bytes'] =
135 this.element.webkitVideoDecodedByteCount || 0;
136 this.metrics['decoded_audio_bytes'] =
137 this.element.webkitAudioDecodedByteCount || 0;
138 return this.metrics;
141 function MediaMetric(element) {
142 if (element instanceof HTMLMediaElement)
143 return new HTMLMediaMetric(element);
144 throw new Error('Unrecognized media element type.');
147 function Timer() {
148 this.start_ = 0;
149 this.start();
152 Timer.prototype = {
153 start: function() {
154 this.start_ = getCurrentTime();
157 stop: function() {
158 // Return delta time since start in millisecs.
159 return Math.round((getCurrentTime() - this.start_) * 1000) / 1000;
163 function checkElementIsNotBound(element) {
164 if (!element)
165 return;
166 if (getMediaMetric(element))
167 throw new Error('Can not create MediaMetric for same element twice.');
170 function getMediaMetric(element) {
171 for (var i = 0; i < window.__mediaMetrics.length; i++) {
172 if (window.__mediaMetrics[i].element == element)
173 return window.__mediaMetrics[i];
175 return null;
178 function createMediaMetricsForDocument() {
179 // Searches for all video and audio elements on the page and creates a
180 // corresponding media metric instance for each.
181 var mediaElements = document.querySelectorAll('video, audio');
182 for (var i = 0; i < mediaElements.length; i++)
183 window.__mediaMetrics.push(new MediaMetric(mediaElements[i]));
186 function getCurrentTime() {
187 if (window.performance)
188 return (performance.now ||
189 performance.mozNow ||
190 performance.msNow ||
191 performance.oNow ||
192 performance.webkitNow).call(window.performance);
193 else
194 return Date.now();
197 function getAllMetrics() {
198 // Returns a summary (info + metrics) for all media metrics.
199 var metrics = [];
200 for (var i = 0; i < window.__mediaMetrics.length; i++)
201 metrics.push(window.__mediaMetrics[i].getSummary());
202 return metrics;
205 window.__globalCounter = 0;
206 window.__mediaMetrics = [];
207 window.__getMediaMetric = getMediaMetric;
208 window.__getAllMetrics = getAllMetrics;
209 window.__createMediaMetricsForDocument = createMediaMetricsForDocument;
210 })();