[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / test / data / media / webrtc_test_audio.js
blob7bddd882222810345e067ddaf1d82d1fb25f06cb
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) {
14   addExpectedEvent();
16   var attempt = 1;
17   gatherAudioLevelSamples(peerConnection, function(samples) {
18     if (identifyFakeDeviceSignal_(samples)) {
19       eventOccured();
20       return true;
21     }
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);
25     }
26     return false;
27   });
30 // Queries WebRTC stats on |peerConnection| to find out whether audio is muted
31 // on the connection.
32 function ensureSilence(peerConnection) {
33   addExpectedEvent();
35   var attempt = 1;
36   gatherAudioLevelSamples(peerConnection, function(samples) {
37     if (identifySilence_(samples)) {
38       eventOccured();
39       return true;
40     }
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);
44     }
45     return false;
46   });
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];
56   }
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);
87       return;
88     }
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.');
96         return;
97       }
98       lastRunAt = new Date();
99     }
100     // Otherwise, continue as fast as we can.
101     peerConnection.getStats(gotStats);
102   }
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).
113 * @private
115 function identifyFakeDeviceSignal_(samples) {
116   var numPeaks = 0;
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)
124       numPeaks++;
125     currentlyOverThreshold = samples[i] >= threshold;
126   }
128   var expectedPeaks = 3;
129   console.log(numPeaks + '/' + expectedPeaks + ' signal peaks identified.');
130   return numPeaks >= expectedPeaks;
134  * @private
135  */
136 function identifySilence_(samples) {
137   var average = 0;
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;
148  * @private
149  */
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'));
157     }
158   }
159   return audioOutputLevels;