QUIC - cleanup changes to sync chromium tree with internal source.
[chromium-blink-merge.git] / ppapi / tests / test_media_stream_audio_track.cc
blob6509f2a16c9b92dc30cde45384e3447a96edabe7
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.
7 #include "ppapi/tests/test_media_stream_audio_track.h"
9 // For MSVC.
10 #define _USE_MATH_DEFINES
11 #include <math.h>
12 #include <stdint.h>
14 #include <algorithm>
16 #include "ppapi/c/private/ppb_testing_private.h"
17 #include "ppapi/cpp/audio_buffer.h"
18 #include "ppapi/cpp/completion_callback.h"
19 #include "ppapi/cpp/instance.h"
20 #include "ppapi/cpp/var.h"
21 #include "ppapi/tests/test_utils.h"
22 #include "ppapi/tests/testing_instance.h"
24 REGISTER_TEST_CASE(MediaStreamAudioTrack);
26 namespace {
28 // Real constants defined in
29 // content/renderer/pepper/pepper_media_stream_audio_track_host.cc.
30 const int32_t kMaxNumberOfBuffers = 1000;
31 const int32_t kMinDuration = 10;
32 const int32_t kMaxDuration = 10000;
33 const int32_t kTimes = 3;
34 const char kJSCode[] =
35 "function gotStream(stream) {"
36 " test_stream = stream;"
37 " var track = stream.getAudioTracks()[0];"
38 " var plugin = document.getElementById('plugin');"
39 " plugin.postMessage(track);"
40 "}"
41 "var constraints = {"
42 " audio: true,"
43 " video: false,"
44 "};"
45 "navigator.getUserMedia = "
46 " navigator.getUserMedia || navigator.webkitGetUserMedia;"
47 "navigator.getUserMedia(constraints,"
48 " gotStream, function() {});";
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);";
75 // Helper to check if the |sample_rate| is listed in PP_AudioBuffer_SampleRate
76 // enum.
77 bool IsSampleRateValid(PP_AudioBuffer_SampleRate sample_rate) {
78 switch (sample_rate) {
79 case PP_AUDIOBUFFER_SAMPLERATE_8000:
80 case PP_AUDIOBUFFER_SAMPLERATE_16000:
81 case PP_AUDIOBUFFER_SAMPLERATE_22050:
82 case PP_AUDIOBUFFER_SAMPLERATE_32000:
83 case PP_AUDIOBUFFER_SAMPLERATE_44100:
84 case PP_AUDIOBUFFER_SAMPLERATE_48000:
85 case PP_AUDIOBUFFER_SAMPLERATE_96000:
86 case PP_AUDIOBUFFER_SAMPLERATE_192000:
87 return true;
88 default:
89 return false;
93 } // namespace
95 TestMediaStreamAudioTrack::TestMediaStreamAudioTrack(TestingInstance* instance)
96 : TestCase(instance),
97 event_(instance_->pp_instance()) {
100 bool TestMediaStreamAudioTrack::Init() {
101 return true;
104 TestMediaStreamAudioTrack::~TestMediaStreamAudioTrack() {
107 void TestMediaStreamAudioTrack::RunTests(const std::string& filter) {
108 RUN_TEST(Create, filter);
109 RUN_TEST(GetBuffer, filter);
110 RUN_TEST(Configure, filter);
111 RUN_TEST(ConfigureClose, filter);
112 RUN_TEST(VerifyWaveform, filter);
115 void TestMediaStreamAudioTrack::HandleMessage(const pp::Var& message) {
116 if (message.is_resource()) {
117 audio_track_ = pp::MediaStreamAudioTrack(message.AsResource());
119 event_.Signal();
122 std::string TestMediaStreamAudioTrack::TestCreate() {
123 // Create a track.
124 instance_->EvalScript(kJSCode);
125 event_.Wait();
126 event_.Reset();
128 ASSERT_FALSE(audio_track_.is_null());
129 ASSERT_FALSE(audio_track_.HasEnded());
130 ASSERT_FALSE(audio_track_.GetId().empty());
132 // Close the track.
133 audio_track_.Close();
134 ASSERT_TRUE(audio_track_.HasEnded());
135 audio_track_ = pp::MediaStreamAudioTrack();
136 PASS();
139 std::string TestMediaStreamAudioTrack::TestGetBuffer() {
140 // Create a track.
141 instance_->EvalScript(kJSCode);
142 event_.Wait();
143 event_.Reset();
145 ASSERT_FALSE(audio_track_.is_null());
146 ASSERT_FALSE(audio_track_.HasEnded());
147 ASSERT_FALSE(audio_track_.GetId().empty());
149 PP_TimeDelta timestamp = 0.0;
151 // Get |kTimes| buffers.
152 for (int i = 0; i < kTimes; ++i) {
153 TestCompletionCallbackWithOutput<pp::AudioBuffer> cc(
154 instance_->pp_instance(), false);
155 cc.WaitForResult(audio_track_.GetBuffer(cc.GetCallback()));
156 ASSERT_EQ(PP_OK, cc.result());
157 pp::AudioBuffer buffer = cc.output();
158 ASSERT_FALSE(buffer.is_null());
159 ASSERT_TRUE(IsSampleRateValid(buffer.GetSampleRate()));
160 ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS);
162 ASSERT_GE(buffer.GetTimestamp(), timestamp);
163 timestamp = buffer.GetTimestamp();
165 ASSERT_GT(buffer.GetDataBufferSize(), 0U);
166 ASSERT_TRUE(buffer.GetDataBuffer() != NULL);
168 audio_track_.RecycleBuffer(buffer);
170 // A recycled buffer should be invalidated.
171 ASSERT_EQ(buffer.GetSampleRate(), PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN);
172 ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_UNKNOWN);
173 ASSERT_EQ(buffer.GetDataBufferSize(), 0U);
174 ASSERT_TRUE(buffer.GetDataBuffer() == NULL);
177 // Close the track.
178 audio_track_.Close();
179 ASSERT_TRUE(audio_track_.HasEnded());
180 audio_track_ = pp::MediaStreamAudioTrack();
181 PASS();
184 std::string TestMediaStreamAudioTrack::CheckConfigure(
185 int32_t attrib_list[], int32_t expected_result) {
186 TestCompletionCallback cc_configure(instance_->pp_instance(), false);
187 cc_configure.WaitForResult(
188 audio_track_.Configure(attrib_list, cc_configure.GetCallback()));
189 ASSERT_EQ(expected_result, cc_configure.result());
190 PASS();
193 std::string TestMediaStreamAudioTrack::CheckGetBuffer(
194 int times, int expected_duration) {
195 PP_TimeDelta timestamp = 0.0;
196 for (int j = 0; j < times; ++j) {
197 TestCompletionCallbackWithOutput<pp::AudioBuffer> cc_get_buffer(
198 instance_->pp_instance(), false);
199 cc_get_buffer.WaitForResult(
200 audio_track_.GetBuffer(cc_get_buffer.GetCallback()));
201 ASSERT_EQ(PP_OK, cc_get_buffer.result());
202 pp::AudioBuffer buffer = cc_get_buffer.output();
203 ASSERT_FALSE(buffer.is_null());
204 ASSERT_TRUE(IsSampleRateValid(buffer.GetSampleRate()));
205 ASSERT_EQ(buffer.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS);
207 ASSERT_GE(buffer.GetTimestamp(), timestamp);
208 timestamp = buffer.GetTimestamp();
210 ASSERT_TRUE(buffer.GetDataBuffer() != NULL);
211 if (expected_duration > 0) {
212 uint32_t buffer_size = buffer.GetDataBufferSize();
213 uint32_t channels = buffer.GetNumberOfChannels();
214 uint32_t sample_rate = buffer.GetSampleRate();
215 uint32_t bytes_per_frame = channels * 2;
216 int32_t duration = expected_duration;
217 ASSERT_EQ(buffer_size % bytes_per_frame, 0U);
218 ASSERT_EQ(buffer_size,
219 (duration * sample_rate * bytes_per_frame) / 1000);
220 } else {
221 ASSERT_GT(buffer.GetDataBufferSize(), 0U);
224 audio_track_.RecycleBuffer(buffer);
226 PASS();
229 std::string TestMediaStreamAudioTrack::TestConfigure() {
230 // Create a track.
231 instance_->EvalScript(kJSCode);
232 event_.Wait();
233 event_.Reset();
235 ASSERT_FALSE(audio_track_.is_null());
236 ASSERT_FALSE(audio_track_.HasEnded());
237 ASSERT_FALSE(audio_track_.GetId().empty());
239 // Perform a |Configure()| with no attributes. This ends up making an IPC
240 // call, but the host implementation has a fast-path when there are no changes
241 // to the configuration. This test is intended to hit that fast-path and make
242 // sure it works correctly.
244 int32_t attrib_list[] = {
245 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
247 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK));
250 // Configure number of buffers.
251 struct {
252 int32_t buffers;
253 int32_t expect_result;
254 } buffers[] = {
255 { 8, PP_OK },
256 { 100, PP_OK },
257 { kMaxNumberOfBuffers, PP_OK },
258 { -1, PP_ERROR_BADARGUMENT },
259 { kMaxNumberOfBuffers + 1, PP_OK }, // Clipped to max value.
260 { 0, PP_OK }, // Use default.
262 for (size_t i = 0; i < sizeof(buffers) / sizeof(buffers[0]); ++i) {
263 int32_t attrib_list[] = {
264 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, buffers[i].buffers,
265 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
267 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list,
268 buffers[i].expect_result));
269 // Get some buffers. This should also succeed when configure fails.
270 ASSERT_SUBTEST_SUCCESS(CheckGetBuffer(kTimes, -1));
273 // Configure buffer duration.
274 struct {
275 int32_t duration;
276 int32_t expect_result;
277 } durations[] = {
278 { kMinDuration, PP_OK },
279 { 123, PP_OK },
280 { kMinDuration - 1, PP_ERROR_BADARGUMENT },
281 { kMaxDuration + 1, PP_ERROR_BADARGUMENT },
283 for (size_t i = 0; i < sizeof(durations) / sizeof(durations[0]); ++i) {
284 int32_t attrib_list[] = {
285 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION, durations[i].duration,
286 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
288 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list,
289 durations[i].expect_result));
291 // Get some buffers. This always works, but the buffer size will vary.
292 int duration =
293 durations[i].expect_result == PP_OK ? durations[i].duration : -1;
294 ASSERT_SUBTEST_SUCCESS(CheckGetBuffer(kTimes, duration));
296 // Test kMaxDuration separately since each GetBuffer will take 10 seconds.
298 int32_t attrib_list[] = {
299 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION, kMaxDuration,
300 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
302 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK));
305 // Reset the duration to prevent the next part from taking 10 seconds.
307 int32_t attrib_list[] = {
308 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION, kMinDuration,
309 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
311 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK));
314 // Configure should fail while plugin holds buffers.
316 TestCompletionCallbackWithOutput<pp::AudioBuffer> cc_get_buffer(
317 instance_->pp_instance(), false);
318 cc_get_buffer.WaitForResult(
319 audio_track_.GetBuffer(cc_get_buffer.GetCallback()));
320 ASSERT_EQ(PP_OK, cc_get_buffer.result());
321 pp::AudioBuffer buffer = cc_get_buffer.output();
322 int32_t attrib_list[] = {
323 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, 0,
324 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
326 TestCompletionCallback cc_configure(instance_->pp_instance(), false);
327 cc_configure.WaitForResult(
328 audio_track_.Configure(attrib_list, cc_configure.GetCallback()));
329 ASSERT_EQ(PP_ERROR_INPROGRESS, cc_configure.result());
330 audio_track_.RecycleBuffer(buffer);
333 // Close the track.
334 audio_track_.Close();
335 ASSERT_TRUE(audio_track_.HasEnded());
336 audio_track_ = pp::MediaStreamAudioTrack();
337 PASS();
340 std::string TestMediaStreamAudioTrack::TestConfigureClose() {
341 // Create a track.
342 instance_->EvalScript(kJSCode);
343 event_.Wait();
344 event_.Reset();
346 ASSERT_FALSE(audio_track_.is_null());
347 ASSERT_FALSE(audio_track_.HasEnded());
348 ASSERT_FALSE(audio_track_.GetId().empty());
350 // Configure the audio track and close it immediately. The Configure() call
351 // should complete.
352 int32_t attrib_list[] = {
353 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, 10,
354 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
356 TestCompletionCallback cc_configure(instance_->pp_instance(), false);
357 int32_t result = audio_track_.Configure(attrib_list,
358 cc_configure.GetCallback());
359 ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
360 audio_track_.Close();
361 cc_configure.WaitForResult(result);
362 result = cc_configure.result();
363 // Unfortunately, we can't control whether the configure succeeds or is
364 // aborted.
365 ASSERT_TRUE(result == PP_OK || result == PP_ERROR_ABORTED);
367 PASS();
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;
377 if (angle < 0) {
378 angle += 2 * M_PI;
380 return round(angle * period / (2 * M_PI));
383 std::string TestMediaStreamAudioTrack::TestVerifyWaveform() {
384 // Create a track.
385 instance_->EvalScript(kSineJSCode);
386 event_.Wait();
387 event_.Reset();
389 ASSERT_FALSE(audio_track_.is_null());
390 ASSERT_FALSE(audio_track_.HasEnded());
391 ASSERT_FALSE(audio_track_.GetId().empty());
393 // Use a weird buffer length and number of buffers.
394 const int32_t kBufferSize = 13;
395 const int32_t kNumBuffers = 3;
397 const uint32_t kChannels = 2;
398 const uint32_t kFreqLeft = 25;
399 const uint32_t kFreqRight = 100;
401 int32_t attrib_list[] = {
402 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION, kBufferSize,
403 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, kNumBuffers,
404 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE,
406 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK));
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();
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);
435 // Period of sine wave, in samples.
436 uint32_t left_period = sample_rate / kFreqLeft;
437 uint32_t right_period = sample_rate / kFreqRight;
439 int16_t* data_buffer = static_cast<int16_t*>(buffer.GetDataBuffer());
440 ASSERT_TRUE(data_buffer != NULL);
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);
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);
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);
469 audio_track_.RecycleBuffer(buffer);
472 PASS();