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.
5 // Tests PPB_MediaStreamAudioTrack interface.
7 #include "ppapi/tests/test_media_stream_audio_track.h"
9 #include "ppapi/c/private/ppb_testing_private.h"
10 #include "ppapi/cpp/audio_buffer.h"
11 #include "ppapi/cpp/completion_callback.h"
12 #include "ppapi/cpp/instance.h"
13 #include "ppapi/cpp/var.h"
14 #include "ppapi/tests/test_utils.h"
15 #include "ppapi/tests/testing_instance.h"
17 REGISTER_TEST_CASE(MediaStreamAudioTrack
);
21 // Real constants defined in
22 // content/renderer/pepper/pepper_media_stream_audio_track_host.cc.
23 const int32_t kMaxNumberOfBuffers
= 1000;
24 const int32_t kMinDuration
= 10;
25 const int32_t kMaxDuration
= 10000;
26 const int32_t kTimes
= 3;
27 const char kJSCode
[] =
28 "function gotStream(stream) {"
29 " test_stream = stream;"
30 " var track = stream.getAudioTracks()[0];"
31 " var plugin = document.getElementById('plugin');"
32 " plugin.postMessage(track);"
38 "navigator.getUserMedia = "
39 " navigator.getUserMedia || navigator.webkitGetUserMedia;"
40 "navigator.getUserMedia(constraints,"
41 " gotStream, function() {});";
43 // Helper to check if the |sample_rate| is listed in PP_AudioBuffer_SampleRate
45 bool IsSampleRateValid(PP_AudioBuffer_SampleRate sample_rate
) {
46 switch (sample_rate
) {
47 case PP_AUDIOBUFFER_SAMPLERATE_8000
:
48 case PP_AUDIOBUFFER_SAMPLERATE_16000
:
49 case PP_AUDIOBUFFER_SAMPLERATE_22050
:
50 case PP_AUDIOBUFFER_SAMPLERATE_32000
:
51 case PP_AUDIOBUFFER_SAMPLERATE_44100
:
52 case PP_AUDIOBUFFER_SAMPLERATE_48000
:
53 case PP_AUDIOBUFFER_SAMPLERATE_96000
:
54 case PP_AUDIOBUFFER_SAMPLERATE_192000
:
63 TestMediaStreamAudioTrack::TestMediaStreamAudioTrack(TestingInstance
* instance
)
65 event_(instance_
->pp_instance()) {
68 bool TestMediaStreamAudioTrack::Init() {
72 TestMediaStreamAudioTrack::~TestMediaStreamAudioTrack() {
75 void TestMediaStreamAudioTrack::RunTests(const std::string
& filter
) {
76 RUN_TEST(Create
, filter
);
77 RUN_TEST(GetBuffer
, filter
);
78 RUN_TEST(Configure
, filter
);
79 RUN_TEST(ConfigureClose
, filter
);
82 void TestMediaStreamAudioTrack::HandleMessage(const pp::Var
& message
) {
83 if (message
.is_resource()) {
84 audio_track_
= pp::MediaStreamAudioTrack(message
.AsResource());
89 std::string
TestMediaStreamAudioTrack::TestCreate() {
91 instance_
->EvalScript(kJSCode
);
95 ASSERT_FALSE(audio_track_
.is_null());
96 ASSERT_FALSE(audio_track_
.HasEnded());
97 ASSERT_FALSE(audio_track_
.GetId().empty());
100 audio_track_
.Close();
101 ASSERT_TRUE(audio_track_
.HasEnded());
102 audio_track_
= pp::MediaStreamAudioTrack();
106 std::string
TestMediaStreamAudioTrack::TestGetBuffer() {
108 instance_
->EvalScript(kJSCode
);
112 ASSERT_FALSE(audio_track_
.is_null());
113 ASSERT_FALSE(audio_track_
.HasEnded());
114 ASSERT_FALSE(audio_track_
.GetId().empty());
116 PP_TimeDelta timestamp
= 0.0;
118 // Get |kTimes| buffers.
119 for (int i
= 0; i
< kTimes
; ++i
) {
120 TestCompletionCallbackWithOutput
<pp::AudioBuffer
> cc(
121 instance_
->pp_instance(), false);
122 cc
.WaitForResult(audio_track_
.GetBuffer(cc
.GetCallback()));
123 ASSERT_EQ(PP_OK
, cc
.result());
124 pp::AudioBuffer buffer
= cc
.output();
125 ASSERT_FALSE(buffer
.is_null());
126 ASSERT_TRUE(IsSampleRateValid(buffer
.GetSampleRate()));
127 ASSERT_EQ(buffer
.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS
);
129 ASSERT_GE(buffer
.GetTimestamp(), timestamp
);
130 timestamp
= buffer
.GetTimestamp();
132 ASSERT_GT(buffer
.GetDataBufferSize(), 0U);
133 ASSERT_TRUE(buffer
.GetDataBuffer() != NULL
);
135 audio_track_
.RecycleBuffer(buffer
);
137 // A recycled buffer should be invalidated.
138 ASSERT_EQ(buffer
.GetSampleRate(), PP_AUDIOBUFFER_SAMPLERATE_UNKNOWN
);
139 ASSERT_EQ(buffer
.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_UNKNOWN
);
140 ASSERT_EQ(buffer
.GetDataBufferSize(), 0U);
141 ASSERT_TRUE(buffer
.GetDataBuffer() == NULL
);
145 audio_track_
.Close();
146 ASSERT_TRUE(audio_track_
.HasEnded());
147 audio_track_
= pp::MediaStreamAudioTrack();
151 std::string
TestMediaStreamAudioTrack::CheckConfigure(
152 int32_t attrib_list
[], int32_t expected_result
) {
153 TestCompletionCallback
cc_configure(instance_
->pp_instance(), false);
154 cc_configure
.WaitForResult(
155 audio_track_
.Configure(attrib_list
, cc_configure
.GetCallback()));
156 ASSERT_EQ(expected_result
, cc_configure
.result());
160 std::string
TestMediaStreamAudioTrack::CheckGetBuffer(
161 int times
, int expected_duration
) {
162 PP_TimeDelta timestamp
= 0.0;
163 for (int j
= 0; j
< times
; ++j
) {
164 TestCompletionCallbackWithOutput
<pp::AudioBuffer
> cc_get_buffer(
165 instance_
->pp_instance(), false);
166 cc_get_buffer
.WaitForResult(
167 audio_track_
.GetBuffer(cc_get_buffer
.GetCallback()));
168 ASSERT_EQ(PP_OK
, cc_get_buffer
.result());
169 pp::AudioBuffer buffer
= cc_get_buffer
.output();
170 ASSERT_FALSE(buffer
.is_null());
171 ASSERT_TRUE(IsSampleRateValid(buffer
.GetSampleRate()));
172 ASSERT_EQ(buffer
.GetSampleSize(), PP_AUDIOBUFFER_SAMPLESIZE_16_BITS
);
174 ASSERT_GE(buffer
.GetTimestamp(), timestamp
);
175 timestamp
= buffer
.GetTimestamp();
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
);
180 if (expected_duration
> 0) {
181 uint32_t buffer_size
= buffer
.GetDataBufferSize();
182 uint32_t channels
= buffer
.GetNumberOfChannels();
183 uint32_t sample_rate
= buffer
.GetSampleRate();
184 uint32_t bytes_per_frame
= channels
* 2;
185 int32_t duration
= expected_duration
;
186 ASSERT_EQ(buffer_size
% bytes_per_frame
, 0U);
187 ASSERT_EQ(buffer_size
,
188 (duration
* sample_rate
* bytes_per_frame
) / 1000);
190 ASSERT_GT(buffer
.GetDataBufferSize(), 0U);
193 audio_track_
.RecycleBuffer(buffer
);
198 std::string
TestMediaStreamAudioTrack::TestConfigure() {
200 instance_
->EvalScript(kJSCode
);
204 ASSERT_FALSE(audio_track_
.is_null());
205 ASSERT_FALSE(audio_track_
.HasEnded());
206 ASSERT_FALSE(audio_track_
.GetId().empty());
208 // Perform a |Configure()| with no attributes. This ends up making an IPC
209 // call, but the host implementation has a fast-path when there are no changes
210 // to the configuration. This test is intended to hit that fast-path and make
211 // sure it works correctly.
213 int32_t attrib_list
[] = {
214 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE
,
216 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list
, PP_OK
));
219 // Configure number of buffers.
222 int32_t expect_result
;
226 { kMaxNumberOfBuffers
, PP_OK
},
227 { -1, PP_ERROR_BADARGUMENT
},
228 { kMaxNumberOfBuffers
+ 1, PP_OK
}, // Clipped to max value.
229 { 0, PP_OK
}, // Use default.
231 for (size_t i
= 0; i
< sizeof(buffers
) / sizeof(buffers
[0]); ++i
) {
232 int32_t attrib_list
[] = {
233 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS
, buffers
[i
].buffers
,
234 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE
,
236 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list
,
237 buffers
[i
].expect_result
));
238 // Get some buffers. This should also succeed when configure fails.
239 ASSERT_SUBTEST_SUCCESS(CheckGetBuffer(kTimes
, -1));
242 // Configure buffer duration.
245 int32_t expect_result
;
247 { kMinDuration
, PP_OK
},
249 { kMinDuration
- 1, PP_ERROR_BADARGUMENT
},
250 { kMaxDuration
+ 1, PP_ERROR_BADARGUMENT
},
252 for (size_t i
= 0; i
< sizeof(durations
) / sizeof(durations
[0]); ++i
) {
253 int32_t attrib_list
[] = {
254 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION
, durations
[i
].duration
,
255 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE
,
257 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list
,
258 durations
[i
].expect_result
));
260 // Get some buffers. This always works, but the buffer size will vary.
262 durations
[i
].expect_result
== PP_OK
? durations
[i
].duration
: -1;
263 ASSERT_SUBTEST_SUCCESS(CheckGetBuffer(kTimes
, duration
));
265 // Test kMaxDuration separately since each GetBuffer will take 10 seconds.
267 int32_t attrib_list
[] = {
268 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION
, kMaxDuration
,
269 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE
,
271 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list
, PP_OK
));
274 // Reset the duration to prevent the next part from taking 10 seconds.
276 int32_t attrib_list
[] = {
277 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_DURATION
, kMinDuration
,
278 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE
,
280 ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list
, PP_OK
));
283 // Configure should fail while plugin holds buffers.
285 TestCompletionCallbackWithOutput
<pp::AudioBuffer
> cc_get_buffer(
286 instance_
->pp_instance(), false);
287 cc_get_buffer
.WaitForResult(
288 audio_track_
.GetBuffer(cc_get_buffer
.GetCallback()));
289 ASSERT_EQ(PP_OK
, cc_get_buffer
.result());
290 pp::AudioBuffer buffer
= cc_get_buffer
.output();
291 int32_t attrib_list
[] = {
292 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS
, 0,
293 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE
,
295 TestCompletionCallback
cc_configure(instance_
->pp_instance(), false);
296 cc_configure
.WaitForResult(
297 audio_track_
.Configure(attrib_list
, cc_configure
.GetCallback()));
298 ASSERT_EQ(PP_ERROR_INPROGRESS
, cc_configure
.result());
299 audio_track_
.RecycleBuffer(buffer
);
303 audio_track_
.Close();
304 ASSERT_TRUE(audio_track_
.HasEnded());
305 audio_track_
= pp::MediaStreamAudioTrack();
309 std::string
TestMediaStreamAudioTrack::TestConfigureClose() {
311 instance_
->EvalScript(kJSCode
);
315 ASSERT_FALSE(audio_track_
.is_null());
316 ASSERT_FALSE(audio_track_
.HasEnded());
317 ASSERT_FALSE(audio_track_
.GetId().empty());
319 // Configure the audio track and close it immediately. The Configure() call
321 int32_t attrib_list
[] = {
322 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS
, 10,
323 PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE
,
325 TestCompletionCallback
cc_configure(instance_
->pp_instance(), false);
326 int32_t result
= audio_track_
.Configure(attrib_list
,
327 cc_configure
.GetCallback());
328 ASSERT_EQ(PP_OK_COMPLETIONPENDING
, result
);
329 audio_track_
.Close();
330 cc_configure
.WaitForResult(result
);
331 result
= cc_configure
.result();
332 // Unfortunately, we can't control whether the configure succeeds or is
334 ASSERT_TRUE(result
== PP_OK
|| result
== PP_ERROR_ABORTED
);