OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // Tests PPB_MediaStreamAudioTrack interface. | 5 // Tests PPB_MediaStreamAudioTrack interface. |
6 | 6 |
7 #include "ppapi/tests/test_media_stream_audio_track.h" | 7 #include "ppapi/tests/test_media_stream_audio_track.h" |
8 | 8 |
| 9 // For MSVC. |
| 10 #define _USE_MATH_DEFINES |
| 11 #include <math.h> |
| 12 #include <stdint.h> |
| 13 |
| 14 #include <algorithm> |
| 15 |
9 #include "ppapi/c/private/ppb_testing_private.h" | 16 #include "ppapi/c/private/ppb_testing_private.h" |
10 #include "ppapi/cpp/audio_buffer.h" | 17 #include "ppapi/cpp/audio_buffer.h" |
11 #include "ppapi/cpp/completion_callback.h" | 18 #include "ppapi/cpp/completion_callback.h" |
12 #include "ppapi/cpp/instance.h" | 19 #include "ppapi/cpp/instance.h" |
13 #include "ppapi/cpp/var.h" | 20 #include "ppapi/cpp/var.h" |
14 #include "ppapi/tests/test_utils.h" | 21 #include "ppapi/tests/test_utils.h" |
15 #include "ppapi/tests/testing_instance.h" | 22 #include "ppapi/tests/testing_instance.h" |
16 | 23 |
17 REGISTER_TEST_CASE(MediaStreamAudioTrack); | 24 REGISTER_TEST_CASE(MediaStreamAudioTrack); |
18 | 25 |
(...skipping 14 matching lines...) Expand all Loading... |
33 "}" | 40 "}" |
34 "var constraints = {" | 41 "var constraints = {" |
35 " audio: true," | 42 " audio: true," |
36 " video: false," | 43 " video: false," |
37 "};" | 44 "};" |
38 "navigator.getUserMedia = " | 45 "navigator.getUserMedia = " |
39 " navigator.getUserMedia || navigator.webkitGetUserMedia;" | 46 " navigator.getUserMedia || navigator.webkitGetUserMedia;" |
40 "navigator.getUserMedia(constraints," | 47 "navigator.getUserMedia(constraints," |
41 " gotStream, function() {});"; | 48 " gotStream, function() {});"; |
42 | 49 |
| 50 const char kSineJSCode[] = |
| 51 // Create oscillators for the left and right channels. Use a sine wave, |
| 52 // which is the easiest to calculate expected values. The oscillator output |
| 53 // is low-pass filtered (as per spec) making comparison hard. |
| 54 "var context = new AudioContext();" |
| 55 "var l_osc = context.createOscillator();" |
| 56 "l_osc.type = \"sine\";" |
| 57 "l_osc.frequency.value = 25;" |
| 58 "var r_osc = context.createOscillator();" |
| 59 "r_osc.type = \"sine\";" |
| 60 "r_osc.frequency.value = 100;" |
| 61 // Combine the left and right channels. |
| 62 "var merger = context.createChannelMerger(2);" |
| 63 "merger.channelInterpretation = \"discrete\";" |
| 64 "l_osc.connect(merger, 0, 0);" |
| 65 "r_osc.connect(merger, 0, 1);" |
| 66 "var dest_stream = context.createMediaStreamDestination();" |
| 67 "merger.connect(dest_stream);" |
| 68 // Dump the generated waveform to a MediaStream output. |
| 69 "l_osc.start();" |
| 70 "r_osc.start();" |
| 71 "var track = dest_stream.stream.getAudioTracks()[0];" |
| 72 "var plugin = document.getElementById('plugin');" |
| 73 "plugin.postMessage(track);"; |
| 74 |
43 // Helper to check if the |sample_rate| is listed in PP_AudioBuffer_SampleRate | 75 // Helper to check if the |sample_rate| is listed in PP_AudioBuffer_SampleRate |
44 // enum. | 76 // enum. |
45 bool IsSampleRateValid(PP_AudioBuffer_SampleRate sample_rate) { | 77 bool IsSampleRateValid(PP_AudioBuffer_SampleRate sample_rate) { |
46 switch (sample_rate) { | 78 switch (sample_rate) { |
47 case PP_AUDIOBUFFER_SAMPLERATE_8000: | 79 case PP_AUDIOBUFFER_SAMPLERATE_8000: |
48 case PP_AUDIOBUFFER_SAMPLERATE_16000: | 80 case PP_AUDIOBUFFER_SAMPLERATE_16000: |
49 case PP_AUDIOBUFFER_SAMPLERATE_22050: | 81 case PP_AUDIOBUFFER_SAMPLERATE_22050: |
50 case PP_AUDIOBUFFER_SAMPLERATE_32000: | 82 case PP_AUDIOBUFFER_SAMPLERATE_32000: |
51 case PP_AUDIOBUFFER_SAMPLERATE_44100: | 83 case PP_AUDIOBUFFER_SAMPLERATE_44100: |
52 case PP_AUDIOBUFFER_SAMPLERATE_48000: | 84 case PP_AUDIOBUFFER_SAMPLERATE_48000: |
(...skipping 17 matching lines...) Expand all Loading... |
70 } | 102 } |
71 | 103 |
72 TestMediaStreamAudioTrack::~TestMediaStreamAudioTrack() { | 104 TestMediaStreamAudioTrack::~TestMediaStreamAudioTrack() { |
73 } | 105 } |
74 | 106 |
75 void TestMediaStreamAudioTrack::RunTests(const std::string& filter) { | 107 void TestMediaStreamAudioTrack::RunTests(const std::string& filter) { |
76 RUN_TEST(Create, filter); | 108 RUN_TEST(Create, filter); |
77 RUN_TEST(GetBuffer, filter); | 109 RUN_TEST(GetBuffer, filter); |
78 RUN_TEST(Configure, filter); | 110 RUN_TEST(Configure, filter); |
79 RUN_TEST(ConfigureClose, filter); | 111 RUN_TEST(ConfigureClose, filter); |
| 112 RUN_TEST(VerifyWaveform, filter); |
80 } | 113 } |
81 | 114 |
82 void TestMediaStreamAudioTrack::HandleMessage(const pp::Var& message) { | 115 void TestMediaStreamAudioTrack::HandleMessage(const pp::Var& message) { |
83 if (message.is_resource()) { | 116 if (message.is_resource()) { |
84 audio_track_ = pp::MediaStreamAudioTrack(message.AsResource()); | 117 audio_track_ = pp::MediaStreamAudioTrack(message.AsResource()); |
85 } | 118 } |
86 event_.Signal(); | 119 event_.Signal(); |
87 } | 120 } |
88 | 121 |
89 std::string TestMediaStreamAudioTrack::TestCreate() { | 122 std::string TestMediaStreamAudioTrack::TestCreate() { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
167 audio_track_.GetBuffer(cc_get_buffer.GetCallback())); | 200 audio_track_.GetBuffer(cc_get_buffer.GetCallback())); |
168 ASSERT_EQ(PP_OK, cc_get_buffer.result()); | 201 ASSERT_EQ(PP_OK, cc_get_buffer.result()); |
169 pp::AudioBuffer buffer = cc_get_buffer.output(); | 202 pp::AudioBuffer buffer = cc_get_buffer.output(); |
170 ASSERT_FALSE(buffer.is_null()); | 203 ASSERT_FALSE(buffer.is_null()); |
171 ASSERT_TRUE(IsSampleRateValid(buffer.GetSampleRate())); | 204 ASSERT_TRUE(IsSampleRateValid(buffer.GetSampleRate())); |
172 ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS); | 205 ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS); |
173 | 206 |
174 ASSERT_GE(buffer.GetTimestamp(), timestamp); | 207 ASSERT_GE(buffer.GetTimestamp(), timestamp); |
175 timestamp = buffer.GetTimestamp(); | 208 timestamp = buffer.GetTimestamp(); |
176 | 209 |
177 // TODO(amistry): Figure out how to inject a predictable audio pattern, such | |
178 // as a sawtooth, and check the buffer data to make sure it's correct. | |
179 ASSERT_TRUE(buffer.GetDataBuffer() != NULL); | 210 ASSERT_TRUE(buffer.GetDataBuffer() != NULL); |
180 if (expected_duration > 0) { | 211 if (expected_duration > 0) { |
181 uint32_t buffer_size = buffer.GetDataBufferSize(); | 212 uint32_t buffer_size = buffer.GetDataBufferSize(); |
182 uint32_t channels = buffer.GetNumberOfChannels(); | 213 uint32_t channels = buffer.GetNumberOfChannels(); |
183 uint32_t sample_rate = buffer.GetSampleRate(); | 214 uint32_t sample_rate = buffer.GetSampleRate(); |
184 uint32_t bytes_per_frame = channels * 2; | 215 uint32_t bytes_per_frame = channels * 2; |
185 int32_t duration = expected_duration; | 216 int32_t duration = expected_duration; |
186 ASSERT_EQ(buffer_size % bytes_per_frame, 0U); | 217 ASSERT_EQ(buffer_size % bytes_per_frame, 0U); |
187 ASSERT_EQ(buffer_size, | 218 ASSERT_EQ(buffer_size, |
188 (duration * sample_rate * bytes_per_frame) / 1000); | 219 (duration * sample_rate * bytes_per_frame) / 1000); |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
328 ASSERT_EQ(PP_OK_COMPLETIONPENDING, result); | 359 ASSERT_EQ(PP_OK_COMPLETIONPENDING, result); |
329 audio_track_.Close(); | 360 audio_track_.Close(); |
330 cc_configure.WaitForResult(result); | 361 cc_configure.WaitForResult(result); |
331 result = cc_configure.result(); | 362 result = cc_configure.result(); |
332 // Unfortunately, we can't control whether the configure succeeds or is | 363 // Unfortunately, we can't control whether the configure succeeds or is |
333 // aborted. | 364 // aborted. |
334 ASSERT_TRUE(result == PP_OK || result == PP_ERROR_ABORTED); | 365 ASSERT_TRUE(result == PP_OK || result == PP_ERROR_ABORTED); |
335 | 366 |
336 PASS(); | 367 PASS(); |
337 } | 368 } |
| 369 |
| 370 uint32_t CalculateWaveStartingTime(int16_t sample, int16_t next_sample, |
| 371 uint32_t period) { |
| 372 int16_t slope = next_sample - sample; |
| 373 double angle = asin(sample / (double)INT16_MAX); |
| 374 if (slope < 0) { |
| 375 angle = M_PI - angle; |
| 376 } |
| 377 if (angle < 0) { |
| 378 angle += 2 * M_PI; |
| 379 } |
| 380 return round(angle * period / (2 * M_PI)); |
| 381 } |
| 382 |
| 383 std::string TestMediaStreamAudioTrack::TestVerifyWaveform() { |
| 384 // Create a track. |
| 385 instance_->EvalScript(kSineJSCode); |
| 386 event_.Wait(); |
| 387 event_.Reset(); |
| 388 |
| 389 ASSERT_FALSE(audio_track_.is_null()); |
| 390 ASSERT_FALSE(audio_track_.HasEnded()); |
| 391 ASSERT_FALSE(audio_track_.GetId().empty()); |
| 392 |
| 393 // Use a weird buffer length and number of buffers. |
| 394 const int32_t kBufferSize = 13; |
| 395 const int32_t kNumBuffers = 3; |
| 396 |
| 397 const uint32_t kChannels = 2; |
| 398 const uint32_t kFreqLeft = 25; |
| 399 const uint32_t kFreqRight = 100; |
| 400 |
| 401 int32_t attrib_list[] = { |
| 402 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION, kBufferSize, |
| 403 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, kNumBuffers, |
| 404 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE, |
| 405 }; |
| 406 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK)); |
| 407 |
| 408 // Get kNumBuffers buffers and verify they conform to the expected waveform. |
| 409 PP_TimeDelta timestamp = 0.0; |
| 410 int sample_time = 0; |
| 411 uint32_t left_start = 0; |
| 412 uint32_t right_start = 0; |
| 413 for (int j = 0; j < kNumBuffers; ++j) { |
| 414 TestCompletionCallbackWithOutput<pp::AudioBuffer> cc_get_buffer( |
| 415 instance_->pp_instance(), false); |
| 416 cc_get_buffer.WaitForResult( |
| 417 audio_track_.GetBuffer(cc_get_buffer.GetCallback())); |
| 418 ASSERT_EQ(PP_OK, cc_get_buffer.result()); |
| 419 pp::AudioBuffer buffer = cc_get_buffer.output(); |
| 420 ASSERT_FALSE(buffer.is_null()); |
| 421 ASSERT_TRUE(IsSampleRateValid(buffer.GetSampleRate())); |
| 422 ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS); |
| 423 ASSERT_EQ(buffer.GetNumberOfChannels(), kChannels); |
| 424 ASSERT_GE(buffer.GetTimestamp(), timestamp); |
| 425 timestamp = buffer.GetTimestamp(); |
| 426 |
| 427 uint32_t buffer_size = buffer.GetDataBufferSize(); |
| 428 uint32_t sample_rate = buffer.GetSampleRate(); |
| 429 uint32_t num_samples = buffer.GetNumberOfSamples(); |
| 430 uint32_t bytes_per_frame = kChannels * 2; |
| 431 ASSERT_EQ(num_samples, (kChannels * kBufferSize * sample_rate) / 1000); |
| 432 ASSERT_EQ(buffer_size % bytes_per_frame, 0U); |
| 433 ASSERT_EQ(buffer_size, num_samples * 2); |
| 434 |
| 435 // Period of sine wave, in samples. |
| 436 uint32_t left_period = sample_rate / kFreqLeft; |
| 437 uint32_t right_period = sample_rate / kFreqRight; |
| 438 |
| 439 int16_t* data_buffer = static_cast<int16_t*>(buffer.GetDataBuffer()); |
| 440 ASSERT_TRUE(data_buffer != NULL); |
| 441 |
| 442 if (j == 0) { |
| 443 // The generated wave doesn't necessarily start at 0, so compensate for |
| 444 // this. |
| 445 left_start = CalculateWaveStartingTime(data_buffer[0], data_buffer[2], |
| 446 left_period); |
| 447 right_start = CalculateWaveStartingTime(data_buffer[1], data_buffer[3], |
| 448 right_period); |
| 449 } |
| 450 |
| 451 for (uint32_t sample = 0; sample < num_samples; |
| 452 sample += 2, sample_time++) { |
| 453 int16_t left = data_buffer[sample]; |
| 454 int16_t right = data_buffer[sample + 1]; |
| 455 double angle = (2.0 * M_PI * ((sample_time + left_start) % left_period)) / |
| 456 left_period; |
| 457 int16_t expected = INT16_MAX * sin(angle); |
| 458 // Account for off-by-one errors due to rounding. |
| 459 ASSERT_GE(left, std::max<int16_t>(expected, INT16_MIN + 1) - 1); |
| 460 ASSERT_LE(left, std::min<int16_t>(expected, INT16_MAX - 1) + 1); |
| 461 |
| 462 angle = (2 * M_PI * ((sample_time + right_start) % right_period)) / |
| 463 right_period; |
| 464 expected = INT16_MAX * sin(angle); |
| 465 ASSERT_GE(right, std::max<int16_t>(expected, INT16_MIN + 1) - 1); |
| 466 ASSERT_LE(right, std::min<int16_t>(expected, INT16_MAX - 1) + 1); |
| 467 } |
| 468 |
| 469 audio_track_.RecycleBuffer(buffer); |
| 470 } |
| 471 |
| 472 PASS(); |
| 473 } |
OLD | NEW |