shaka-packager/tools/perf/metrics/media.js

177 lines
5.6 KiB
JavaScript
Raw Normal View History

// Copyright 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;
}
MediaMetricBase.prototype.getMetrics = function() {
return this.metrics;
};
MediaMetricBase.prototype.getSummary = function() {
return {
'id': this.id,
'metrics': this.getMetrics()
};
};
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();
// Listen to when a Telemetry actions gets called.
this.element.addEventListener('willPlay', function (e) {
metric.onWillPlay(e);
}, false);
this.element.addEventListener('willSeek', function (e) {
metric.onWillSeek(e);
}, false);
}
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.onWillPlay = function(e) {
this.playbackTimer = new Timer();
};
HTMLMediaMetric.prototype.onWillSeek = function(e) {
var seekLabel = '';
if (e.seekLabel)
seekLabel = '_' + e.seekLabel;
var metric = this;
var onSeeked = function(e) {
metric.appendMetric('seek' + seekLabel, metric.seekTimer.stop())
e.target.removeEventListener('seeked', onSeeked);
};
this.seekTimer = new Timer();
this.element.addEventListener('seeked', onSeeked);
};
HTMLMediaMetric.prototype.appendMetric = function(metric, value) {
if (!this.metrics[metric])
this.metrics[metric] = [];
this.metrics[metric].push(value);
}
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();
};
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;
};
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;
})();