1// Copyright (c) 2012 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.
4
5#include "ppapi/tests/test_audio.h"
6
7#include <string.h>
8
9#include "ppapi/c/ppb_audio_config.h"
10#include "ppapi/c/ppb_audio.h"
11#include "ppapi/cpp/module.h"
12#include "ppapi/tests/testing_instance.h"
13#include "ppapi/tests/test_utils.h"
14
15#if defined(__native_client__)
16#include "native_client/src/untrusted/irt/irt.h"
17#include "ppapi/native_client/src/untrusted/irt_stub/thread_creator.h"
18#endif
19
20#define ARRAYSIZE_UNSAFE(a) \
21  ((sizeof(a) / sizeof(*(a))) / \
22   static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
23
24#if defined(__native_client__)
25namespace {
26
27void GetNaClIrtPpapiHook(struct nacl_irt_ppapihook* hooks) {
28  nacl_interface_query(NACL_IRT_PPAPIHOOK_v0_1, hooks, sizeof(*hooks));
29}
30
31struct PP_ThreadFunctions g_thread_funcs = {};
32
33void ThreadFunctionsGetter(const struct PP_ThreadFunctions* thread_funcs) {
34  g_thread_funcs = *thread_funcs;
35}
36
37// In order to check if the thread_create is called, CountingThreadCreate()
38// increments this variable. Callers can check if the function is actually
39// called by looking at this value.
40int g_num_thread_create_called = 0;
41int g_num_thread_join_called = 0;
42
43int CountingThreadCreate(uintptr_t* tid,
44                         void (*func)(void* thread_argument),
45                         void* thread_argument) {
46  ++g_num_thread_create_called;
47  return g_thread_funcs.thread_create(tid, func, thread_argument);
48}
49
50int CountingThreadJoin(uintptr_t tid) {
51  ++g_num_thread_join_called;
52  return g_thread_funcs.thread_join(tid);
53}
54
55// Sets NULL for PP_ThreadFunctions to emulate the situation that
56// ppapi_register_thread_creator() is not yet called.
57void SetNullThreadFunctions() {
58  nacl_irt_ppapihook hooks;
59  GetNaClIrtPpapiHook(&hooks);
60  PP_ThreadFunctions thread_functions = {};
61  hooks.ppapi_register_thread_creator(&thread_functions);
62}
63
64void InjectCountingThreadFunctions() {
65  // First of all, we extract the system default thread functions.
66  // Internally, __nacl_register_thread_creator calls
67  // hooks.ppapi_register_thread_creator with default PP_ThreadFunctions
68  // instance. ThreadFunctionGetter stores it to g_thread_funcs.
69  nacl_irt_ppapihook hooks = { NULL, ThreadFunctionsGetter };
70  __nacl_register_thread_creator(&hooks);
71
72  // Here g_thread_funcs stores the thread functions.
73  // Inject the CountingThreadCreate.
74  PP_ThreadFunctions thread_functions = {
75    CountingThreadCreate,
76    CountingThreadJoin,
77  };
78  GetNaClIrtPpapiHook(&hooks);
79  hooks.ppapi_register_thread_creator(&thread_functions);
80}
81
82// Resets the PP_ThreadFunctions on exit from the scope.
83class ScopedThreadFunctionsResetter {
84 public:
85  ScopedThreadFunctionsResetter() {}
86  ~ScopedThreadFunctionsResetter() {
87    nacl_irt_ppapihook hooks;
88    GetNaClIrtPpapiHook(&hooks);
89    __nacl_register_thread_creator(&hooks);
90  }
91};
92
93}  // namespace
94#endif  // __native_client__
95
96REGISTER_TEST_CASE(Audio);
97
98TestAudio::TestAudio(TestingInstance* instance)
99    : TestCase(instance),
100      audio_callback_method_(NULL),
101      audio_callback_event_(instance->pp_instance()),
102      test_done_(false),
103      audio_interface_(NULL),
104      audio_interface_1_0_(NULL),
105      audio_config_interface_(NULL),
106      core_interface_(NULL) {
107}
108
109TestAudio::~TestAudio() {
110}
111
112bool TestAudio::Init() {
113  audio_interface_ = static_cast<const PPB_Audio_1_1*>(
114      pp::Module::Get()->GetBrowserInterface(PPB_AUDIO_INTERFACE_1_1));
115  audio_interface_1_0_ = static_cast<const PPB_Audio_1_0*>(
116      pp::Module::Get()->GetBrowserInterface(PPB_AUDIO_INTERFACE_1_0));
117  audio_config_interface_ = static_cast<const PPB_AudioConfig*>(
118      pp::Module::Get()->GetBrowserInterface(PPB_AUDIO_CONFIG_INTERFACE));
119  core_interface_ = static_cast<const PPB_Core*>(
120      pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
121  return audio_interface_ && audio_interface_1_0_ && audio_config_interface_ &&
122         core_interface_;
123}
124
125void TestAudio::RunTests(const std::string& filter) {
126  RUN_TEST(Creation, filter);
127  RUN_TEST(DestroyNoStop, filter);
128  RUN_TEST(Failures, filter);
129  RUN_TEST(AudioCallback1, filter);
130  RUN_TEST(AudioCallback2, filter);
131  RUN_TEST(AudioCallback3, filter);
132  RUN_TEST(AudioCallback4, filter);
133
134#if defined(__native_client__)
135  RUN_TEST(AudioThreadCreatorIsRequired, filter);
136  RUN_TEST(AudioThreadCreatorIsCalled, filter);
137#endif
138}
139
140// Test creating audio resources for all guaranteed sample rates and various
141// frame counts.
142std::string TestAudio::TestCreation() {
143  static const PP_AudioSampleRate kSampleRates[] = {
144    PP_AUDIOSAMPLERATE_44100,
145    PP_AUDIOSAMPLERATE_48000
146  };
147  static const uint32_t kRequestFrameCounts[] = {
148    PP_AUDIOMINSAMPLEFRAMECOUNT,
149    PP_AUDIOMAXSAMPLEFRAMECOUNT,
150    // Include some "okay-looking" frame counts; check their validity below.
151    PP_AUDIOSAMPLERATE_44100 / 100,  // 10ms @ 44.1kHz
152    PP_AUDIOSAMPLERATE_48000 / 100,  // 10ms @ 48kHz
153    2 * PP_AUDIOSAMPLERATE_44100 / 100,  // 20ms @ 44.1kHz
154    2 * PP_AUDIOSAMPLERATE_48000 / 100,  // 20ms @ 48kHz
155    1024,
156    2048,
157    4096
158  };
159  PP_AudioSampleRate sample_rate = audio_config_interface_->RecommendSampleRate(
160      instance_->pp_instance());
161  ASSERT_TRUE(sample_rate == PP_AUDIOSAMPLERATE_NONE ||
162              sample_rate == PP_AUDIOSAMPLERATE_44100 ||
163              sample_rate == PP_AUDIOSAMPLERATE_48000);
164  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSampleRates); i++) {
165    PP_AudioSampleRate sample_rate = kSampleRates[i];
166
167    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kRequestFrameCounts); j++) {
168      // Make a config, create the audio resource, and release the config.
169      uint32_t request_frame_count = kRequestFrameCounts[j];
170      uint32_t frame_count = audio_config_interface_->RecommendSampleFrameCount(
171          instance_->pp_instance(), sample_rate, request_frame_count);
172      PP_Resource ac = audio_config_interface_->CreateStereo16Bit(
173          instance_->pp_instance(), sample_rate, frame_count);
174      ASSERT_TRUE(ac);
175      PP_Resource audio = audio_interface_->Create(
176          instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
177      core_interface_->ReleaseResource(ac);
178      ac = 0;
179
180      ASSERT_TRUE(audio);
181      ASSERT_TRUE(audio_interface_->IsAudio(audio));
182
183      // Check that the config returned for |audio| matches what we gave it.
184      ac = audio_interface_->GetCurrentConfig(audio);
185      ASSERT_TRUE(ac);
186      ASSERT_TRUE(audio_config_interface_->IsAudioConfig(ac));
187      ASSERT_EQ(sample_rate, audio_config_interface_->GetSampleRate(ac));
188      ASSERT_EQ(frame_count, audio_config_interface_->GetSampleFrameCount(ac));
189      core_interface_->ReleaseResource(ac);
190      ac = 0;
191
192      // Start and stop audio playback. The documentation indicates that
193      // |StartPlayback()| and |StopPlayback()| may fail, but gives no
194      // indication as to why ... so check that they succeed.
195      audio_callback_method_ = &TestAudio::AudioCallbackTrivial;
196      ASSERT_TRUE(audio_interface_->StartPlayback(audio));
197      ASSERT_TRUE(audio_interface_->StopPlayback(audio));
198      audio_callback_method_ = NULL;
199
200      core_interface_->ReleaseResource(audio);
201    }
202  }
203
204  PASS();
205}
206
207// Test that releasing the resource without calling |StopPlayback()| "works".
208std::string TestAudio::TestDestroyNoStop() {
209  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 2048);
210  ASSERT_TRUE(ac);
211  audio_callback_method_ = NULL;
212  PP_Resource audio = audio_interface_->Create(
213      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
214  core_interface_->ReleaseResource(ac);
215  ac = 0;
216
217  ASSERT_TRUE(audio);
218  ASSERT_TRUE(audio_interface_->IsAudio(audio));
219
220  // Start playback and release the resource.
221  audio_callback_method_ = &TestAudio::AudioCallbackTrivial;
222  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
223  core_interface_->ReleaseResource(audio);
224  audio_callback_method_ = NULL;
225
226  PASS();
227}
228
229std::string TestAudio::TestFailures() {
230  // Test invalid parameters to |Create()|.
231
232  // We want a valid config for some of our tests of |Create()|.
233  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 2048);
234  ASSERT_TRUE(ac);
235
236  // Failure cases should never lead to the callback being called.
237  audio_callback_method_ = NULL;
238
239  // Invalid instance -> failure.
240  PP_Resource audio = audio_interface_->Create(
241      0, ac, AudioCallbackTrampoline, this);
242  ASSERT_EQ(0, audio);
243
244  // Invalid config -> failure.
245  audio = audio_interface_->Create(
246      instance_->pp_instance(), 0, AudioCallbackTrampoline, this);
247  ASSERT_EQ(0, audio);
248
249  // Null callback -> failure.
250  audio = audio_interface_->Create(
251      instance_->pp_instance(), ac, NULL, NULL);
252  ASSERT_EQ(0, audio);
253
254  core_interface_->ReleaseResource(ac);
255  ac = 0;
256
257  // Test the other functions with an invalid audio resource.
258  ASSERT_FALSE(audio_interface_->IsAudio(0));
259  ASSERT_EQ(0, audio_interface_->GetCurrentConfig(0));
260  ASSERT_FALSE(audio_interface_->StartPlayback(0));
261  ASSERT_FALSE(audio_interface_->StopPlayback(0));
262
263  PASS();
264}
265
266// NOTE: |TestAudioCallbackN| assumes that the audio callback is called at least
267// once. If the audio stream does not start up correctly or is interrupted this
268// may not be the case and these tests will fail. However, in order to properly
269// test the audio callbacks, we must have a configuration where audio can
270// successfully play, so we assume this is the case on bots.
271
272// This test starts playback and verifies that:
273//  1) the audio callback is actually called;
274//  2) that |StopPlayback()| waits for the audio callback to finish.
275std::string TestAudio::TestAudioCallback1() {
276  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
277  ASSERT_TRUE(ac);
278  audio_callback_method_ = NULL;
279  PP_Resource audio = audio_interface_->Create(
280      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
281  core_interface_->ReleaseResource(ac);
282  ac = 0;
283
284  audio_callback_event_.Reset();
285  test_done_ = false;
286
287  audio_callback_method_ = &TestAudio::AudioCallbackTest;
288  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
289
290  // Wait for the audio callback to be called.
291  audio_callback_event_.Wait();
292  ASSERT_TRUE(audio_interface_->StopPlayback(audio));
293  test_done_ = true;
294
295  // If any more audio callbacks are generated, we should crash (which is good).
296  audio_callback_method_ = NULL;
297
298  core_interface_->ReleaseResource(audio);
299
300  PASS();
301}
302
303// This is the same as |TestAudioCallback1()|, except that instead of calling
304// |StopPlayback()|, it just releases the resource.
305std::string TestAudio::TestAudioCallback2() {
306  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
307  ASSERT_TRUE(ac);
308  audio_callback_method_ = NULL;
309  PP_Resource audio = audio_interface_->Create(
310      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
311  core_interface_->ReleaseResource(ac);
312  ac = 0;
313
314  audio_callback_event_.Reset();
315  test_done_ = false;
316
317  audio_callback_method_ = &TestAudio::AudioCallbackTest;
318  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
319
320  // Wait for the audio callback to be called.
321  audio_callback_event_.Wait();
322
323  core_interface_->ReleaseResource(audio);
324
325  test_done_ = true;
326
327  // If any more audio callbacks are generated, we should crash (which is good).
328  audio_callback_method_ = NULL;
329
330  PASS();
331}
332
333// This is the same as |TestAudioCallback1()|, except that it attempts a second
334// round of |StartPlayback| and |StopPlayback| to make sure the callback
335// function still responds when using the same audio resource.
336std::string TestAudio::TestAudioCallback3() {
337  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
338  ASSERT_TRUE(ac);
339  audio_callback_method_ = NULL;
340  PP_Resource audio = audio_interface_->Create(
341      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
342  core_interface_->ReleaseResource(ac);
343  ac = 0;
344
345  audio_callback_event_.Reset();
346  test_done_ = false;
347
348  audio_callback_method_ = &TestAudio::AudioCallbackTest;
349  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
350
351  // Wait for the audio callback to be called.
352  audio_callback_event_.Wait();
353
354  ASSERT_TRUE(audio_interface_->StopPlayback(audio));
355
356  // Repeat one more |StartPlayback| & |StopPlayback| cycle, and verify again
357  // that the callback function was invoked.
358  audio_callback_event_.Reset();
359  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
360
361  // Wait for the audio callback to be called.
362  audio_callback_event_.Wait();
363  ASSERT_TRUE(audio_interface_->StopPlayback(audio));
364  test_done_ = true;
365
366  // If any more audio callbacks are generated, we should crash (which is good).
367  audio_callback_method_ = NULL;
368
369  core_interface_->ReleaseResource(audio);
370
371  PASS();
372}
373
374// This is the same as |TestAudioCallback1()|, except that it uses
375// PPB_Audio_1_0.
376std::string TestAudio::TestAudioCallback4() {
377  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
378  ASSERT_TRUE(ac);
379  audio_callback_method_ = NULL;
380  PP_Resource audio = audio_interface_1_0_->Create(
381      instance_->pp_instance(), ac, AudioCallbackTrampoline1_0, this);
382  core_interface_->ReleaseResource(ac);
383  ac = 0;
384
385  audio_callback_event_.Reset();
386  test_done_ = false;
387
388  audio_callback_method_ = &TestAudio::AudioCallbackTest;
389  ASSERT_TRUE(audio_interface_1_0_->StartPlayback(audio));
390
391  // Wait for the audio callback to be called.
392  audio_callback_event_.Wait();
393  ASSERT_TRUE(audio_interface_1_0_->StopPlayback(audio));
394  test_done_ = true;
395
396  // If any more audio callbacks are generated, we should crash (which is good).
397  audio_callback_method_ = NULL;
398
399  core_interface_->ReleaseResource(audio);
400
401  PASS();
402}
403
404#if defined(__native_client__)
405// Tests the behavior of the thread_create functions.
406// For PPB_Audio_Shared to work properly, the user code must call
407// ppapi_register_thread_creator(). This test checks the error handling for the
408// case when user code doesn't call ppapi_register_thread_creator().
409std::string TestAudio::TestAudioThreadCreatorIsRequired() {
410  // We'll inject some thread functions in this test case.
411  // Reset them at the end of this case.
412  ScopedThreadFunctionsResetter thread_resetter;
413
414  // Set the thread functions to NULLs to emulate the situation where
415  // ppapi_register_thread_creator() is not called by user code.
416  SetNullThreadFunctions();
417
418  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
419  ASSERT_TRUE(ac);
420  audio_callback_method_ = NULL;
421  PP_Resource audio = audio_interface_->Create(
422      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
423  core_interface_->ReleaseResource(ac);
424  ac = 0;
425
426  // StartPlayback() fails, because no thread creating function
427  // is available.
428  ASSERT_FALSE(audio_interface_->StartPlayback(audio));
429
430  // If any more audio callbacks are generated,
431  // we should crash (which is good).
432  audio_callback_method_ = NULL;
433
434  core_interface_->ReleaseResource(audio);
435
436  PASS();
437}
438
439// Tests whether the thread functions passed from the user code are actually
440// called.
441std::string TestAudio::TestAudioThreadCreatorIsCalled() {
442  // We'll inject some thread functions in this test case.
443  // Reset them at the end of this case.
444  ScopedThreadFunctionsResetter thread_resetter;
445
446  // Inject the thread counting function. In the injected function,
447  // when called, g_num_thread_create_called is incremented.
448  g_num_thread_create_called = 0;
449  g_num_thread_join_called = 0;
450  InjectCountingThreadFunctions();
451
452  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
453  ASSERT_TRUE(ac);
454  audio_callback_method_ = NULL;
455  PP_Resource audio = audio_interface_->Create(
456      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
457  core_interface_->ReleaseResource(ac);
458  ac = 0;
459
460  audio_callback_event_.Reset();
461  test_done_ = false;
462
463  audio_callback_method_ = &TestAudio::AudioCallbackTest;
464  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
465
466  // Wait for the audio callback to be called.
467  audio_callback_event_.Wait();
468  // Here, the injected thread_create is called, but thread_join is not yet.
469  ASSERT_EQ(1, g_num_thread_create_called);
470  ASSERT_EQ(0, g_num_thread_join_called);
471
472  ASSERT_TRUE(audio_interface_->StopPlayback(audio));
473
474  test_done_ = true;
475
476  // Here, the injected thread_join is called.
477  ASSERT_EQ(1, g_num_thread_join_called);
478
479  // If any more audio callbacks are generated,
480  // we should crash (which is good).
481  audio_callback_method_ = NULL;
482
483  core_interface_->ReleaseResource(audio);
484
485  PASS();
486}
487#endif
488
489// TODO(raymes): Test that actually playback happens correctly, etc.
490
491static void Crash() {
492  *static_cast<volatile unsigned*>(NULL) = 0xdeadbeef;
493}
494
495// static
496void TestAudio::AudioCallbackTrampoline(void* sample_buffer,
497                                        uint32_t buffer_size_in_bytes,
498                                        PP_TimeDelta latency,
499                                        void* user_data) {
500  TestAudio* thiz = static_cast<TestAudio*>(user_data);
501
502  // Crash if on the main thread.
503  if (thiz->core_interface_->IsMainThread())
504    Crash();
505
506  AudioCallbackMethod method = thiz->audio_callback_method_;
507  (thiz->*method)(sample_buffer, buffer_size_in_bytes, latency);
508}
509
510// static
511void TestAudio::AudioCallbackTrampoline1_0(void* sample_buffer,
512                                           uint32_t buffer_size_in_bytes,
513                                           void* user_data) {
514  AudioCallbackTrampoline(sample_buffer, buffer_size_in_bytes, 0.0, user_data);
515}
516
517void TestAudio::AudioCallbackTrivial(void* sample_buffer,
518                                     uint32_t buffer_size_in_bytes,
519                                     PP_TimeDelta latency) {
520  if (latency < 0)
521    Crash();
522
523  memset(sample_buffer, 0, buffer_size_in_bytes);
524}
525
526void TestAudio::AudioCallbackTest(void* sample_buffer,
527                                  uint32_t buffer_size_in_bytes,
528                                  PP_TimeDelta latency) {
529  if (test_done_ || latency < 0)
530    Crash();
531
532  memset(sample_buffer, 0, buffer_size_in_bytes);
533  audio_callback_event_.Signal();
534}
535
536PP_Resource TestAudio::CreateAudioConfig(
537    PP_AudioSampleRate sample_rate,
538    uint32_t requested_sample_frame_count) {
539  uint32_t frame_count = audio_config_interface_->RecommendSampleFrameCount(
540      instance_->pp_instance(), sample_rate, requested_sample_frame_count);
541  return audio_config_interface_->CreateStereo16Bit(
542      instance_->pp_instance(), sample_rate, frame_count);
543}
544