OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // Audio test utilities. | 5 // Audio test utilities. |
6 | 6 |
7 // GetStats reports audio output energy in the [0, 32768] range. | 7 // GetStats reports audio output energy in the [0, 32768] range. |
8 var MAX_AUDIO_OUTPUT_ENERGY = 32768; | 8 var MAX_AUDIO_OUTPUT_ENERGY = 32768; |
9 | 9 |
10 // Queries WebRTC stats on |peerConnection| to find out whether audio is playing | 10 // Queries WebRTC stats on |peerConnection| to find out whether audio is playing |
11 // on the connection. Note this does not necessarily mean the audio is actually | 11 // on the connection. Note this does not necessarily mean the audio is actually |
12 // playing out (for instance if there's a bug in the WebRTC web media player). | 12 // playing out (for instance if there's a bug in the WebRTC web media player). |
13 // If |beLenient| is true, we assume we're on a slow and unreliable bot and that | 13 // If |beLenient| is true, we assume we're on a slow and unreliable bot and that |
14 // we should do a minimum of checking. | 14 // we should do a minimum of checking. |
15 function ensureAudioPlaying(peerConnection, beLenient) { | 15 function ensureAudioPlaying(peerConnection, beLenient) { |
16 addExpectedEvent(); | 16 addExpectedEvent(); |
17 | 17 |
18 // Gather 50 samples per second for 2 seconds. | 18 gatherAudioLevelSamples(peerConnection, 3 * 1000, function(samples) { |
19 gatherAudioLevelSamples(peerConnection, 100, 50, function(samples) { | |
20 identifyFakeDeviceSignal_(samples, beLenient); | 19 identifyFakeDeviceSignal_(samples, beLenient); |
21 eventOccured(); | 20 eventOccured(); |
22 }); | 21 }); |
23 } | 22 } |
24 | 23 |
25 // Queries WebRTC stats on |peerConnection| to find out whether audio is muted | 24 // Queries WebRTC stats on |peerConnection| to find out whether audio is muted |
26 // on the connection. | 25 // on the connection. |
27 function ensureSilence(peerConnection) { | 26 function ensureSilence(peerConnection) { |
28 addExpectedEvent(); | 27 addExpectedEvent(); |
29 setTimeout(function() { | 28 setTimeout(function() { |
30 gatherAudioLevelSamples(peerConnection, 100, 50, function(samples) { | 29 gatherAudioLevelSamples(peerConnection, 1 * 1000, function(samples) { |
31 identifySilence_(samples); | 30 identifySilence_(samples); |
32 eventOccured(); | 31 eventOccured(); |
33 }); | 32 }); |
34 }, 500); | 33 }, 500); |
35 } | 34 } |
36 | 35 |
37 // Not sure if this is a bug, but sometimes we get several audio ssrc's where | 36 // Not sure if this is a bug, but sometimes we get several audio ssrc's where |
38 // just reports audio level zero. Think of the nonzero level as the more | 37 // just reports audio level zero. Think of the nonzero level as the more |
39 // credible one here. http://crbug.com/479147. | 38 // credible one here. http://crbug.com/479147. |
40 function workAroundSeveralReportsIssue(audioOutputLevels) { | 39 function workAroundSeveralReportsIssue(audioOutputLevels) { |
41 if (audioOutputLevels.length == 1) { | 40 if (audioOutputLevels.length == 1) { |
42 return audioOutputLevels[0]; | 41 return audioOutputLevels[0]; |
43 } | 42 } |
44 | 43 |
45 console.log("Hit issue where one report batch returns two or more reports " + | 44 console.log("Hit issue where one report batch returns two or more reports " + |
46 "with audioReportLevel; got " + audioOutputLevels); | 45 "with audioReportLevel; got " + audioOutputLevels); |
47 | 46 |
48 return Math.max(audioOutputLevels[0], audioOutputLevels[1]); | 47 return Math.max(audioOutputLevels[0], audioOutputLevels[1]); |
49 } | 48 } |
50 | 49 |
51 // Gathers |numSamples| samples at |frequency| number of times per second and | 50 // Gathers samples from WebRTC stats as fast as possible for |durationMs| |
52 // calls back |callback| with an array with numbers in the [0, 32768] range. | 51 // milliseconds and calls back |callback| with an array with numbers in the |
53 function gatherAudioLevelSamples(peerConnection, numSamples, frequency, | 52 // [0, 32768] range. There are no guarantees for how often we will be able to |
54 callback) { | 53 // collect values, but this function deliberately avoids setTimeout calls in |
55 console.log('Gathering ' + numSamples + ' audio samples...'); | 54 // order be as insensitive as possible to starvation (particularly when this |
| 55 // code runs in parallel with other tests on a heavily loaded bot). |
| 56 function gatherAudioLevelSamples(peerConnection, durationMs, callback) { |
| 57 console.log('Gathering audio samples for ' + durationMs + ' milliseconds...'); |
56 var audioLevelSamples = [] | 58 var audioLevelSamples = [] |
57 | 59 |
58 // If this times out and never found any audio output levels, the call | 60 // If this times out and never found any audio output levels, the call |
59 // probably doesn't have an audio stream. | 61 // probably doesn't have an audio stream. |
60 var gatherSamples = setInterval(function() { | 62 var startTime = new Date(); |
61 peerConnection.getStats(function(response) { | 63 var gotStats = function(response) { |
62 audioOutputLevels = getAudioLevelFromStats_(response); | 64 audioOutputLevels = getAudioLevelFromStats_(response); |
63 if (audioOutputLevels.length == 0) { | 65 if (audioOutputLevels.length == 0) { |
64 // The call probably isn't up yet. | 66 // The call probably isn't up yet. |
65 return; | 67 peerConnection.getStats(gotStats); |
66 } | 68 return; |
67 var outputLevel = workAroundSeveralReportsIssue(audioOutputLevels); | 69 } |
68 audioLevelSamples.push(outputLevel); | 70 var outputLevel = workAroundSeveralReportsIssue(audioOutputLevels); |
| 71 audioLevelSamples.push(outputLevel); |
69 | 72 |
70 if (audioLevelSamples.length == numSamples) { | 73 var elapsed = new Date() - startTime; |
71 console.log('Gathered all samples.'); | 74 if (elapsed > durationMs) { |
72 clearInterval(gatherSamples); | 75 console.log('Gathered all samples.'); |
73 callback(audioLevelSamples); | 76 callback(audioLevelSamples); |
74 } | 77 return; |
75 }); | 78 } |
76 }, 1000 / frequency); | 79 peerConnection.getStats(gotStats); |
| 80 } |
| 81 peerConnection.getStats(gotStats); |
77 } | 82 } |
78 | 83 |
79 /** | 84 /** |
80 * Tries to identify the beep-every-half-second signal generated by the fake | 85 * Tries to identify the beep-every-half-second signal generated by the fake |
81 * audio device in media/video/capture/fake_video_capture_device.cc. Fails the | 86 * audio device in media/video/capture/fake_video_capture_device.cc. Fails the |
82 * test if we can't see a signal. The samples should have been gathered over at | 87 * test if we can't see a signal. The samples should have been gathered over at |
83 * least two seconds since we expect to see at least three "peaks" in there | 88 * least two seconds since we expect to see at least three "peaks" in there |
84 * (we should see either 3 or 4 depending on how things line up). | 89 * (we should see either 3 or 4 depending on how things line up). |
85 * | 90 * |
86 * If |beLenient| is specified, we assume we're running on a slow device or | 91 * If |beLenient| is specified, we assume we're running on a slow device or |
87 * or under TSAN, and relax the checks quite a bit. | 92 * or under TSAN, and relax the checks quite a bit. |
88 * | 93 * |
89 * @private | 94 * @private |
90 */ | 95 */ |
91 function identifyFakeDeviceSignal_(samples, beLenient) { | 96 function identifyFakeDeviceSignal_(samples, beLenient) { |
92 var numPeaks = 0; | 97 var numPeaks = 0; |
93 var threshold = MAX_AUDIO_OUTPUT_ENERGY * 0.7; | 98 var threshold = MAX_AUDIO_OUTPUT_ENERGY * 0.7; |
94 if (beLenient) | 99 if (beLenient) |
95 threshold = MAX_AUDIO_OUTPUT_ENERGY * 0.6; | 100 threshold = MAX_AUDIO_OUTPUT_ENERGY * 0.6; |
96 var currentlyOverThreshold = false; | 101 var currentlyOverThreshold = false; |
97 | 102 |
98 // Detect when we have been been over the threshold and is going back again | 103 // Detect when we have been been over the threshold and is going back again |
99 // (i.e. count peaks). We should see about one peak per second. | 104 // (i.e. count peaks). We should see about two peaks per second. |
100 for (var i = 0; i < samples.length; ++i) { | 105 for (var i = 0; i < samples.length; ++i) { |
101 if (currentlyOverThreshold && samples[i] < threshold) | 106 if (currentlyOverThreshold && samples[i] < threshold) |
102 numPeaks++; | 107 numPeaks++; |
103 currentlyOverThreshold = samples[i] >= threshold; | 108 currentlyOverThreshold = samples[i] >= threshold; |
104 } | 109 } |
105 | 110 |
106 console.log('Number of peaks identified: ' + numPeaks); | 111 console.log('Number of peaks identified: ' + numPeaks); |
107 | 112 |
108 var expectedPeaks = 2; | 113 var expectedPeaks = 2; |
109 if (beLenient) | 114 if (beLenient) |
(...skipping 26 matching lines...) Expand all Loading... |
136 var reports = response.result(); | 141 var reports = response.result(); |
137 var audioOutputLevels = []; | 142 var audioOutputLevels = []; |
138 for (var i = 0; i < reports.length; ++i) { | 143 for (var i = 0; i < reports.length; ++i) { |
139 var report = reports[i]; | 144 var report = reports[i]; |
140 if (report.names().indexOf('audioOutputLevel') != -1) { | 145 if (report.names().indexOf('audioOutputLevel') != -1) { |
141 audioOutputLevels.push(report.stat('audioOutputLevel')); | 146 audioOutputLevels.push(report.stat('audioOutputLevel')); |
142 } | 147 } |
143 } | 148 } |
144 return audioOutputLevels; | 149 return audioOutputLevels; |
145 } | 150 } |
OLD | NEW |