Chromium Code Reviews| 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; |
|
scherkus (not reviewing)
2013/06/14 20:50:45
when would we create a MediaRecorder for a non-med
shadi
2013/06/17 19:13:59
The plan is that we will add support for non HTML5
|
| + 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 |
|
scherkus (not reviewing)
2013/06/14 20:50:45
are we going to have tests w/ autoplay or should w
shadi
2013/06/17 19:13:59
Videostack tests won't have autoplay, but media me
|
| + // 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); |
| + for (var i = 0; i < recorders.length; i++) { |
| + recorders[i].play(); |
| + } |
| + } |
| + |
| + 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)) |
| + 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(); |
| +})(); |