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
3 // found in the LICENSE file.
5 // Audio test utilities.
7 // GetStats reports audio output energy in the [0, 32768] range.
8 var MAX_AUDIO_OUTPUT_ENERGY
= 32768;
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
12 // playing out (for instance if there's a bug in the WebRTC web media player).
13 function ensureAudioPlaying(peerConnection
) {
17 gatherAudioLevelSamples(peerConnection
, function(samples
) {
18 if (identifyFakeDeviceSignal_(samples
)) {
22 if (attempt
++ % 5 == 0) {
23 console
.log('Still waiting for the fake audio signal.');
24 console
.log('Dumping samples so far for analysis: ' + samples
);
30 // Queries WebRTC stats on |peerConnection| to find out whether audio is muted
32 function ensureSilence(peerConnection
) {
36 gatherAudioLevelSamples(peerConnection
, function(samples
) {
37 if (identifySilence_(samples
)) {
41 if (attempt
++ % 5 == 0) {
42 console
.log('Still waiting for audio to go silent.');
43 console
.log('Dumping samples so far for analysis: ' + samples
);
50 // Not sure if this is a bug, but sometimes we get several audio ssrc's where
51 // just reports audio level zero. Think of the nonzero level as the more
52 // credible one here. http://crbug.com/479147.
53 function workAroundSeveralReportsIssue(audioOutputLevels
) {
54 if (audioOutputLevels
.length
== 1) {
55 return audioOutputLevels
[0];
58 console
.log("Hit issue where one report batch returns two or more reports " +
59 "with audioReportLevel; got " + audioOutputLevels
);
61 return Math
.max(audioOutputLevels
[0], audioOutputLevels
[1]);
64 // Gathers samples from WebRTC stats as fast as possible for and calls back
65 // |callback| continuously with an array with numbers in the [0, 32768] range.
66 // The array will grow continuously over time as we gather more samples. The
67 // |callback| should return true when it is satisfied. It will be called about
68 // once a second and can contain expensive processing (but faster = better).
70 // There are no guarantees for how often we will be able to collect values,
71 // but this function deliberately avoids setTimeout calls in order be as
72 // insensitive as possible to starvation (particularly when this code runs in
73 // parallel with other tests on a heavily loaded bot).
74 function gatherAudioLevelSamples(peerConnection
, callback
) {
75 console
.log('Gathering audio samples...');
76 var callbackIntervalMs
= 1000;
77 var audioLevelSamples
= []
79 // If this times out and never found any audio output levels, the call
80 // probably doesn't have an audio stream.
81 var lastRunAt
= new Date();
82 var gotStats = function(response
) {
83 audioOutputLevels
= getAudioLevelFromStats_(response
);
84 if (audioOutputLevels
.length
== 0) {
85 // The call probably isn't up yet.
86 peerConnection
.getStats(gotStats
);
89 var outputLevel
= workAroundSeveralReportsIssue(audioOutputLevels
);
90 audioLevelSamples
.push(outputLevel
);
92 var elapsed
= new Date() - lastRunAt
;
93 if (elapsed
> callbackIntervalMs
) {
94 if (callback(audioLevelSamples
)) {
95 console
.log('Done gathering samples: we found what we looked for.');
98 lastRunAt
= new Date();
100 // Otherwise, continue as fast as we can.
101 peerConnection
.getStats(gotStats
);
103 peerConnection
.getStats(gotStats
);
107 * Tries to identify the beep-every-half-second signal generated by the fake
108 * audio device in media/capture/video/fake_video_capture_device.cc. Fails the
109 * test if we can't see a signal. The samples should have been gathered over at
110 * least two seconds since we expect to see at least three "peaks" in there
111 * (we should see either 3 or 4 depending on how things line up).
115 function identifyFakeDeviceSignal_(samples
) {
117 var threshold
= MAX_AUDIO_OUTPUT_ENERGY
* 0.7;
118 var currentlyOverThreshold
= false;
120 // Detect when we have been been over the threshold and is going back again
121 // (i.e. count peaks). We should see about two peaks per second.
122 for (var i
= 0; i
< samples
.length
; ++i
) {
123 if (currentlyOverThreshold
&& samples
[i
] < threshold
)
125 currentlyOverThreshold
= samples
[i
] >= threshold
;
128 var expectedPeaks
= 3;
129 console
.log(numPeaks
+ '/' + expectedPeaks
+ ' signal peaks identified.');
130 return numPeaks
>= expectedPeaks
;
136 function identifySilence_(samples
) {
138 for (var i
= 0; i
< samples
.length
; ++i
)
139 average
+= samples
[i
] / samples
.length
;
141 // If silent (like when muted), we should get very near zero audio level.
142 console
.log('Average audio level: ' + average
);
144 return average
< 0.01 * MAX_AUDIO_OUTPUT_ENERGY
;
150 function getAudioLevelFromStats_(response
) {
151 var reports
= response
.result();
152 var audioOutputLevels
= [];
153 for (var i
= 0; i
< reports
.length
; ++i
) {
154 var report
= reports
[i
];
155 if (report
.names().indexOf('audioOutputLevel') != -1) {
156 audioOutputLevels
.push(report
.stat('audioOutputLevel'));
159 return audioOutputLevels
;