test_audio_processing.js
Go to the documentation of this file.
1 /* vim: set sw=2 : miv*/
2 /*
3  *=BEGIN SONGBIRD GPL
4  *
5  * This file is part of the Songbird web player.
6  *
7  * Copyright(c) 2005-2010 POTI, Inc.
8  * http://www.songbirdnest.com
9  *
10  * This file may be licensed under the terms of of the
11  * GNU General Public License Version 2 (the ``GPL'').
12  *
13  * Software distributed under the License is distributed
14  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
15  * express or implied. See the GPL for the specific language
16  * governing rights and limitations.
17  *
18  * You should have received a copy of the GPL along with this
19  * program. If not, go to http://www.gnu.org/licenses/gpl.html
20  * or write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  *=END SONGBIRD GPL
24  */
25 
31 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
32 
33 if (typeof(Cc) == "undefined")
34  this.Cc = Components.classes;
35 if (typeof(Ci) == "undefined")
36  this.Ci = Components.interfaces;
37 
38 var TEST_FILES = newAppRelativeFile("testharness/gstreamer/files");
39 
40 function checkFormat(listener, processor, test, eventDetails) {
41  var format = eventDetails.QueryInterface(Ci.sbIMediaFormatAudio);
42 
43  assertTrue(format.sampleRate == test.expectedRate,
44  "Expected sample rate "+test.expectedRate+
45  " differed from actual sample rate "+format.sampleRate);
46 
47  assertTrue(format.channels == test.expectedChannels,
48  "Expected channel count "+test.expectedChannels+
49  " differed from actual channel count "+format.channels);
50 
51  var actualFormat;
52  if (format.audioType == "audio/x-int")
53  actualFormat = Ci.sbIMediacoreAudioProcessor.FORMAT_INT16;
54  else
55  actualFormat = Ci.sbIMediacoreAudioProcessor.FORMAT_FLOAT;
56 
57  assertTrue(
58  test.expectedFormat == 0 || actualFormat == test.expectedFormat,
59  "Expected format "+test.expectedFormat+
60  " differed from actual format "+actualFormat);
61 
62  return true;
63 };
64 
65 function pauseTest(listener, processor, test, eventDetails) {
66  processor.suspend();
67 
68  // Unpause in a bit. No events or data should arrive in the middle.
69  doTimeout(200, function() {
70  processor.resume();
71 
72  listener.nextSequence();
73  });
74 
75  // Don't automatically continue to the next item - we do that once our
76  // timeout expires. This ensures that we check that no additional data is
77  // arriving before we restart the processor.
78  return false;
79 };
80 
81 function startAgain(listener, processor, test, eventDetails) {
82  var startFailed = false;
83  try {
84  processor.start();
85  } catch (e) {
86  startFailed = true;
87  }
88 
89  assertTrue(startFailed, "Calling start() again did not fail");
90 
91  return true;
92 };
93 
94 function stopProcessing(listener, processor, test, eventDetails) {
95  processor.stop();
96 
97  return true;
98 };
99 
100 function pauseMomentarily(listener, processor, test, unused) {
101  processor.suspend();
102 
103  // Unpause as soon as possible.
104  doTimeout(0, function() {
105  processor.resume();
106  });
107 };
108 
109 const K_TEST_CASES = [
110  {
111  description: "simple unconstrained decode",
112  filename: "simple.ogg",
113 
114  constraintRate: 0,
115  constraintChannels: 0,
116  constraintBlockSize: 0,
117  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
118 
119  expectedRate: 44100,
120  expectedChannels: 2,
121  expectedBlockSize: 0, // Will vary, we don't care.
122  expectedFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
123 
124  sequence: [
125  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_START, action: checkFormat },
126  {expected: "samples", sampleCount: 20480, action: null, blockAction: null },
127  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_EOS, action: null }
128  ]
129  },
130  {
131  description: "constrained decode to mono",
132  filename: "simple.ogg",
133 
134  constraintRate: 22050,
135  constraintChannels: 1,
136  constraintBlockSize: 2048,
137  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_FLOAT,
138 
139  expectedRate: 22050,
140  expectedChannels: 1,
141  expectedBlockSize: 2048,
142  expectedFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_FLOAT,
143 
144  sequence: [
145  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_START, action: checkFormat },
146  {expected: "samples", sampleCount: 5120, action: null, blockAction: null },
147  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_EOS, action: null }
148  ]
149  },
150  {
151  description: "unconstrained 5.1 channel vorbis",
152  filename: "surround51.ogg",
153 
154  constraintRate: 0,
155  constraintChannels: 0,
156  constraintBlockSize: 0,
157  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY,
158 
159  expectedRate: 44100,
160  expectedChannels: 2, // Note that despite not setting a constraint, we
161  // expect 2 channels out of a 5.1 file here - more
162  // than 2 channels aren't supported.
163  expectedBlockSize: 0,
164  expectedFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY,
165 
166  sequence: [
167  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_START, action: checkFormat },
168  {expected: "samples", sampleCount: 20480, action: null, blockAction: null },
169  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_EOS, action: null }
170  ]
171  },
172  {
173  description: "pause/unpause testing",
174  filename: "simple.ogg",
175 
176  constraintRate: 0,
177  constraintChannels: 0,
178  constraintBlockSize: 1000,
179  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
180 
181  expectedRate: 44100,
182  expectedChannels: 2,
183  expectedBlockSize: 1000,
184  expectedFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
185 
186  sequence: [
187  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_START, action: checkFormat },
188  {expected: "samples", sampleCount: 10000, action: pauseTest, blockAction: null },
189  {expected: "samples", sampleCount: 10480, action: null, blockAction: null },
190  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_EOS, action: null }
191  ]
192  },
193  {
194  description: "pause/unpause every block testing",
195  filename: "simple.ogg",
196 
197  constraintRate: 0,
198  constraintChannels: 0,
199  constraintBlockSize: 1000,
200  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
201 
202  expectedRate: 44100,
203  expectedChannels: 2,
204  expectedBlockSize: 1000,
205  expectedFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
206 
207  sequence: [
208  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_START, action: checkFormat },
209  {expected: "samples", sampleCount: 20480, action: null, blockAction: pauseMomentarily },
210  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_EOS, action: null }
211  ]
212  },
213  {
214  description: "multiple calls to start",
215  filename: "simple.ogg",
216 
217  constraintRate: 0,
218  constraintChannels: 0,
219  constraintBlockSize: 0,
220  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
221 
222  sequence: [
223  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_START, action: startAgain },
224  {expected: "samples", sampleCount: 20480, action: null, blockAction: null },
225  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_EOS, action: null }
226  ]
227  },
228  {
229  description: "stop in the middle",
230  filename: "simple.ogg",
231 
232  constraintRate: 0,
233  constraintChannels: 0,
234  constraintBlockSize: 1000,
235  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
236 
237  sequence: [
238  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_START, action: startAgain },
239  {expected: "samples", sampleCount: 12000, action: stopProcessing, blockAction: null }
240  ]
241  },
242  {
243  description: "video only file",
244  filename: "video.ogg",
245 
246  constraintRate: 0,
247  constraintChannels: 0,
248  constraintBlockSize: 0,
249  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
250 
251  sequence: [
252  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_ERROR, action: null },
253  ]
254  },
255  {
256  description: "file that doesn't exist",
257  filename: "nothere.ogg",
258 
259  constraintRate: 0,
260  constraintChannels: 0,
261  constraintBlockSize: 0,
262  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY, // We don't care.
263 
264  sequence: [
265  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_ERROR, action: null },
266  ]
267  },
268  {
269  description: "gap test (corrupt data in middle of file)",
270  filename: "corrupt-in-middle.ogg",
271 
272  constraintRate: 0,
273  constraintChannels: 0,
274  constraintBlockSize: 0,
275  constraintFormat: Ci.sbIMediacoreAudioProcessor.FORMAT_ANY,
276 
277  sequence: [
278  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_START, action: null},
279  {expected: "samples", sampleCount: 21184, action: null, blockAction: null },
280  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_GAP, action: null },
281  {expected: "samples", sampleCount: 19456, action: null, blockAction: null },
282  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_GAP, action: null },
283  {expected: "samples", sampleCount: 39232, action: null, blockAction: null },
284  {expected: "event", eventType: Ci.sbIMediacoreAudioProcessorListener.EVENT_EOS, action: null }
285  ]
286  }
287 
288 ];
289 
290 function runAudioProcessingTest(library, test)
291 {
292  var processor = Cc["@songbirdnest.com/Songbird/Mediacore/GStreamer/AudioProcessor;1"]
293  .createInstance(Ci.sbIMediacoreAudioProcessor);
294  assertTrue(processor, "failed to create processor");
295 
296  var listener = {
297  samplesCounted : 0,
298 
299  seqIdx : 0,
300 
301  onIntegerAudioDecoded : function(timestamp, numSamples, sampleData) {
302  this.onAudio(timestamp, numSamples);
303  },
304 
305  onFloatAudioDecoded : function(timestamp, numSamples, sampleData) {
306  this.onAudio(timestamp, numSamples);
307  },
308 
309  onAudio : function(timestamp, numSamples) {
310  assertTrue(this.seqIdx < test.sequence.length, "Audio after end!");
311  var expectingSamples = test.sequence[this.seqIdx].expected == "samples";
312  assertTrue(expectingSamples,
313  "Got audio samples when not expecting any");
314  assertTrue(test.constraintBlockSize == 0 ||
315  numSamples <= test.constraintBlockSize,
316  "Received incorrect block size");
317 
318  var numSamplesExpected = test.sequence[this.seqIdx].sampleCount;
319 
320  this.samplesCounted += numSamples;
321 
322  assertTrue(this.samplesCounted <= numSamplesExpected,
323  "Received more samples than expected");
324 
325  if (test.sequence[this.seqIdx].blockAction != null)
326  {
327  test.sequence[this.seqIdx].blockAction(this, processor, test, null);
328  }
329 
330  if (this.samplesCounted == numSamplesExpected) {
331  this.samplesCounted = 0;
332  this.nextAction(null);
333  }
334  },
335 
336  onEvent : function(eventType, details) {
337  assertTrue(this.seqIdx < test.sequence.length, "Event after end!");
338  var expectingEvent = test.sequence[this.seqIdx].expected == "event";
339  assertTrue(expectingEvent,
340  "Got event of type "+eventType+" when not expecting an event");
341 
342  var expectedEventType = test.sequence[this.seqIdx].eventType;
343  assertEqual(expectedEventType, eventType);
344 
345  this.nextAction(details);
346  },
347 
348  nextAction: function(data) {
349  // Do next action, if any.
350  var continueToNextSequence = true;
351  if (test.sequence[this.seqIdx].action != null)
352  continueToNextSequence = test.sequence[this.seqIdx].action(
353  this, processor, test, data);
354 
355  if (continueToNextSequence)
356  this.nextSequence();
357  },
358 
359  nextSequence: function() {
360  this.seqIdx++;
361  // Check if we're done now.
362  if (this.seqIdx == test.sequence.length) {
363  processor.stop();
364  // Clear processor object to avoid leaks.
365  processor = null;
366  testFinished();
367  }
368  },
369 
370  QueryInterface: XPCOMUtils.generateQI([Ci.sbIMediacoreAudioProcessorListener])
371  };
372 
373  processor.init(listener);
374 
375  processor.constraintSampleRate = test.constraintRate;
376  processor.constraintChannelCount = test.constraintChannels;
377  processor.constraintBlockSize = test.constraintBlockSize;
378  processor.constraintAudioFormat = test.constraintFormat;
379 
380  var ioService = Cc["@mozilla.org/network/io-service;1"]
381  .getService(Ci.nsIIOService);
382 
383  // Set up a media item for test.filename
384  var file = TEST_FILES.clone();
385  file.append(test.filename);
386 
387  var fileURI = ioService.newFileURI(file, false);
388  var mediaItem = library.createMediaItem(fileURI);
389  processor.start(mediaItem);
390 
391  // Now we wait!
392  testPending();
393 }
394 
395 
396 function runTest() {
397  // Set up a test library to use for the media items we process
398  var testlib = createLibrary("test_audio_processing");
399 
400  for each (var testcase in K_TEST_CASES) {
401  log("Checking testcase [" + testcase.description + "]");
402 
403  runAudioProcessingTest(testlib, testcase);
404  }
405 
406  return;
407 }
function runTest()
Advanced DataRemote unit tests.
const Cc
function newAppRelativeFile(path)
function log(s)
function testFinished()
function pauseMomentarily(listener, processor, test, unused)
const K_TEST_CASES
sbOSDControlService prototype QueryInterface
function assertTrue(aTest, aMessage)
var ioService
function assertEqual(aExpected, aActual, aMessage)
var TEST_FILES
function startAgain(listener, processor, test, eventDetails)
function checkFormat(listener, processor, test, eventDetails)
function stopProcessing(listener, processor, test, eventDetails)
function pauseTest(listener, processor, test, eventDetails)
return null
Definition: FeedWriter.js:1143
function createLibrary(databaseGuid, databaseLocation)
Definition: test_load.js:151
var expected
function runAudioProcessingTest(library, test)
function onEvent(aEvent)
const Ci
function doTimeout(delay, func)
observe data
Definition: FeedWriter.js:1329
var file
function testPending()