Index: tools/perf/perf_tools/media_metrics.js |
diff --git a/tools/perf/perf_tools/media_metrics.js b/tools/perf/perf_tools/media_metrics.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..534e53a9ad9c6579ac01089a416e1492f22f8e7a |
--- /dev/null |
+++ b/tools/perf/perf_tools/media_metrics.js |
@@ -0,0 +1,152 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// This file contains common utilities to find video/audio elements on a page |
+// and collect metrics for each. |
+ |
+(function() { |
+ // MediaMetric class responsible for collecting metrics on a media element. |
+ // It attaches required event listeners in order to collect different metrics. |
+ function MediaMetricBase(element) { |
+ checkElementIsNotBound(element); |
+ this.metrics = {}; |
+ this.id = ''; |
+ this.element = element; |
+ // Listen to when a Telemetry 'Play' action gets called. |
+ // TODO(shadi): Add event listeners for other media actions here. |
+ if (this.element) |
+ this.element.addEventListener('willPlay', this.onWillPlay, false); |
+ } |
+ |
+ MediaMetricBase.prototype.getMetrics = function() { |
+ return this.metrics; |
+ }; |
+ |
+ MediaMetricBase.prototype.getSummary = function() { |
+ return { |
+ 'id': this.id, |
+ 'metrics': this.getMetrics() |
+ }; |
+ }; |
+ |
+ MediaMetricBase.prototype.onWillPlay = function() { |
+ this.playbackTimer = new Timer(); |
+ }; |
+ |
+ function HTMLMediaMetric(element) { |
+ MediaMetricBase.prototype.constructor.call(this, element); |
+ // Set the basic event handlers for HTML5 media element. |
+ var metric = this; |
+ function onVideoLoad(event) { |
+ // If a 'Play' action is performed, then playback_timer != undefined. |
+ if (metric.playbackTimer == undefined) |
+ metric.playbackTimer = new Timer(); |
+ } |
+ // For the cases where autoplay=true, and without a 'play' action, we want |
+ // to start playbackTimer at 'play' or 'loadedmetadata' events. |
+ this.element.addEventListener('play', onVideoLoad); |
+ this.element.addEventListener('loadedmetadata', onVideoLoad); |
+ this.element.addEventListener('playing', function(e) { |
+ metric.onPlaying(e); |
+ }); |
+ this.element.addEventListener('ended', function(e) { |
+ metric.onEnded(e); |
+ }); |
+ this.setID(); |
+ } |
+ |
+ HTMLMediaMetric.prototype = new MediaMetricBase(); |
+ HTMLMediaMetric.prototype.constructor = HTMLMediaMetric; |
+ HTMLMediaMetric.prototype.setID = function() { |
+ if (this.element.src) |
+ this.id = this.element.src.substring(this.element.src.lastIndexOf("/")+1); |
+ else if (this.element.id) |
+ this.id = this.element.id; |
+ else |
+ this.id = 'media_' + window.__globalCounter++; |
+ }; |
+ |
+ HTMLMediaMetric.prototype.getMetrics = function() { |
+ this.metrics['decoded_frame_count'] = this.element.webkitDecodedFrameCount; |
+ this.metrics['dropped_frame_count'] = this.element.webkitDroppedFrameCount; |
+ this.metrics['decoded_video_bytes'] = |
+ this.element.webkitVideoDecodedByteCount; |
+ this.metrics['decoded_audio_bytes'] = |
+ this.element.webkitAudioDecodedByteCount; |
+ return this.metrics; |
+ }; |
+ |
+ HTMLMediaMetric.prototype.onPlaying = function(event) { |
+ // Playing event can fire more than once if seeking. |
+ if (!this.metrics['time_to_play']) |
+ this.metrics['time_to_play'] = this.playbackTimer.stop(); |
+ }; |
+ |
+ HTMLMediaMetric.prototype.onEnded = function(event) { |
+ this.metrics['playback_time'] = this.playbackTimer.stop(); |
+ }; |
+ |
+ function MediaMetric(element) { |
+ if (element instanceof HTMLMediaElement) |
+ return new HTMLMediaMetric(element); |
+ throw new Error('Unrecognized media element type.'); |
+ } |
+ |
+ function Timer() { |
+ this.start_ = 0; |
+ this.start(); |
+ } |
+ |
+ Timer.prototype = { |
+ start: function() { |
+ this.start_ = getCurrentTime(); |
+ }, |
+ |
+ stop: function() { |
+ // Return delta time since start in secs. |
+ return ((getCurrentTime() - this.start_) / 1000).toFixed(3); |
+ } |
+ }; |
+ |
+ function checkElementIsNotBound(element) { |
+ if (!element) |
+ return; |
+ for (var i = 0; i < window.__mediaMetrics.length; i++) { |
+ if (window.__mediaMetrics[i].element == element) |
+ throw new Error('Can not create MediaMetric for same element twice.'); |
+ } |
+ } |
+ |
+ function createMediaMetricsForDocument() { |
+ // Searches for all video and audio elements on the page and creates a |
+ // corresponding media metric instance for each. |
+ var mediaElements = document.querySelectorAll('video, audio'); |
+ for (var i = 0; i < mediaElements.length; i++) |
+ window.__mediaMetrics.push(new MediaMetric(mediaElements[i])); |
+ } |
+ |
+ function getCurrentTime() { |
+ if (window.performance) |
+ return (performance.now || |
+ performance.mozNow || |
+ performance.msNow || |
+ performance.oNow || |
+ performance.webkitNow).call(window.performance); |
+ else |
+ return Date.now(); |
+ } |
+ |
+ function getAllMetrics() { |
+ // Returns a summary (info + metrics) for all media metrics. |
+ var metrics = []; |
+ for (var i = 0; i < window.__mediaMetrics.length; i++) |
+ metrics.push(window.__mediaMetrics[i].getSummary()); |
+ return metrics; |
+ } |
+ |
+ window.__globalCounter = 0; |
+ window.__mediaMetrics = []; |
+ window.__getAllMetrics = getAllMetrics; |
+ window.__createMediaMetricsForDocument = createMediaMetricsForDocument; |
+})(); |