| 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;
|
| +})();
|
|
|