test_timestamps.cpp revision c75d97f20843388b9084561635996a01a53d0c0f
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 "utils/AAudioExampleUtils.h"
26#include "../examples/utils/AAudioExampleUtils.h"
27
28// Arbitrary period for glitches, once per second at 48000 Hz.
29#define FORCED_UNDERRUN_PERIOD_FRAMES    48000
30// How long to sleep in a callback to cause an intentional glitch. For testing.
31#define FORCED_UNDERRUN_SLEEP_MICROS     (10 * 1000)
32
33#define MAX_TIMESTAMPS          1000
34
35#define DEFAULT_TIMEOUT_NANOS   ((int64_t)1000000000)
36
37#define NUM_SECONDS             1
38#define NUM_LOOPS               4
39
40typedef 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
49typedef 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// Callback function that fills the audio output buffer.
58aaudio_data_callback_result_t timestampDataCallbackProc(
59        AAudioStream *stream,
60        void *userData,
61        void *audioData __unused,
62        int32_t numFrames
63) {
64
65    // should not happen but just in case...
66    if (userData == nullptr) {
67        printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
68        return AAUDIO_CALLBACK_RESULT_STOP;
69    }
70    TimestampCallbackData_t *timestampData = (TimestampCallbackData_t *) userData;
71
72    aaudio_direction_t direction = AAudioStream_getDirection(stream);
73    if (direction == AAUDIO_DIRECTION_INPUT) {
74        timestampData->framesTotal += numFrames;
75    }
76
77    if (timestampData->forceUnderruns) {
78        if (timestampData->framesTotal > timestampData->nextFrameToGlitch) {
79            usleep(FORCED_UNDERRUN_SLEEP_MICROS);
80            printf("Simulate glitch at %lld\n", (long long) timestampData->framesTotal);
81            timestampData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
82        }
83    }
84
85    if (timestampData->timestampCount < MAX_TIMESTAMPS) {
86        TimestampInfo *timestamp = &timestampData->timestamps[timestampData->timestampCount];
87        timestamp->result = AAudioStream_getTimestamp(stream,
88                                                      CLOCK_MONOTONIC,
89                                                      &timestamp->timestampPosition,
90                                                      &timestamp->timestampNanos);
91        timestamp->framesTotal = timestampData->framesTotal;
92        timestamp->appPosition = (direction == AAUDIO_DIRECTION_OUTPUT)
93                ? AAudioStream_getFramesWritten(stream)
94                : AAudioStream_getFramesRead(stream);
95        timestamp->appNanoseconds = getNanoseconds();
96        timestampData->timestampCount++;
97    }
98
99    if (direction == AAUDIO_DIRECTION_OUTPUT) {
100        timestampData->framesTotal += numFrames;
101    }
102    return AAUDIO_CALLBACK_RESULT_CONTINUE;
103}
104
105static TimestampCallbackData_t sTimestampData;
106
107static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy,
108                           aaudio_sharing_mode_t sharingMode,
109                           aaudio_performance_mode_t performanceMode,
110                           aaudio_direction_t direction) {
111    aaudio_result_t result = AAUDIO_OK;
112
113    int32_t framesPerBurst = 0;
114    float *buffer = nullptr;
115
116    int32_t actualChannelCount = 0;
117    int32_t actualSampleRate = 0;
118    int32_t originalBufferSize = 0;
119    int32_t requestedBufferSize = 0;
120    int32_t finalBufferSize = 0;
121    aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
122    aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
123    aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
124
125    AAudioStreamBuilder *aaudioBuilder = nullptr;
126    AAudioStream *aaudioStream = nullptr;
127
128    memset(&sTimestampData, 0, sizeof(sTimestampData));
129
130    printf("------------ testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) -----------\n",
131           mmapPolicy,
132           getSharingModeText(sharingMode),
133           getPerformanceModeText(performanceMode),
134           getDirectionText(direction));
135
136    AAudio_setMMapPolicy(mmapPolicy);
137
138    // Use an AAudioStreamBuilder to contain requested parameters.
139    result = AAudio_createStreamBuilder(&aaudioBuilder);
140    if (result != AAUDIO_OK) {
141        printf("AAudio_createStreamBuilder returned %s",
142               AAudio_convertResultToText(result));
143        goto finish;
144    }
145
146    // Request stream properties.
147    AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_I16);
148    AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
149    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, performanceMode);
150    AAudioStreamBuilder_setDirection(aaudioBuilder, direction);
151    AAudioStreamBuilder_setDataCallback(aaudioBuilder, timestampDataCallbackProc, &sTimestampData);
152
153    // Create an AAudioStream using the Builder.
154    result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
155    if (result != AAUDIO_OK) {
156        printf("AAudioStreamBuilder_openStream returned %s",
157               AAudio_convertResultToText(result));
158        goto finish;
159    }
160
161    // Check to see what kind of stream we actually got.
162    actualSampleRate = AAudioStream_getSampleRate(aaudioStream);
163    actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
164    actualDataFormat = AAudioStream_getFormat(aaudioStream);
165
166    actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
167    if (actualSharingMode != sharingMode) {
168        printf("did not get expected sharingMode, got %3d, skipping test\n",
169               actualSharingMode);
170        result = AAUDIO_OK;
171        goto finish;
172    }
173    actualPerformanceMode = AAudioStream_getPerformanceMode(aaudioStream);
174    if (actualPerformanceMode != performanceMode) {
175        printf("did not get expected performanceMode, got %3d, skipping test\n",
176               actualPerformanceMode);
177        result = AAUDIO_OK;
178        goto finish;
179    }
180
181    printf("    chans = %3d, rate = %6d format = %d\n",
182           actualChannelCount, actualSampleRate, actualDataFormat);
183    printf("    Is MMAP used? %s\n", AAudioStream_isMMapUsed(aaudioStream)
184                                     ? "yes" : "no");
185
186    // This is the number of frames that are read in one chunk by a DMA controller
187    // or a DSP or a mixer.
188    framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
189    printf("    framesPerBurst = %3d\n", framesPerBurst);
190
191    originalBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream);
192    requestedBufferSize = 4 * framesPerBurst;
193    finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
194
195    printf("    BufferSize: original = %4d, requested = %4d, final = %4d\n",
196           originalBufferSize, requestedBufferSize, finalBufferSize);
197
198    {
199        int64_t position;
200        int64_t nanoseconds;
201        result = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC, &position, &nanoseconds);
202        printf("before start, AAudioStream_getTimestamp() returns %s\n",
203               AAudio_convertResultToText(result));
204    }
205
206    for (int runs = 0; runs < NUM_LOOPS; runs++) {
207        printf("------------------ loop #%d\n", runs);
208
209        int64_t temp = sTimestampData.framesTotal;
210        memset(&sTimestampData, 0, sizeof(sTimestampData));
211        sTimestampData.framesTotal = temp;
212
213        sTimestampData.forceUnderruns = false;
214
215        result = AAudioStream_requestStart(aaudioStream);
216        if (result != AAUDIO_OK) {
217            printf("AAudioStream_requestStart returned %s",
218                   AAudio_convertResultToText(result));
219            goto finish;
220        }
221
222        for (int second = 0; second < NUM_SECONDS; second++) {
223            // Give AAudio callback time to run in the background.
224            sleep(1);
225
226            // Periodically print the progress so we know it hasn't died.
227            printf("framesWritten = %d, XRuns = %d\n",
228                   (int) AAudioStream_getFramesWritten(aaudioStream),
229                   (int) AAudioStream_getXRunCount(aaudioStream)
230            );
231        }
232
233        result = AAudioStream_requestStop(aaudioStream);
234        if (result != AAUDIO_OK) {
235            printf("AAudioStream_requestStop returned %s\n",
236                   AAudio_convertResultToText(result));
237        }
238
239        printf("timestampCount = %d\n", sTimestampData.timestampCount);
240        int printed = 0;
241        for (int i = 0; i < sTimestampData.timestampCount; i++) {
242            TimestampInfo *timestamp = &sTimestampData.timestamps[i];
243            bool posChanged = (timestamp->timestampPosition != (timestamp - 1)->timestampPosition);
244            bool timeChanged = (timestamp->timestampNanos != (timestamp - 1)->timestampNanos);
245            if ((printed < 20) && ((i < 10) || posChanged || timeChanged)) {
246                printf("  %3d : frames %8lld, xferd %8lld", i,
247                       (long long) timestamp->framesTotal,
248                       (long long) timestamp->appPosition);
249                if (timestamp->result != AAUDIO_OK) {
250                    printf(", result = %s\n", AAudio_convertResultToText(timestamp->result));
251                } else {
252                    bool negative = timestamp->timestampPosition < 0;
253                    bool retro = (i > 0 && (timestamp->timestampPosition <
254                                            (timestamp - 1)->timestampPosition));
255                    const char *message = negative ? " <=NEGATIVE!"
256                                                   : (retro ? "  <= RETROGRADE!" : "");
257
258                    double latency = calculateLatencyMillis(timestamp->timestampPosition,
259                                             timestamp->timestampNanos,
260                                             timestamp->appPosition,
261                                             timestamp->appNanoseconds,
262                                             actualSampleRate);
263                    printf(", STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n",
264                           (long long) timestamp->timestampPosition,
265                           (long long) timestamp->timestampNanos,
266                           latency,
267                           message);
268                }
269                printed++;
270            }
271        }
272
273        // Avoid race conditions in AudioFlinger.
274        // There is normally a delay between a real user stopping and restarting a stream.
275        sleep(1);
276    }
277
278finish:
279    if (aaudioStream != nullptr) {
280        AAudioStream_close(aaudioStream);
281    }
282    AAudioStreamBuilder_delete(aaudioBuilder);
283    printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
284
285    return result;
286}
287
288int main(int argc, char **argv) {
289    (void) argc;
290    (void *) argv;
291
292    aaudio_result_t result = AAUDIO_OK;
293
294    // Make printf print immediately so that debug info is not stuck
295    // in a buffer if we hang or crash.
296    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
297
298    printf("Test Timestamps V0.1.3\n");
299
300    // Legacy
301    aaudio_policy_t policy = AAUDIO_POLICY_NEVER;
302    result = testTimeStamps(policy,
303                            AAUDIO_SHARING_MODE_SHARED,
304                            AAUDIO_PERFORMANCE_MODE_NONE,
305                            AAUDIO_DIRECTION_INPUT);
306    result = testTimeStamps(policy,
307                            AAUDIO_SHARING_MODE_SHARED,
308                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
309                            AAUDIO_DIRECTION_INPUT);
310    result = testTimeStamps(policy,
311                            AAUDIO_SHARING_MODE_SHARED,
312                            AAUDIO_PERFORMANCE_MODE_NONE,
313                            AAUDIO_DIRECTION_OUTPUT);
314    result = testTimeStamps(policy,
315                            AAUDIO_SHARING_MODE_SHARED,
316                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
317                            AAUDIO_DIRECTION_OUTPUT);
318
319    // MMAP
320    policy = AAUDIO_POLICY_ALWAYS;
321    result = testTimeStamps(policy,
322                            AAUDIO_SHARING_MODE_EXCLUSIVE,
323                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
324                            AAUDIO_DIRECTION_INPUT);
325    result = testTimeStamps(policy,
326                            AAUDIO_SHARING_MODE_EXCLUSIVE,
327                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
328                            AAUDIO_DIRECTION_OUTPUT);
329    result = testTimeStamps(policy,
330                            AAUDIO_SHARING_MODE_SHARED,
331                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
332                            AAUDIO_DIRECTION_INPUT);
333    result = testTimeStamps(policy,
334                            AAUDIO_SHARING_MODE_SHARED,
335                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
336                            AAUDIO_DIRECTION_OUTPUT);
337
338    return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
339}
340