test_media_stream_audio_track.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. // Copyright 2014 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. // Tests PPB_MediaStreamAudioTrack interface.
  6. #include "ppapi/tests/test_media_stream_audio_track.h"
  7. // For MSVC.
  8. #define _USE_MATH_DEFINES
  9. #include <math.h>
  10. #include <stddef.h>
  11. #include <stdint.h>
  12. #include <algorithm>
  13. #include "ppapi/c/private/ppb_testing_private.h"
  14. #include "ppapi/cpp/audio_buffer.h"
  15. #include "ppapi/cpp/completion_callback.h"
  16. #include "ppapi/cpp/instance.h"
  17. #include "ppapi/cpp/var.h"
  18. #include "ppapi/tests/test_utils.h"
  19. #include "ppapi/tests/testing_instance.h"
  20. REGISTER_TEST_CASE(MediaStreamAudioTrack);
  21. namespace {
  22. // Real constants defined in
  23. // content/renderer/pepper/pepper_media_stream_audio_track_host.cc.
  24. const int32_t kMaxNumberOfBuffers = 1000;
  25. const int32_t kMinDuration = 10;
  26. const int32_t kMaxDuration = 10000;
  27. const int32_t kTimes = 3;
  28. const char kJSCode[] =
  29. "function gotStream(stream) {"
  30. " test_stream = stream;"
  31. " var track = stream.getAudioTracks()[0];"
  32. " var plugin = document.getElementById('plugin');"
  33. " plugin.postMessage(track);"
  34. "}"
  35. "var constraints = {"
  36. " audio: true,"
  37. " video: false,"
  38. "};"
  39. "navigator.getUserMedia = "
  40. " navigator.getUserMedia || navigator.webkitGetUserMedia;"
  41. "navigator.getUserMedia(constraints,"
  42. " gotStream, function() {});";
  43. const char kSineJSCode[] =
  44. // Create oscillators for the left and right channels. Use a sine wave,
  45. // which is the easiest to calculate expected values. The oscillator output
  46. // is low-pass filtered (as per spec) making comparison hard.
  47. "var context = new AudioContext();"
  48. "var l_osc = context.createOscillator();"
  49. "l_osc.type = \"sine\";"
  50. "l_osc.frequency.value = 25;"
  51. "var r_osc = context.createOscillator();"
  52. "r_osc.type = \"sine\";"
  53. "r_osc.frequency.value = 100;"
  54. // Combine the left and right channels.
  55. "var merger = context.createChannelMerger(2);"
  56. "merger.channelInterpretation = \"discrete\";"
  57. "l_osc.connect(merger, 0, 0);"
  58. "r_osc.connect(merger, 0, 1);"
  59. "var dest_stream = context.createMediaStreamDestination();"
  60. "merger.connect(dest_stream);"
  61. // Dump the generated waveform to a MediaStream output.
  62. "l_osc.start();"
  63. "r_osc.start();"
  64. "var track = dest_stream.stream.getAudioTracks()[0];"
  65. "var plugin = document.getElementById('plugin');"
  66. "plugin.postMessage(track);";
  67. // Helper to check if the |sample_rate| is listed in PP_AudioBuffer_SampleRate
  68. // enum.
  69. bool IsSampleRateValid(PP_AudioBuffer_SampleRate sample_rate) {
  70. switch (sample_rate) {
  71. case PP_AUDIOBUFFER_SAMPLERATE_8000:
  72. case PP_AUDIOBUFFER_SAMPLERATE_16000:
  73. case PP_AUDIOBUFFER_SAMPLERATE_22050:
  74. case PP_AUDIOBUFFER_SAMPLERATE_32000:
  75. case PP_AUDIOBUFFER_SAMPLERATE_44100:
  76. case PP_AUDIOBUFFER_SAMPLERATE_48000:
  77. case PP_AUDIOBUFFER_SAMPLERATE_96000:
  78. case PP_AUDIOBUFFER_SAMPLERATE_192000:
  79. return true;
  80. default:
  81. return false;
  82. }
  83. }
  84. } // namespace
  85. TestMediaStreamAudioTrack::TestMediaStreamAudioTrack(TestingInstance* instance)
  86. : TestCase(instance),
  87. event_(instance_->pp_instance()) {
  88. }
  89. bool TestMediaStreamAudioTrack::Init() {
  90. return true;
  91. }
  92. TestMediaStreamAudioTrack::~TestMediaStreamAudioTrack() {
  93. }
  94. void TestMediaStreamAudioTrack::RunTests(const std::string& filter) {
  95. RUN_TEST(Create, filter);
  96. RUN_TEST(GetBuffer, filter);
  97. RUN_TEST(Configure, filter);
  98. RUN_TEST(ConfigureClose, filter);
  99. RUN_TEST(VerifyWaveform, filter);
  100. }
  101. void TestMediaStreamAudioTrack::HandleMessage(const pp::Var& message) {
  102. if (message.is_resource()) {
  103. audio_track_ = pp::MediaStreamAudioTrack(message.AsResource());
  104. }
  105. event_.Signal();
  106. }
  107. std::string TestMediaStreamAudioTrack::TestCreate() {
  108. // Create a track.
  109. instance_->EvalScript(kJSCode);
  110. event_.Wait();
  111. event_.Reset();
  112. ASSERT_FALSE(audio_track_.is_null());
  113. ASSERT_FALSE(audio_track_.HasEnded());
  114. ASSERT_FALSE(audio_track_.GetId().empty());
  115. // Close the track.
  116. audio_track_.Close();
  117. ASSERT_TRUE(audio_track_.HasEnded());
  118. audio_track_ = pp::MediaStreamAudioTrack();
  119. PASS();
  120. }
  121. std::string TestMediaStreamAudioTrack::TestGetBuffer() {
  122. // Create a track.
  123. instance_->EvalScript(kJSCode);
  124. event_.Wait();
  125. event_.Reset();
  126. ASSERT_FALSE(audio_track_.is_null());
  127. ASSERT_FALSE(audio_track_.HasEnded());
  128. ASSERT_FALSE(audio_track_.GetId().empty());
  129. PP_TimeDelta timestamp = 0.0;
  130. // Get |kTimes| buffers.
  131. for (int i = 0; i < kTimes; ++i) {
  132. TestCompletionCallbackWithOutput<pp::AudioBuffer> cc(
  133. instance_->pp_instance(), false);
  134. cc.WaitForResult(audio_track_.GetBuffer(cc.GetCallback()));
  135. ASSERT_EQ(PP_OK, cc.result());
  136. pp::AudioBuffer buffer = cc.output();
  137. ASSERT_FALSE(buffer.is_null());
  138. ASSERT_TRUE(IsSampleRateValid(buffer.GetSampleRate()));
  139. ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS);
  140. ASSERT_GE(buffer.GetTimestamp(), timestamp);
  141. timestamp = buffer.GetTimestamp();
  142. ASSERT_GT(buffer.GetDataBufferSize(), 0U);
  143. ASSERT_TRUE(buffer.GetDataBuffer() != NULL);
  144. audio_track_.RecycleBuffer(buffer);
  145. // A recycled buffer should be invalidated.
  146. ASSERT_EQ(buffer.GetSampleRate(), PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN);
  147. ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_UNKNOWN);
  148. ASSERT_EQ(buffer.GetDataBufferSize(), 0U);
  149. ASSERT_TRUE(buffer.GetDataBuffer() == NULL);
  150. }
  151. // Close the track.
  152. audio_track_.Close();
  153. ASSERT_TRUE(audio_track_.HasEnded());
  154. audio_track_ = pp::MediaStreamAudioTrack();
  155. PASS();
  156. }
  157. std::string TestMediaStreamAudioTrack::CheckConfigure(
  158. int32_t attrib_list[], int32_t expected_result) {
  159. TestCompletionCallback cc_configure(instance_->pp_instance(), false);
  160. cc_configure.WaitForResult(
  161. audio_track_.Configure(attrib_list, cc_configure.GetCallback()));
  162. ASSERT_EQ(expected_result, cc_configure.result());
  163. PASS();
  164. }
  165. std::string TestMediaStreamAudioTrack::CheckGetBuffer(
  166. int times, int expected_duration) {
  167. PP_TimeDelta timestamp = 0.0;
  168. for (int j = 0; j < times; ++j) {
  169. TestCompletionCallbackWithOutput<pp::AudioBuffer> cc_get_buffer(
  170. instance_->pp_instance(), false);
  171. cc_get_buffer.WaitForResult(
  172. audio_track_.GetBuffer(cc_get_buffer.GetCallback()));
  173. ASSERT_EQ(PP_OK, cc_get_buffer.result());
  174. pp::AudioBuffer buffer = cc_get_buffer.output();
  175. ASSERT_FALSE(buffer.is_null());
  176. ASSERT_TRUE(IsSampleRateValid(buffer.GetSampleRate()));
  177. ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS);
  178. ASSERT_GE(buffer.GetTimestamp(), timestamp);
  179. timestamp = buffer.GetTimestamp();
  180. ASSERT_TRUE(buffer.GetDataBuffer() != NULL);
  181. if (expected_duration > 0) {
  182. uint32_t buffer_size = buffer.GetDataBufferSize();
  183. uint32_t channels = buffer.GetNumberOfChannels();
  184. uint32_t sample_rate = buffer.GetSampleRate();
  185. uint32_t bytes_per_frame = channels * 2;
  186. int32_t duration = expected_duration;
  187. ASSERT_EQ(buffer_size % bytes_per_frame, 0U);
  188. ASSERT_EQ(buffer_size,
  189. (duration * sample_rate * bytes_per_frame) / 1000);
  190. } else {
  191. ASSERT_GT(buffer.GetDataBufferSize(), 0U);
  192. }
  193. audio_track_.RecycleBuffer(buffer);
  194. }
  195. PASS();
  196. }
  197. std::string TestMediaStreamAudioTrack::TestConfigure() {
  198. // Create a track.
  199. instance_->EvalScript(kJSCode);
  200. event_.Wait();
  201. event_.Reset();
  202. ASSERT_FALSE(audio_track_.is_null());
  203. ASSERT_FALSE(audio_track_.HasEnded());
  204. ASSERT_FALSE(audio_track_.GetId().empty());
  205. // Perform a |Configure()| with no attributes. This ends up making an IPC
  206. // call, but the host implementation has a fast-path when there are no changes
  207. // to the configuration. This test is intended to hit that fast-path and make
  208. // sure it works correctly.
  209. {
  210. int32_t attrib_list[] = {
  211. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
  212. };
  213. ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK));
  214. }
  215. // Configure number of buffers.
  216. struct {
  217. int32_t buffers;
  218. int32_t expect_result;
  219. } buffers[] = {
  220. { 8, PP_OK },
  221. { 100, PP_OK },
  222. { kMaxNumberOfBuffers, PP_OK },
  223. { -1, PP_ERROR_BADARGUMENT },
  224. { kMaxNumberOfBuffers + 1, PP_OK }, // Clipped to max value.
  225. { 0, PP_OK }, // Use default.
  226. };
  227. for (size_t i = 0; i < sizeof(buffers) / sizeof(buffers[0]); ++i) {
  228. int32_t attrib_list[] = {
  229. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, buffers[i].buffers,
  230. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
  231. };
  232. ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list,
  233. buffers[i].expect_result));
  234. // Get some buffers. This should also succeed when configure fails.
  235. ASSERT_SUBTEST_SUCCESS(CheckGetBuffer(kTimes, -1));
  236. }
  237. // Configure buffer duration.
  238. struct {
  239. int32_t duration;
  240. int32_t expect_result;
  241. } durations[] = {
  242. { kMinDuration, PP_OK },
  243. { 123, PP_OK },
  244. { kMinDuration - 1, PP_ERROR_BADARGUMENT },
  245. { kMaxDuration + 1, PP_ERROR_BADARGUMENT },
  246. };
  247. for (size_t i = 0; i < sizeof(durations) / sizeof(durations[0]); ++i) {
  248. int32_t attrib_list[] = {
  249. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION, durations[i].duration,
  250. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
  251. };
  252. ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list,
  253. durations[i].expect_result));
  254. // Get some buffers. This always works, but the buffer size will vary.
  255. int duration =
  256. durations[i].expect_result == PP_OK ? durations[i].duration : -1;
  257. ASSERT_SUBTEST_SUCCESS(CheckGetBuffer(kTimes, duration));
  258. }
  259. // Test kMaxDuration separately since each GetBuffer will take 10 seconds.
  260. {
  261. int32_t attrib_list[] = {
  262. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION, kMaxDuration,
  263. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
  264. };
  265. ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK));
  266. }
  267. // Reset the duration to prevent the next part from taking 10 seconds.
  268. {
  269. int32_t attrib_list[] = {
  270. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION, kMinDuration,
  271. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
  272. };
  273. ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK));
  274. }
  275. // Configure should fail while plugin holds buffers.
  276. {
  277. TestCompletionCallbackWithOutput<pp::AudioBuffer> cc_get_buffer(
  278. instance_->pp_instance(), false);
  279. cc_get_buffer.WaitForResult(
  280. audio_track_.GetBuffer(cc_get_buffer.GetCallback()));
  281. ASSERT_EQ(PP_OK, cc_get_buffer.result());
  282. pp::AudioBuffer buffer = cc_get_buffer.output();
  283. int32_t attrib_list[] = {
  284. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, 0,
  285. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
  286. };
  287. TestCompletionCallback cc_configure(instance_->pp_instance(), false);
  288. cc_configure.WaitForResult(
  289. audio_track_.Configure(attrib_list, cc_configure.GetCallback()));
  290. ASSERT_EQ(PP_ERROR_INPROGRESS, cc_configure.result());
  291. audio_track_.RecycleBuffer(buffer);
  292. }
  293. // Close the track.
  294. audio_track_.Close();
  295. ASSERT_TRUE(audio_track_.HasEnded());
  296. audio_track_ = pp::MediaStreamAudioTrack();
  297. PASS();
  298. }
  299. std::string TestMediaStreamAudioTrack::TestConfigureClose() {
  300. // Create a track.
  301. instance_->EvalScript(kJSCode);
  302. event_.Wait();
  303. event_.Reset();
  304. ASSERT_FALSE(audio_track_.is_null());
  305. ASSERT_FALSE(audio_track_.HasEnded());
  306. ASSERT_FALSE(audio_track_.GetId().empty());
  307. // Configure the audio track and close it immediately. The Configure() call
  308. // should complete.
  309. int32_t attrib_list[] = {
  310. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, 10,
  311. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
  312. };
  313. TestCompletionCallback cc_configure(instance_->pp_instance(), false);
  314. int32_t result = audio_track_.Configure(attrib_list,
  315. cc_configure.GetCallback());
  316. ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
  317. audio_track_.Close();
  318. cc_configure.WaitForResult(result);
  319. result = cc_configure.result();
  320. // Unfortunately, we can't control whether the configure succeeds or is
  321. // aborted.
  322. ASSERT_TRUE(result == PP_OK || result == PP_ERROR_ABORTED);
  323. PASS();
  324. }
  325. uint32_t CalculateWaveStartingTime(int16_t sample, int16_t next_sample,
  326. uint32_t period) {
  327. int16_t slope = next_sample - sample;
  328. double angle = asin(sample / (double)INT16_MAX);
  329. if (slope < 0) {
  330. angle = M_PI - angle;
  331. }
  332. if (angle < 0) {
  333. angle += 2 * M_PI;
  334. }
  335. return round(angle * period / (2 * M_PI));
  336. }
  337. std::string TestMediaStreamAudioTrack::TestVerifyWaveform() {
  338. // Create a track.
  339. instance_->EvalScript(kSineJSCode);
  340. event_.Wait();
  341. event_.Reset();
  342. ASSERT_FALSE(audio_track_.is_null());
  343. ASSERT_FALSE(audio_track_.HasEnded());
  344. ASSERT_FALSE(audio_track_.GetId().empty());
  345. // Use a weird buffer length and number of buffers.
  346. const int32_t kBufferSize = 13;
  347. const int32_t kNumBuffers = 3;
  348. const uint32_t kChannels = 2;
  349. const uint32_t kFreqLeft = 25;
  350. const uint32_t kFreqRight = 100;
  351. int32_t attrib_list[] = {
  352. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION, kBufferSize,
  353. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, kNumBuffers,
  354. PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
  355. };
  356. ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK));
  357. // Get kNumBuffers buffers and verify they conform to the expected waveform.
  358. PP_TimeDelta timestamp = 0.0;
  359. int sample_time = 0;
  360. uint32_t left_start = 0;
  361. uint32_t right_start = 0;
  362. for (int j = 0; j < kNumBuffers; ++j) {
  363. TestCompletionCallbackWithOutput<pp::AudioBuffer> cc_get_buffer(
  364. instance_->pp_instance(), false);
  365. cc_get_buffer.WaitForResult(
  366. audio_track_.GetBuffer(cc_get_buffer.GetCallback()));
  367. ASSERT_EQ(PP_OK, cc_get_buffer.result());
  368. pp::AudioBuffer buffer = cc_get_buffer.output();
  369. ASSERT_FALSE(buffer.is_null());
  370. ASSERT_TRUE(IsSampleRateValid(buffer.GetSampleRate()));
  371. ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS);
  372. ASSERT_EQ(buffer.GetNumberOfChannels(), kChannels);
  373. ASSERT_GE(buffer.GetTimestamp(), timestamp);
  374. timestamp = buffer.GetTimestamp();
  375. uint32_t buffer_size = buffer.GetDataBufferSize();
  376. uint32_t sample_rate = buffer.GetSampleRate();
  377. uint32_t num_samples = buffer.GetNumberOfSamples();
  378. uint32_t bytes_per_frame = kChannels * 2;
  379. ASSERT_EQ(num_samples, (kChannels * kBufferSize * sample_rate) / 1000);
  380. ASSERT_EQ(buffer_size % bytes_per_frame, 0U);
  381. ASSERT_EQ(buffer_size, num_samples * 2);
  382. // Period of sine wave, in samples.
  383. uint32_t left_period = sample_rate / kFreqLeft;
  384. uint32_t right_period = sample_rate / kFreqRight;
  385. int16_t* data_buffer = static_cast<int16_t*>(buffer.GetDataBuffer());
  386. ASSERT_TRUE(data_buffer != NULL);
  387. if (j == 0) {
  388. // The generated wave doesn't necessarily start at 0, so compensate for
  389. // this.
  390. left_start = CalculateWaveStartingTime(data_buffer[0], data_buffer[2],
  391. left_period);
  392. right_start = CalculateWaveStartingTime(data_buffer[1], data_buffer[3],
  393. right_period);
  394. }
  395. for (uint32_t sample = 0; sample < num_samples;
  396. sample += 2, sample_time++) {
  397. int16_t left = data_buffer[sample];
  398. int16_t right = data_buffer[sample + 1];
  399. double angle = (2.0 * M_PI * ((sample_time + left_start) % left_period)) /
  400. left_period;
  401. int16_t expected = INT16_MAX * sin(angle);
  402. // Account for off-by-one errors due to rounding.
  403. ASSERT_GE(left, std::max<int16_t>(expected, INT16_MIN + 1) - 1);
  404. ASSERT_LE(left, std::min<int16_t>(expected, INT16_MAX - 1) + 1);
  405. angle = (2 * M_PI * ((sample_time + right_start) % right_period)) /
  406. right_period;
  407. expected = INT16_MAX * sin(angle);
  408. ASSERT_GE(right, std::max<int16_t>(expected, INT16_MIN + 1) - 1);
  409. ASSERT_LE(right, std::min<int16_t>(expected, INT16_MAX - 1) + 1);
  410. }
  411. audio_track_.RecycleBuffer(buffer);
  412. }
  413. PASS();
  414. }