1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // Play silence and recover from dead servers or disconnected devices.
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22
23 #include <aaudio/AAudio.h>
24 #include <aaudio/AAudioTesting.h>
25 #include "AAudioExampleUtils.h"
26
27 // Arbitrary period for glitches, once per second at 48000 Hz.
28 #define FORCED_UNDERRUN_PERIOD_FRAMES 48000
29 // How long to sleep in a callback to cause an intentional glitch. For testing.
30 #define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
31
32 #define MAX_TIMESTAMPS 1000
33
34 #define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
35
36 #define NUM_SECONDS 1
37 #define NUM_LOOPS 4
38 #define MAX_TESTS 20
39
40 typedef struct TimestampInfo {
41 int64_t framesTotal;
42 int64_t appPosition; // frames
43 int64_t appNanoseconds;
44 int64_t timestampPosition; // frames
45 int64_t timestampNanos;
46 aaudio_result_t result;
47 } TimestampInfo;
48
49 typedef struct TimestampCallbackData_s {
50 TimestampInfo timestamps[MAX_TIMESTAMPS];
51 int64_t framesTotal = 0;
52 int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
53 int32_t timestampCount = 0; // in timestamps
54 bool forceUnderruns = false;
55 } TimestampCallbackData_t;
56
57 struct TimeStampTestLog {
58 aaudio_policy_t isMmap;
59 aaudio_sharing_mode_t sharingMode;
60 aaudio_performance_mode_t performanceMode;
61 aaudio_direction_t direction;
62 aaudio_result_t result;
63 };
64
65 static int s_numTests = 0;
66 // Use a plain old array because we reference this from the callback and do not want any
67 // automatic memory allocation.
68 static TimeStampTestLog s_testLogs[MAX_TESTS]{};
69
logTestResult(bool isMmap,aaudio_sharing_mode_t sharingMode,aaudio_performance_mode_t performanceMode,aaudio_direction_t direction,aaudio_result_t result)70 static void logTestResult(bool isMmap,
71 aaudio_sharing_mode_t sharingMode,
72 aaudio_performance_mode_t performanceMode,
73 aaudio_direction_t direction,
74 aaudio_result_t result) {
75 if(s_numTests >= MAX_TESTS) {
76 printf("ERROR - MAX_TESTS too small = %d\n", MAX_TESTS);
77 return;
78 }
79 s_testLogs[s_numTests].isMmap = isMmap;
80 s_testLogs[s_numTests].sharingMode = sharingMode;
81 s_testLogs[s_numTests].performanceMode = performanceMode;
82 s_testLogs[s_numTests].direction = direction;
83 s_testLogs[s_numTests].result = result;
84 s_numTests++;
85 }
86
printTestResults()87 static void printTestResults() {
88 for (int i = 0; i < s_numTests; i++) {
89 TimeStampTestLog *log = &s_testLogs[i];
90 printf("%2d: mmap = %3s, sharing = %9s, perf = %11s, dir = %6s ---- %4s\n",
91 i,
92 log->isMmap ? "yes" : "no",
93 getSharingModeText(log->sharingMode),
94 getPerformanceModeText(log->performanceMode),
95 getDirectionText(log->direction),
96 log->result ? "FAIL" : "pass");
97 }
98 }
99
100 // Callback function that fills the audio output buffer.
timestampDataCallbackProc(AAudioStream * stream,void * userData,void * audioData __unused,int32_t numFrames)101 aaudio_data_callback_result_t timestampDataCallbackProc(
102 AAudioStream *stream,
103 void *userData,
104 void *audioData __unused,
105 int32_t numFrames
106 ) {
107
108 // should not happen but just in case...
109 if (userData == nullptr) {
110 printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
111 return AAUDIO_CALLBACK_RESULT_STOP;
112 }
113 TimestampCallbackData_t *timestampData = (TimestampCallbackData_t *) userData;
114
115 aaudio_direction_t direction = AAudioStream_getDirection(stream);
116 if (direction == AAUDIO_DIRECTION_INPUT) {
117 timestampData->framesTotal += numFrames;
118 }
119
120 if (timestampData->forceUnderruns) {
121 if (timestampData->framesTotal > timestampData->nextFrameToGlitch) {
122 usleep(FORCED_UNDERRUN_SLEEP_MICROS);
123 printf("Simulate glitch at %lld\n", (long long) timestampData->framesTotal);
124 timestampData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
125 }
126 }
127
128 if (timestampData->timestampCount < MAX_TIMESTAMPS) {
129 TimestampInfo *timestamp = ×tampData->timestamps[timestampData->timestampCount];
130 timestamp->result = AAudioStream_getTimestamp(stream,
131 CLOCK_MONOTONIC,
132 ×tamp->timestampPosition,
133 ×tamp->timestampNanos);
134 timestamp->framesTotal = timestampData->framesTotal;
135 timestamp->appPosition = (direction == AAUDIO_DIRECTION_OUTPUT)
136 ? AAudioStream_getFramesWritten(stream)
137 : AAudioStream_getFramesRead(stream);
138 timestamp->appNanoseconds = getNanoseconds();
139 timestampData->timestampCount++;
140 }
141
142 if (direction == AAUDIO_DIRECTION_OUTPUT) {
143 timestampData->framesTotal += numFrames;
144 }
145 return AAUDIO_CALLBACK_RESULT_CONTINUE;
146 }
147
148 static TimestampCallbackData_t sTimestampData;
149
testTimeStamps(aaudio_policy_t mmapPolicy,aaudio_sharing_mode_t sharingMode,aaudio_performance_mode_t performanceMode,aaudio_direction_t direction)150 static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy,
151 aaudio_sharing_mode_t sharingMode,
152 aaudio_performance_mode_t performanceMode,
153 aaudio_direction_t direction) {
154 aaudio_result_t result = AAUDIO_OK;
155
156 int32_t framesPerBurst = 0;
157 int32_t actualChannelCount = 0;
158 int32_t actualSampleRate = 0;
159 int32_t originalBufferSize = 0;
160 int32_t requestedBufferSize = 0;
161 int32_t finalBufferSize = 0;
162 bool isMmap = false;
163 aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
164 aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
165 aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
166
167 AAudioStreamBuilder *aaudioBuilder = nullptr;
168 AAudioStream *aaudioStream = nullptr;
169
170 memset(&sTimestampData, 0, sizeof(sTimestampData));
171
172 printf("\n=================================================================================\n");
173 printf("--------- testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) --------\n",
174 mmapPolicy,
175 getSharingModeText(sharingMode),
176 getPerformanceModeText(performanceMode),
177 getDirectionText(direction));
178
179 AAudio_setMMapPolicy(mmapPolicy);
180
181 // Use an AAudioStreamBuilder to contain requested parameters.
182 result = AAudio_createStreamBuilder(&aaudioBuilder);
183 if (result != AAUDIO_OK) {
184 printf("AAudio_createStreamBuilder returned %s",
185 AAudio_convertResultToText(result));
186 goto finish;
187 }
188
189 // Request stream properties.
190 AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_I16);
191 AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
192 AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, performanceMode);
193 AAudioStreamBuilder_setDirection(aaudioBuilder, direction);
194 AAudioStreamBuilder_setDataCallback(aaudioBuilder, timestampDataCallbackProc, &sTimestampData);
195
196 // Create an AAudioStream using the Builder.
197 result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
198 if (result != AAUDIO_OK) {
199 printf("AAudioStreamBuilder_openStream returned %s",
200 AAudio_convertResultToText(result));
201 goto finish;
202 }
203
204 // Check to see what kind of stream we actually got.
205 actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
206 actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
207 actualDataFormat = AAudioStream_getFormat(aaudioStream);
208
209 actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
210 if (actualSharingMode != sharingMode) {
211 printf("did not get expected sharingMode, got %3d, skipping test\n",
212 actualSharingMode);
213 result = AAUDIO_OK;
214 goto finish;
215 }
216 actualPerformanceMode = AAudioStream_getPerformanceMode(aaudioStream);
217 if (actualPerformanceMode != performanceMode) {
218 printf("did not get expected performanceMode, got %3d, skipping test\n",
219 actualPerformanceMode);
220 result = AAUDIO_OK;
221 goto finish;
222 }
223
224 printf(" chans = %3d, rate = %6d format = %d\n",
225 actualChannelCount, actualSampleRate, actualDataFormat);
226 isMmap = AAudioStream_isMMapUsed(aaudioStream);
227 printf(" Is MMAP used? %s\n", isMmap ? "yes" : "no");
228
229 // This is the number of frames that are read in one chunk by a DMA controller
230 // or a DSP or a mixer.
231 framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
232 printf(" framesPerBurst = %3d\n", framesPerBurst);
233
234 originalBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream);
235 requestedBufferSize = 4 * framesPerBurst;
236 finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
237
238 printf(" BufferSize: original = %4d, requested = %4d, final = %4d\n",
239 originalBufferSize, requestedBufferSize, finalBufferSize);
240
241 {
242 int64_t position;
243 int64_t nanoseconds;
244 result = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC, &position, &nanoseconds);
245 printf("before start, AAudioStream_getTimestamp() returns %s\n",
246 AAudio_convertResultToText(result));
247 }
248
249 for (int runs = 0; runs < NUM_LOOPS; runs++) {
250 printf("------------------ loop #%d\n", runs);
251
252 int64_t temp = sTimestampData.framesTotal;
253 memset(&sTimestampData, 0, sizeof(sTimestampData));
254 sTimestampData.framesTotal = temp;
255
256 sTimestampData.forceUnderruns = false;
257
258 result = AAudioStream_requestStart(aaudioStream);
259 if (result != AAUDIO_OK) {
260 printf("AAudioStream_requestStart returned %s",
261 AAudio_convertResultToText(result));
262 goto finish;
263 }
264
265 for (int second = 0; second < NUM_SECONDS; second++) {
266 // Give AAudio callback time to run in the background.
267 usleep(200 * 1000);
268
269 // Periodically print the progress so we know it hasn't died.
270 printf("framesWritten = %d, XRuns = %d\n",
271 (int) AAudioStream_getFramesWritten(aaudioStream),
272 (int) AAudioStream_getXRunCount(aaudioStream)
273 );
274 }
275
276 result = AAudioStream_requestStop(aaudioStream);
277 if (result != AAUDIO_OK) {
278 printf("AAudioStream_requestStop returned %s\n",
279 AAudio_convertResultToText(result));
280 }
281
282 printf("timestampCount = %d\n", sTimestampData.timestampCount);
283 int printedGood = 0;
284 int printedBad = 0;
285 for (int i = 1; i < sTimestampData.timestampCount; i++) {
286 TimestampInfo *timestamp = &sTimestampData.timestamps[i];
287 if (timestamp->result != AAUDIO_OK) {
288 if (printedBad < 5) {
289 printf(" %3d : frames %8lld, xferd %8lld, result = %s\n",
290 i,
291 (long long) timestamp->framesTotal,
292 (long long) timestamp->appPosition,
293 AAudio_convertResultToText(timestamp->result));
294 printedBad++;
295 }
296 } else {
297 const bool posChanged = (timestamp->timestampPosition !=
298 (timestamp - 1)->timestampPosition);
299 const bool timeChanged = (timestamp->timestampNanos
300 != (timestamp - 1)->timestampNanos);
301 if ((printedGood < 20) && (posChanged || timeChanged)) {
302 bool negative = timestamp->timestampPosition < 0;
303 bool retro = (i > 0 && (timestamp->timestampPosition <
304 (timestamp - 1)->timestampPosition));
305 const char *message = negative ? " <=NEGATIVE!"
306 : (retro ? " <= RETROGRADE!" : "");
307
308 double latency = calculateLatencyMillis(timestamp->timestampPosition,
309 timestamp->timestampNanos,
310 timestamp->appPosition,
311 timestamp->appNanoseconds,
312 actualSampleRate);
313 printf(" %3d : frames %8lld, xferd %8lld",
314 i,
315 (long long) timestamp->framesTotal,
316 (long long) timestamp->appPosition);
317 printf(" STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n",
318 (long long) timestamp->timestampPosition,
319 (long long) timestamp->timestampNanos,
320 latency,
321 message);
322 printedGood++;
323 }
324 }
325 }
326
327 if (printedGood == 0) {
328 printf("ERROR - AAudioStream_getTimestamp() never gave us a valid timestamp\n");
329 result = AAUDIO_ERROR_INTERNAL;
330 } else {
331 // Make sure we do not get timestamps when stopped.
332 int64_t position;
333 int64_t time;
334 aaudio_result_t tempResult = AAudioStream_getTimestamp(aaudioStream,
335 CLOCK_MONOTONIC,
336 &position, &time);
337 if (tempResult != AAUDIO_ERROR_INVALID_STATE) {
338 printf("ERROR - AAudioStream_getTimestamp() should return"
339 " INVALID_STATE when stopped! %s\n",
340 AAudio_convertResultToText(tempResult));
341 result = AAUDIO_ERROR_INTERNAL;
342 }
343 }
344
345 // Avoid race conditions in AudioFlinger.
346 // There is normally a delay between a real user stopping and restarting a stream.
347 sleep(1);
348 }
349
350 finish:
351
352 logTestResult(isMmap, sharingMode, performanceMode, direction, result);
353
354 if (aaudioStream != nullptr) {
355 AAudioStream_close(aaudioStream);
356 }
357 AAudioStreamBuilder_delete(aaudioBuilder);
358 printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
359 return result;
360 }
361
main(int argc,char ** argv)362 int main(int argc, char **argv) {
363 (void) argc;
364 (void) argv;
365
366 aaudio_result_t result = AAUDIO_OK;
367
368 // Make printf print immediately so that debug info is not stuck
369 // in a buffer if we hang or crash.
370 setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
371
372 printf("Test Timestamps V0.1.4\n");
373
374 // Legacy
375 aaudio_policy_t policy = AAUDIO_POLICY_NEVER;
376 result = testTimeStamps(policy,
377 AAUDIO_SHARING_MODE_SHARED,
378 AAUDIO_PERFORMANCE_MODE_NONE,
379 AAUDIO_DIRECTION_INPUT);
380 result = testTimeStamps(policy,
381 AAUDIO_SHARING_MODE_SHARED,
382 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
383 AAUDIO_DIRECTION_INPUT);
384 result = testTimeStamps(policy,
385 AAUDIO_SHARING_MODE_SHARED,
386 AAUDIO_PERFORMANCE_MODE_NONE,
387 AAUDIO_DIRECTION_OUTPUT);
388 result = testTimeStamps(policy,
389 AAUDIO_SHARING_MODE_SHARED,
390 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
391 AAUDIO_DIRECTION_OUTPUT);
392
393 // MMAP
394 policy = AAUDIO_POLICY_ALWAYS;
395 result = testTimeStamps(policy,
396 AAUDIO_SHARING_MODE_EXCLUSIVE,
397 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
398 AAUDIO_DIRECTION_INPUT);
399 result = testTimeStamps(policy,
400 AAUDIO_SHARING_MODE_EXCLUSIVE,
401 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
402 AAUDIO_DIRECTION_OUTPUT);
403 result = testTimeStamps(policy,
404 AAUDIO_SHARING_MODE_SHARED,
405 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
406 AAUDIO_DIRECTION_INPUT);
407 result = testTimeStamps(policy,
408 AAUDIO_SHARING_MODE_SHARED,
409 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
410 AAUDIO_DIRECTION_OUTPUT);
411
412 printTestResults();
413
414 return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
415 }
416