Home | History | Annotate | Download | only in tests
      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 
     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 
     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.
    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 = &timestampData->timestamps[timestampData->timestampCount];
    130         timestamp->result = AAudioStream_getTimestamp(stream,
    131                                                       CLOCK_MONOTONIC,
    132                                                       &timestamp->timestampPosition,
    133                                                       &timestamp->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 
    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 
    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