OLD | NEW |
1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
2 <!-- | 2 <!-- |
3 Copyright 2017 The Chromium Authors. All rights reserved. | 3 Copyright 2017 The Chromium Authors. All rights reserved. |
4 Use of this source code is governed by a BSD-style license that can be | 4 Use of this source code is governed by a BSD-style license that can be |
5 found in the LICENSE file. | 5 found in the LICENSE file. |
6 --> | 6 --> |
7 | 7 |
8 <link rel="import" href="/tracing/core/test_utils.html"> | 8 <link rel="import" href="/tracing/core/test_utils.html"> |
9 <link rel="import" href="/tracing/extras/importer/trace_event_importer.html"> | 9 <link rel="import" href="/tracing/extras/importer/trace_event_importer.html"> |
10 <link rel="import" href="/tracing/metrics/media_metric.html"> | 10 <link rel="import" href="/tracing/metrics/media_metric.html"> |
11 <link rel="import" href="/tracing/value/histogram_set.html"> | 11 <link rel="import" href="/tracing/value/histogram_set.html"> |
12 | 12 |
13 <script> | 13 <script> |
14 'use strict'; | 14 'use strict'; |
15 | 15 |
16 tr.b.unittest.testSuite(function() { | 16 tr.b.unittest.testSuite(function() { |
| 17 // Arbitrarily selected process ID and thread IDs we'll use in test data |
| 18 const procId = 52; |
| 19 const tidMain = 1; |
| 20 const tidCompositor = 53; |
| 21 const tidAudio = 55; |
| 22 |
| 23 function doLoadEvent(timestamp) { |
| 24 return {name: 'WebMediaPlayerImpl::DoLoad', args: {}, |
| 25 pid: procId, ts: timestamp, cat: 'media', tid: tidMain, ph: 'X'}; |
| 26 } |
| 27 |
| 28 function videoRenderEvent(timestamp) { |
| 29 return {name: 'VideoRendererImpl::Render', args: {}, |
| 30 pid: procId, ts: timestamp, cat: 'media', tid: tidCompositor, ph: 'X'}; |
| 31 } |
| 32 |
| 33 function audioRenderEvent(timestamp) { |
| 34 return {name: 'AudioRendererImpl::Render', args: {}, |
| 35 pid: procId, ts: timestamp, cat: 'media', tid: tidAudio, ph: 'X'}; |
| 36 } |
| 37 |
| 38 function videoFramesDroppedEvent(timestamp, frameCount) { |
| 39 return {name: 'VideoFramesDropped', args: {count: frameCount}, |
| 40 pid: procId, ts: timestamp, cat: 'media', tid: tidCompositor, ph: 'X'}; |
| 41 } |
| 42 |
| 43 function onEndedEvent(timestamp, mediaDuration) { |
| 44 return {name: 'WebMediaPlayerImpl::OnEnded', |
| 45 args: {'duration': mediaDuration}, |
| 46 pid: procId, ts: timestamp, cat: 'media', tid: tidMain, ph: 'X'}; |
| 47 } |
| 48 |
| 49 function doSeekEvent(timestamp, targetTime) { |
| 50 return {name: 'WebMediaPlayerImpl::DoSeek', args: {target: targetTime}, |
| 51 pid: procId, ts: timestamp, cat: 'media', tid: tidMain, ph: 'X'}; |
| 52 } |
| 53 |
| 54 function seekedEvent(timestamp, targetTime) { |
| 55 return {name: 'WebMediaPlayerImpl::OnPipelineSeeked', |
| 56 args: {target: targetTime}, |
| 57 pid: procId, ts: timestamp, cat: 'media', tid: tidMain, ph: 'X'}; |
| 58 } |
| 59 |
| 60 function threadMarker(threadName, threadId) { |
| 61 return {name: 'thread_name', args: {name: threadName}, |
| 62 pid: procId, ts: 0, cat: '__metadata', tid: threadId, ph: 'M'}; |
| 63 } |
| 64 |
| 65 const mainThreadMarker = threadMarker('CrRendererMain', tidMain); |
| 66 const compositorThreadMarker = threadMarker('Compositor', tidCompositor); |
| 67 const audioThreadMarker = threadMarker('AudioOutputDevice', tidAudio); |
| 68 |
17 function makeModel(events) { | 69 function makeModel(events) { |
18 return tr.c.TestUtils.newModelWithEvents([events]); | 70 return tr.c.TestUtils.newModelWithEvents([events]); |
19 } | 71 } |
20 | 72 |
| 73 function checkCloseTo(histograms, histogramName, expectedValue) { |
| 74 assert.isDefined(histograms.getHistogramNamed(histogramName)); |
| 75 const value = histograms.getHistogramNamed(histogramName); |
| 76 const statistics = value.running; |
| 77 assert.strictEqual(statistics.count, 1); |
| 78 assert.closeTo(statistics.mean, expectedValue, 1e-5); |
| 79 } |
| 80 |
| 81 function checkEqual(histograms, histogramName, expectedValue) { |
| 82 assert.isDefined(histograms.getHistogramNamed(histogramName)); |
| 83 const value = histograms.getHistogramNamed(histogramName); |
| 84 const statistics = value.running; |
| 85 assert.strictEqual(statistics.count, 1); |
| 86 assert.strictEqual(statistics.mean, expectedValue); |
| 87 } |
| 88 |
21 test('mediaMetric_noData', function() { | 89 test('mediaMetric_noData', function() { |
22 const histograms = new tr.v.HistogramSet(); | 90 const histograms = new tr.v.HistogramSet(); |
23 const events = [ | 91 const events = []; |
24 {name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'}, | |
25 {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'B'} | |
26 ]; | |
27 tr.metrics.mediaMetric(histograms, makeModel(events)); | 92 tr.metrics.mediaMetric(histograms, makeModel(events)); |
28 assert.lengthOf(histograms, 0); | 93 assert.lengthOf(histograms, 0); |
29 }); | 94 }); |
30 | 95 |
31 test('mediaMetric_videoTimeToPlay', function() { | 96 test('mediaMetric_videoTimeToPlay', function() { |
32 const histograms = new tr.v.HistogramSet(); | 97 const histograms = new tr.v.HistogramSet(); |
33 const events = [ | 98 const events = [ |
34 {name: 'WebMediaPlayerImpl::DoLoad', args: {}, | 99 doLoadEvent(524), |
35 pid: 52, ts: 524, cat: 'media', tid: 1, ph: 'X'}, | 100 videoRenderEvent(560), |
36 {name: 'VideoRendererImpl::Render', args: {}, | 101 videoRenderEvent(580), |
37 pid: 52, ts: 560, cat: 'media', tid: 53, ph: 'X'}, | 102 mainThreadMarker, |
38 {name: 'VideoRendererImpl::Render', args: {}, | 103 compositorThreadMarker, |
39 pid: 52, ts: 580, cat: 'media', tid: 53, ph: 'X'}, | |
40 {name: 'thread_name', args: {name: 'CrRendererMain'}, | |
41 pid: 52, ts: 0, cat: '__metadata', tid: 1, ph: 'M'}, | |
42 {name: 'thread_name', args: {name: 'Compositor'}, | |
43 pid: 52, ts: 0, cat: '__metadata', tid: 53, ph: 'M'}, | |
44 ]; | 104 ]; |
45 tr.metrics.mediaMetric(histograms, makeModel(events)); | 105 tr.metrics.mediaMetric(histograms, makeModel(events)); |
46 | 106 checkCloseTo(histograms, 'time_to_video_play', 0.036); |
47 assert.isDefined(histograms.getHistogramNamed('time_to_video_play')); | |
48 const ttpValue = histograms.getHistogramNamed('time_to_video_play'); | |
49 const ttpStatistics = ttpValue.running; | |
50 assert.strictEqual(ttpStatistics.count, 1); | |
51 assert.closeTo(ttpStatistics.mean, 0.036, 1e-5); | |
52 assert.closeTo(ttpStatistics.max, 0.036, 1e-5); | |
53 }); | 107 }); |
54 | 108 |
55 test('mediaMetric_audioTimeToPlay', function() { | 109 test('mediaMetric_audioTimeToPlay', function() { |
56 const histograms = new tr.v.HistogramSet(); | 110 const histograms = new tr.v.HistogramSet(); |
57 const events = [ | 111 const events = [ |
58 {name: 'thread_name', args: {name: 'CrRendererMain'}, | 112 mainThreadMarker, |
59 pid: 52, ts: 0, cat: '__metadata', tid: 1, ph: 'M'}, | 113 audioThreadMarker, |
60 {name: 'thread_name', args: {name: 'AudioOutputDevice'}, | 114 doLoadEvent(1234), |
61 pid: 52, ts: 0, cat: '__metadata', tid: 53, ph: 'M'}, | 115 audioRenderEvent(4321), |
62 {name: 'WebMediaPlayerImpl::DoLoad', args: {}, | |
63 pid: 52, ts: 1234, cat: 'media', tid: 1, ph: 'X'}, | |
64 {name: 'AudioRendererImpl::Render', args: {}, | |
65 pid: 52, ts: 4321, cat: 'media', tid: 53, ph: 'X'}, | |
66 ]; | 116 ]; |
67 tr.metrics.mediaMetric(histograms, makeModel(events)); | 117 tr.metrics.mediaMetric(histograms, makeModel(events)); |
| 118 checkCloseTo(histograms, 'time_to_audio_play', 3.087); |
| 119 }); |
68 | 120 |
69 assert.isDefined(histograms.getHistogramNamed('time_to_audio_play')); | 121 test('mediaMetric_bufferingTimeVideo', function() { |
70 const ttpValue = histograms.getHistogramNamed('time_to_audio_play'); | 122 const histograms = new tr.v.HistogramSet(); |
71 const ttpStatistics = ttpValue.running; | 123 const events = [ |
72 assert.strictEqual(ttpStatistics.count, 1); | 124 doLoadEvent(1100), |
73 assert.closeTo(ttpStatistics.mean, 3.087, 1e-5); | 125 videoRenderEvent(1500), |
74 assert.closeTo(ttpStatistics.max, 3.087, 1e-5); | 126 videoRenderEvent(1600), |
| 127 onEndedEvent(10066666, 10), |
| 128 mainThreadMarker, |
| 129 compositorThreadMarker, |
| 130 ]; |
| 131 tr.metrics.mediaMetric(histograms, makeModel(events)); |
| 132 checkCloseTo(histograms, 'buffering_time', 65.166); |
| 133 }); |
| 134 |
| 135 test('mediaMetric_bufferingTimeAudio', function() { |
| 136 const histograms = new tr.v.HistogramSet(); |
| 137 const events = [ |
| 138 mainThreadMarker, |
| 139 audioThreadMarker, |
| 140 doLoadEvent(1234), |
| 141 audioRenderEvent(4321), |
| 142 onEndedEvent(5066666, 5), |
| 143 ]; |
| 144 tr.metrics.mediaMetric(histograms, makeModel(events)); |
| 145 checkCloseTo(histograms, 'buffering_time', 62.345); |
| 146 }); |
| 147 |
| 148 // With seek, no buffering time should be reported |
| 149 test('mediaMetric_noBufferingTime', function() { |
| 150 const histograms = new tr.v.HistogramSet(); |
| 151 const events = [ |
| 152 doLoadEvent(1100), |
| 153 videoRenderEvent(1500), |
| 154 videoRenderEvent(1600), |
| 155 onEndedEvent(10066666, 10), |
| 156 doSeekEvent(525, 1.2), |
| 157 seekedEvent(719, 1.2), |
| 158 mainThreadMarker, |
| 159 compositorThreadMarker, |
| 160 ]; |
| 161 tr.metrics.mediaMetric(histograms, makeModel(events)); |
| 162 assert.isUndefined(histograms.getHistogramNamed('buffering_time')); |
| 163 }); |
| 164 |
| 165 test('mediaMetric_droppedFrameCount', function() { |
| 166 const histograms = new tr.v.HistogramSet(); |
| 167 const events = [ |
| 168 doLoadEvent(1100), |
| 169 videoRenderEvent(1500), |
| 170 videoFramesDroppedEvent(123456, 3), |
| 171 videoFramesDroppedEvent(234567, 6), |
| 172 videoFramesDroppedEvent(345678, 1), |
| 173 mainThreadMarker, |
| 174 compositorThreadMarker, |
| 175 ]; |
| 176 tr.metrics.mediaMetric(histograms, makeModel(events)); |
| 177 checkEqual(histograms, 'dropped_frame_count', 10); |
| 178 }); |
| 179 |
| 180 test('mediaMetric_seekTime', function() { |
| 181 const histograms = new tr.v.HistogramSet(); |
| 182 const events = [ |
| 183 doLoadEvent(100), |
| 184 videoRenderEvent(200), |
| 185 doSeekEvent(524, 1.2), |
| 186 seekedEvent(719, 1.2), |
| 187 doSeekEvent(14159, 3.7), |
| 188 seekedEvent(71828, 3.7), |
| 189 mainThreadMarker, |
| 190 compositorThreadMarker, |
| 191 ]; |
| 192 tr.metrics.mediaMetric(histograms, makeModel(events)); |
| 193 checkCloseTo(histograms, 'seek_time_1.2', 0.195); |
| 194 checkCloseTo(histograms, 'seek_time_3.7', 57.669); |
| 195 }); |
| 196 |
| 197 // Scenario: Play mixed audio/video from start to finish |
| 198 test('mediaMetric_playVideoScenario', function() { |
| 199 const histograms = new tr.v.HistogramSet(); |
| 200 const events = [ |
| 201 doLoadEvent(1516), |
| 202 videoRenderEvent(3257), |
| 203 audioRenderEvent(3266), |
| 204 videoRenderEvent(3287), |
| 205 videoFramesDroppedEvent(123456, 4), |
| 206 videoFramesDroppedEvent(234567, 2), |
| 207 onEndedEvent(10012345, 10), |
| 208 mainThreadMarker, |
| 209 compositorThreadMarker, |
| 210 audioThreadMarker, |
| 211 ]; |
| 212 tr.metrics.mediaMetric(histograms, makeModel(events)); |
| 213 checkCloseTo(histograms, 'time_to_video_play', 1.741); |
| 214 checkCloseTo(histograms, 'time_to_audio_play', 1.75); |
| 215 checkCloseTo(histograms, 'buffering_time', 9.088); |
| 216 checkEqual(histograms, 'dropped_frame_count', 6); |
| 217 }); |
| 218 |
| 219 // Scenario: Play audio from start to finish |
| 220 test('mediaMetric_playAudioScenario', function() { |
| 221 const histograms = new tr.v.HistogramSet(); |
| 222 const events = [ |
| 223 doLoadEvent(1516), |
| 224 audioRenderEvent(3266), |
| 225 onEndedEvent(10012345, 10), |
| 226 mainThreadMarker, |
| 227 audioThreadMarker, |
| 228 ]; |
| 229 tr.metrics.mediaMetric(histograms, makeModel(events)); |
| 230 assert.isUndefined(histograms.getHistogramNamed('time_to_video_play')); |
| 231 checkCloseTo(histograms, 'time_to_audio_play', 1.75); |
| 232 checkCloseTo(histograms, 'buffering_time', 9.079); |
| 233 assert.isUndefined(histograms.getHistogramNamed('dropped_frame_count')); |
| 234 }); |
| 235 |
| 236 // Scenario: Play audio/video with two seeks |
| 237 test('mediaMetric_seekScenario', function() { |
| 238 const histograms = new tr.v.HistogramSet(); |
| 239 const events = [ |
| 240 doLoadEvent(1516), |
| 241 videoRenderEvent(3257), |
| 242 audioRenderEvent(3266), |
| 243 videoRenderEvent(3287), |
| 244 doSeekEvent(5123, 0.5), |
| 245 seekedEvent(5234, 0.5), |
| 246 videoFramesDroppedEvent(123456, 4), |
| 247 doSeekEvent(222222, 9), |
| 248 seekedEvent(222444, 9), |
| 249 videoFramesDroppedEvent(234567, 2), |
| 250 onEndedEvent(1234567, 10), |
| 251 mainThreadMarker, |
| 252 compositorThreadMarker, |
| 253 audioThreadMarker, |
| 254 ]; |
| 255 tr.metrics.mediaMetric(histograms, makeModel(events)); |
| 256 checkCloseTo(histograms, 'time_to_video_play', 1.741); |
| 257 checkCloseTo(histograms, 'time_to_audio_play', 1.75); |
| 258 assert.isUndefined(histograms.getHistogramNamed('buffering_time')); |
| 259 checkEqual(histograms, 'dropped_frame_count', 6); |
| 260 checkCloseTo(histograms, 'seek_time_0.5', 0.111); |
| 261 checkCloseTo(histograms, 'seek_time_9', 0.222); |
75 }); | 262 }); |
76 }); | 263 }); |
77 </script> | 264 </script> |
OLD | NEW |