| Index: tracing/tracing/metrics/media_metric_test.html
|
| diff --git a/tracing/tracing/metrics/media_metric_test.html b/tracing/tracing/metrics/media_metric_test.html
|
| index 64ea02b4b2ea74c171096660d8a55d7b914f8704..dd65671ceddf52a33fd1069713e67816b10791a3 100644
|
| --- a/tracing/tracing/metrics/media_metric_test.html
|
| +++ b/tracing/tracing/metrics/media_metric_test.html
|
| @@ -14,16 +14,81 @@ found in the LICENSE file.
|
| 'use strict';
|
|
|
| tr.b.unittest.testSuite(function() {
|
| + // Arbitrarily selected process ID and thread IDs we'll use in test data
|
| + const procId = 52;
|
| + const tidMain = 1;
|
| + const tidCompositor = 53;
|
| + const tidAudio = 55;
|
| +
|
| + function doLoadEvent(timestamp) {
|
| + return {name: 'WebMediaPlayerImpl::DoLoad', args: {},
|
| + pid: procId, ts: timestamp, cat: 'media', tid: tidMain, ph: 'X'};
|
| + }
|
| +
|
| + function videoRenderEvent(timestamp) {
|
| + return {name: 'VideoRendererImpl::Render', args: {},
|
| + pid: procId, ts: timestamp, cat: 'media', tid: tidCompositor, ph: 'X'};
|
| + }
|
| +
|
| + function audioRenderEvent(timestamp) {
|
| + return {name: 'AudioRendererImpl::Render', args: {},
|
| + pid: procId, ts: timestamp, cat: 'media', tid: tidAudio, ph: 'X'};
|
| + }
|
| +
|
| + function videoFramesDroppedEvent(timestamp, frameCount) {
|
| + return {name: 'VideoFramesDropped', args: {count: frameCount},
|
| + pid: procId, ts: timestamp, cat: 'media', tid: tidCompositor, ph: 'X'};
|
| + }
|
| +
|
| + function onEndedEvent(timestamp, mediaDuration) {
|
| + return {name: 'WebMediaPlayerImpl::OnEnded',
|
| + args: {'duration': mediaDuration},
|
| + pid: procId, ts: timestamp, cat: 'media', tid: tidMain, ph: 'X'};
|
| + }
|
| +
|
| + function doSeekEvent(timestamp, targetTime) {
|
| + return {name: 'WebMediaPlayerImpl::DoSeek', args: {target: targetTime},
|
| + pid: procId, ts: timestamp, cat: 'media', tid: tidMain, ph: 'X'};
|
| + }
|
| +
|
| + function seekedEvent(timestamp, targetTime) {
|
| + return {name: 'WebMediaPlayerImpl::OnPipelineSeeked',
|
| + args: {target: targetTime},
|
| + pid: procId, ts: timestamp, cat: 'media', tid: tidMain, ph: 'X'};
|
| + }
|
| +
|
| + function threadMarker(threadName, threadId) {
|
| + return {name: 'thread_name', args: {name: threadName},
|
| + pid: procId, ts: 0, cat: '__metadata', tid: threadId, ph: 'M'};
|
| + }
|
| +
|
| + const mainThreadMarker = threadMarker('CrRendererMain', tidMain);
|
| + const compositorThreadMarker = threadMarker('Compositor', tidCompositor);
|
| + const audioThreadMarker = threadMarker('AudioOutputDevice', tidAudio);
|
| +
|
| function makeModel(events) {
|
| return tr.c.TestUtils.newModelWithEvents([events]);
|
| }
|
|
|
| + function checkCloseTo(histograms, histogramName, expectedValue) {
|
| + assert.isDefined(histograms.getHistogramNamed(histogramName));
|
| + const value = histograms.getHistogramNamed(histogramName);
|
| + const statistics = value.running;
|
| + assert.strictEqual(statistics.count, 1);
|
| + assert.closeTo(statistics.mean, expectedValue, 1e-5);
|
| + }
|
| +
|
| + function checkEqual(histograms, histogramName, expectedValue) {
|
| + assert.isDefined(histograms.getHistogramNamed(histogramName));
|
| + const value = histograms.getHistogramNamed(histogramName);
|
| + const statistics = value.running;
|
| + assert.strictEqual(statistics.count, 1);
|
| + assert.strictEqual(statistics.mean, expectedValue);
|
| + }
|
| +
|
| test('mediaMetric_noData', function() {
|
| const histograms = new tr.v.HistogramSet();
|
| - const events = [
|
| - {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
|
| - {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'B'}
|
| - ];
|
| + const events = [];
|
| tr.metrics.mediaMetric(histograms, makeModel(events));
|
| assert.lengthOf(histograms, 0);
|
| });
|
| @@ -31,47 +96,174 @@ tr.b.unittest.testSuite(function() {
|
| test('mediaMetric_videoTimeToPlay', function() {
|
| const histograms = new tr.v.HistogramSet();
|
| const events = [
|
| - {name: 'WebMediaPlayerImpl::DoLoad', args: {},
|
| - pid: 52, ts: 524, cat: 'media', tid: 1, ph: 'X'},
|
| - {name: 'VideoRendererImpl::Render', args: {},
|
| - pid: 52, ts: 560, cat: 'media', tid: 53, ph: 'X'},
|
| - {name: 'VideoRendererImpl::Render', args: {},
|
| - pid: 52, ts: 580, cat: 'media', tid: 53, ph: 'X'},
|
| - {name: 'thread_name', args: {name: 'CrRendererMain'},
|
| - pid: 52, ts: 0, cat: '__metadata', tid: 1, ph: 'M'},
|
| - {name: 'thread_name', args: {name: 'Compositor'},
|
| - pid: 52, ts: 0, cat: '__metadata', tid: 53, ph: 'M'},
|
| + doLoadEvent(100),
|
| + videoRenderEvent(300),
|
| + // Video renderer always generate multiple render events,
|
| + // one for each frame. For calculation of time-to-play,
|
| + // only the first render event is relevant. Here we put in
|
| + // a second render event to make sure it's ignored by the
|
| + // metric computation code.
|
| + videoRenderEvent(400),
|
| + mainThreadMarker,
|
| + compositorThreadMarker,
|
| ];
|
| tr.metrics.mediaMetric(histograms, makeModel(events));
|
| -
|
| - assert.isDefined(histograms.getHistogramNamed('time_to_video_play'));
|
| - const ttpValue = histograms.getHistogramNamed('time_to_video_play');
|
| - const ttpStatistics = ttpValue.running;
|
| - assert.strictEqual(ttpStatistics.count, 1);
|
| - assert.closeTo(ttpStatistics.mean, 0.036, 1e-5);
|
| - assert.closeTo(ttpStatistics.max, 0.036, 1e-5);
|
| + checkCloseTo(histograms, 'time_to_video_play', 0.2);
|
| });
|
|
|
| test('mediaMetric_audioTimeToPlay', function() {
|
| const histograms = new tr.v.HistogramSet();
|
| const events = [
|
| - {name: 'thread_name', args: {name: 'CrRendererMain'},
|
| - pid: 52, ts: 0, cat: '__metadata', tid: 1, ph: 'M'},
|
| - {name: 'thread_name', args: {name: 'AudioOutputDevice'},
|
| - pid: 52, ts: 0, cat: '__metadata', tid: 53, ph: 'M'},
|
| - {name: 'WebMediaPlayerImpl::DoLoad', args: {},
|
| - pid: 52, ts: 1234, cat: 'media', tid: 1, ph: 'X'},
|
| - {name: 'AudioRendererImpl::Render', args: {},
|
| - pid: 52, ts: 4321, cat: 'media', tid: 53, ph: 'X'},
|
| + mainThreadMarker,
|
| + audioThreadMarker,
|
| + doLoadEvent(1000),
|
| + audioRenderEvent(1100),
|
| + ];
|
| + tr.metrics.mediaMetric(histograms, makeModel(events));
|
| + checkCloseTo(histograms, 'time_to_audio_play', 0.1);
|
| + });
|
| +
|
| + test('mediaMetric_bufferingTimeVideo', function() {
|
| + const histograms = new tr.v.HistogramSet();
|
| + const events = [
|
| + doLoadEvent(1000),
|
| + videoRenderEvent(1500),
|
| + videoRenderEvent(1600),
|
| + onEndedEvent(10051500, 10),
|
| + mainThreadMarker,
|
| + compositorThreadMarker,
|
| + ];
|
| + tr.metrics.mediaMetric(histograms, makeModel(events));
|
| + checkCloseTo(histograms, 'buffering_time', 50);
|
| + });
|
| +
|
| + test('mediaMetric_bufferingTimeAudio', function() {
|
| + const histograms = new tr.v.HistogramSet();
|
| + const events = [
|
| + mainThreadMarker,
|
| + audioThreadMarker,
|
| + doLoadEvent(1000),
|
| + audioRenderEvent(1500),
|
| + onEndedEvent(5002500, 5),
|
| + ];
|
| + tr.metrics.mediaMetric(histograms, makeModel(events));
|
| + checkCloseTo(histograms, 'buffering_time', 1);
|
| + });
|
| +
|
| + // With seek, no buffering time should be reported
|
| + test('mediaMetric_noBufferingTime', function() {
|
| + const histograms = new tr.v.HistogramSet();
|
| + const events = [
|
| + doLoadEvent(1000),
|
| + videoRenderEvent(1500),
|
| + videoRenderEvent(1600),
|
| + onEndedEvent(10066666, 10),
|
| + doSeekEvent(525, 1.2),
|
| + seekedEvent(719, 1.2),
|
| + mainThreadMarker,
|
| + compositorThreadMarker,
|
| + ];
|
| + tr.metrics.mediaMetric(histograms, makeModel(events));
|
| + assert.isUndefined(histograms.getHistogramNamed('buffering_time'));
|
| + });
|
| +
|
| + test('mediaMetric_droppedFrameCount', function() {
|
| + const histograms = new tr.v.HistogramSet();
|
| + const events = [
|
| + doLoadEvent(1000),
|
| + videoRenderEvent(1500),
|
| + videoFramesDroppedEvent(123456, 3),
|
| + videoFramesDroppedEvent(234567, 6),
|
| + videoFramesDroppedEvent(345678, 1),
|
| + mainThreadMarker,
|
| + compositorThreadMarker,
|
| + ];
|
| + tr.metrics.mediaMetric(histograms, makeModel(events));
|
| + checkEqual(histograms, 'dropped_frame_count', 10);
|
| + });
|
| +
|
| + test('mediaMetric_seekTime', function() {
|
| + const histograms = new tr.v.HistogramSet();
|
| + const events = [
|
| + doLoadEvent(1000),
|
| + videoRenderEvent(1500),
|
| + doSeekEvent(2000, 1.2),
|
| + seekedEvent(2500, 1.2),
|
| + doSeekEvent(15000, 3.7),
|
| + seekedEvent(75000, 3.7),
|
| + mainThreadMarker,
|
| + compositorThreadMarker,
|
| + ];
|
| + tr.metrics.mediaMetric(histograms, makeModel(events));
|
| + checkCloseTo(histograms, 'seek_time_1.2', 0.5);
|
| + checkCloseTo(histograms, 'seek_time_3.7', 60);
|
| + });
|
| +
|
| + // Scenario: Play mixed audio/video from start to finish
|
| + test('mediaMetric_playVideoScenario', function() {
|
| + const histograms = new tr.v.HistogramSet();
|
| + const events = [
|
| + doLoadEvent(2000),
|
| + videoRenderEvent(3000),
|
| + audioRenderEvent(3200),
|
| + videoRenderEvent(3300),
|
| + videoFramesDroppedEvent(123456, 4),
|
| + videoFramesDroppedEvent(234567, 2),
|
| + onEndedEvent(10013000, 10),
|
| + mainThreadMarker,
|
| + compositorThreadMarker,
|
| + audioThreadMarker,
|
| + ];
|
| + tr.metrics.mediaMetric(histograms, makeModel(events));
|
| + checkCloseTo(histograms, 'time_to_video_play', 1);
|
| + checkCloseTo(histograms, 'time_to_audio_play', 1.2);
|
| + checkCloseTo(histograms, 'buffering_time', 10);
|
| + checkEqual(histograms, 'dropped_frame_count', 6);
|
| + });
|
| +
|
| + // Scenario: Play audio from start to finish
|
| + test('mediaMetric_playAudioScenario', function() {
|
| + const histograms = new tr.v.HistogramSet();
|
| + const events = [
|
| + doLoadEvent(1000),
|
| + audioRenderEvent(1500),
|
| + onEndedEvent(10002500, 10),
|
| + mainThreadMarker,
|
| + audioThreadMarker,
|
| ];
|
| tr.metrics.mediaMetric(histograms, makeModel(events));
|
| + assert.isUndefined(histograms.getHistogramNamed('time_to_video_play'));
|
| + checkCloseTo(histograms, 'time_to_audio_play', 0.5);
|
| + checkCloseTo(histograms, 'buffering_time', 1);
|
| + assert.isUndefined(histograms.getHistogramNamed('dropped_frame_count'));
|
| + });
|
|
|
| - assert.isDefined(histograms.getHistogramNamed('time_to_audio_play'));
|
| - const ttpValue = histograms.getHistogramNamed('time_to_audio_play');
|
| - const ttpStatistics = ttpValue.running;
|
| - assert.strictEqual(ttpStatistics.count, 1);
|
| - assert.closeTo(ttpStatistics.mean, 3.087, 1e-5);
|
| - assert.closeTo(ttpStatistics.max, 3.087, 1e-5);
|
| + // Scenario: Play audio/video with two seeks
|
| + test('mediaMetric_seekScenario', function() {
|
| + const histograms = new tr.v.HistogramSet();
|
| + const events = [
|
| + doLoadEvent(1000),
|
| + videoRenderEvent(2000),
|
| + audioRenderEvent(2020),
|
| + videoRenderEvent(2040),
|
| + doSeekEvent(5000, 0.5),
|
| + seekedEvent(5200, 0.5),
|
| + videoFramesDroppedEvent(123456, 4),
|
| + doSeekEvent(200000, 9),
|
| + seekedEvent(210000, 9),
|
| + videoFramesDroppedEvent(234567, 2),
|
| + onEndedEvent(300000, 10),
|
| + mainThreadMarker,
|
| + compositorThreadMarker,
|
| + audioThreadMarker,
|
| + ];
|
| + tr.metrics.mediaMetric(histograms, makeModel(events));
|
| + checkCloseTo(histograms, 'time_to_video_play', 1);
|
| + checkCloseTo(histograms, 'time_to_audio_play', 1.02);
|
| + assert.isUndefined(histograms.getHistogramNamed('buffering_time'));
|
| + checkEqual(histograms, 'dropped_frame_count', 6);
|
| + checkCloseTo(histograms, 'seek_time_0.5', 0.2);
|
| + checkCloseTo(histograms, 'seek_time_9', 10);
|
| });
|
| });
|
| </script>
|
|
|