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..ed3f0331a9c0256e064658f245b50e19940f88b6 |
--- /dev/null |
+++ b/tools/perf/perf_tools/media_metrics.js |
@@ -0,0 +1,216 @@ |
+// 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() { |
+ // Media recorder class responsible for recording and performing actions on a |
+ // media element. It attaches required event listeners in order to collect |
+ // different metrics and allows the execution of different media actions. |
+ function MediaRecorder(element) { |
+ this.metrics = {}; |
+ this.info = {}; |
+ this.element = element; |
+ this.isHTML5 = element instanceof HTMLMediaElement; |
+ if (this.isHTML5) |
+ this.initHTML5(); |
+ } |
+ |
+ MediaRecorder.prototype.play = function() { |
+ if (this.isHTML5) { |
+ this.ttp_timer = new Timer(); |
+ this.element.play(); |
+ } else { |
+ // How to raise an error? log? |
+ Error('We can not automatically play this type of element'); |
+ } |
+ }; |
+ |
+ MediaRecorder.prototype.initHTML5 = function() { |
+ // Set the basic event handlers for HTML5 media element. |
+ var recorder = this; |
+ function onVideoLoad(event) { |
+ // If a 'play' action is performed, then ttp_time != undefined. |
+ if (recorder.ttp_timer == undefined) |
+ recorder.ttp_timer = new Timer(); |
+ } |
+ // For the cases where autoplay=true, and without a 'play' action, we want |
+ // to start ttp_timer at 'play' or 'loadedmetadata' events. |
+ this.element.addEventListener('play', onVideoLoad); |
+ this.element.addEventListener('loadedmetadata', onVideoLoad); |
+ this.element.addEventListener('playing', function(e) { |
+ recorder.onPlaying(e); |
+ }); |
+ this.element.addEventListener('ended', function(e) { |
+ recorder.onEnded(e); |
+ }); |
+ this.element.addEventListener('error', function(e) { |
+ recorder.onError(e); |
+ }); |
+ this.element.addEventListener('abort', function(e) { |
+ recorder.onError(e); |
+ }); |
+ }; |
+ |
+ MediaRecorder.prototype.onPlaying = function(event) { |
+ this.playing = true; |
+ this.metrics['ttp'] = [this.ttp_timer.stop(), 'sec']; |
+ }; |
+ |
+ MediaRecorder.prototype.onEnded = function(event) { |
+ this.ended = true; |
+ this.metrics['playback_time'] = [this.ttp_timer.stop(), 'sec']; |
+ }; |
+ |
+ MediaRecorder.prototype.onError = function(event) { |
+ // TODO(shadi): communicate to Telemetry that an error has occured. |
+ console.log('Media failed to play: ' + event.type); |
+ }; |
+ |
+ MediaRecorder.prototype.getElementInfo = function() { |
+ // Return information about the media element for this recorder. |
+ if (this.element.id) |
+ this.info['id'] = this.element.id; |
+ if (this.isHTML5) { |
+ // Add insteresting media specific tags. |
+ if (this.element.src) |
+ this.info['src'] = this.element.src; |
+ } |
+ return this.info; |
+ }; |
+ |
+ MediaRecorder.prototype.getMetrics = function() { |
+ // Returns all recorded metrics for the element |
+ if (this.isHTML5) { |
+ this.metrics['decoded_frame_count'] = |
+ [this.element.webkitDecodedFrameCount, 'frames']; |
+ this.metrics['dropped_frame_count'] = |
+ [this.element.webkitDroppedFrameCount, 'frames']; |
+ this.metrics['decoded_video_bytes'] = |
+ [this.element.webkitVideoDecodedByteCount, 'bytes']; |
+ this.metrics['decoded_audio_bytes'] = |
+ [this.element.webkitAudioDecodedByteCount, 'bytes']; |
+ } |
+ return this.metrics; |
+ }; |
+ |
+ MediaRecorder.prototype.getSummary = function() { |
+ return { |
+ 'info': this.getElementInfo(), |
+ 'metrics': this.getMetrics() |
+ }; |
+ }; |
+ |
+ MediaRecorder.prototype.hasEventCompleted = function(event_name) { |
+ return this[event_name] == true; |
+ }; |
+ |
+ function Timer() { |
+ this.start_ = getCurrentTime(); |
+ this.times_ = []; |
+ } |
+ |
+ Timer.prototype = { |
+ start: function() { |
+ this.start_ = getCurrentTime(); |
+ }, |
+ |
+ stop: function() { |
+ // Store time logs in secs. |
+ var delta = (getCurrentTime() - this.start_) / 1000; |
+ this.times_.push(delta); |
+ return delta; |
+ } |
+ }; |
+ |
+ function CreateMediaRecorders() { |
+ // Searches for all video and audio elements on the page and creates a |
+ // corresponding media recorder instance for each. |
+ var media = document.getElementsByTagName('video'); |
+ for (var i = 0; i < media.length; i++) { |
+ mediaRecorders.push(new MediaRecorder(media[i])); |
+ } |
+ media = document.getElementsByTagName('audio'); |
+ for (var i = 0; i < media.length; i++) { |
+ mediaRecorders.push(new MediaRecorder(media[i])); |
+ } |
+ } |
+ |
+ function getCurrentTime() { |
+ if (window.performance) |
+ return (performance.now || |
+ performance.mozNow || |
+ performance.msNow || |
+ performance.oNow || |
+ performance.webkitNow).call(window.performance); |
+ else |
+ return Date().getTime(); |
+ } |
+ |
+ function getAllMetrics() { |
+ // Returns a summary (info + metrics) for all media recorders. |
+ var metrics = []; |
+ for (var i = 0; i < mediaRecorders.length; i++) { |
+ metrics.push(mediaRecorders[i].getSummary()); |
+ } |
+ return metrics; |
+ } |
+ |
+ function findMediaRecorders(selector) { |
+ // Returns element matching the selector, otherwise returns the first video |
+ // or audio tag element that can be found. |
+ // If selector == 'all', returns all media recorders. |
+ if (selector == 'all') { |
+ return mediaRecorders; |
+ } |
+ var mediaElement = null; |
+ if (selector) { |
+ mediaElement = document.querySelector(selector); |
+ } else { |
+ var media = document.getElementsByTagName('video'); |
+ if (media.length > 0) { |
+ mediaElement = media[0]; |
+ } else { |
+ media = document.getElementsByTagName('audio'); |
+ if (media.length > 0) { |
+ mediaElement = media[0]; |
+ } |
+ } |
+ } |
+ if (mediaElement) { |
+ for (var i = 0; i < mediaRecorders.length; i++) { |
+ if (mediaElement == mediaRecorders[i].element) |
+ return [mediaRecorders[i]]; |
+ } |
+ } |
+ return []; |
+ } |
+ |
+ function playMedia(selector) { |
+ // Performs the "Play" action on media satisfying selector. |
+ var recorders = findMediaRecorders(selector); |
nduca
2013/06/19 18:12:03
this should throw an exception if none was found?
|
+ for (var i = 0; i < recorders.length; i++) { |
+ recorders[i].play(); |
nduca
2013/06/19 18:12:03
var e = document.createEvent('Event');
e.initEve
shadi
2013/06/21 00:39:51
Done.
|
+ } |
+ } |
+ |
+ function hasEventCompleted(selector, event_name) { |
+ // Return true if the event_name fired for media satisfying the selector. |
+ var recorders = findMediaRecorders(selector); |
+ for (var i = 0; i < recorders.length; i++) { |
+ if (!recorders[i].hasEventCompleted(event_name)) |
nduca
2013/06/19 18:12:03
hopefully this we can avoid this too :)
shadi
2013/06/21 00:39:51
Done.
|
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ // Stores recorders for all media elements on the page upon loading. |
+ var mediaRecorders = []; |
+ window.__mediaRecorders = mediaRecorders; |
+ window.__getAllMetrics = getAllMetrics; |
+ window.__playMedia = playMedia; |
+ window.__hasEventCompleted = hasEventCompleted; |
+ CreateMediaRecorders(); |
+})(); |