media/libcubeb/src/cubeb_opensl.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /*
     2  * Copyright © 2012 Mozilla Foundation
     3  *
     4  * This program is made available under an ISC-style license.  See the
     5  * accompanying file LICENSE for details.
     6  */
     7 #undef NDEBUG
     8 #include <assert.h>
     9 #include <dlfcn.h>
    10 #include <stdlib.h>
    11 #include <SLES/OpenSLES.h>
    12 #if defined(__ANDROID__)
    13 #include "android/sles_definitions.h"
    14 #include <SLES/OpenSLES_Android.h>
    15 #include <android/log.h>
    16 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL" , ## args)
    17 #endif
    18 #include "cubeb/cubeb.h"
    19 #include "cubeb-internal.h"
    21 static struct cubeb_ops const opensl_ops;
    23 struct cubeb {
    24   struct cubeb_ops const * ops;
    25   void * lib;
    26   void * libmedia;
    27   int32_t (* get_output_latency)(uint32_t * latency, int stream_type);
    28   SLInterfaceID SL_IID_BUFFERQUEUE;
    29   SLInterfaceID SL_IID_PLAY;
    30 #if defined(__ANDROID__)
    31   SLInterfaceID SL_IID_ANDROIDCONFIGURATION;
    32 #endif
    33   SLObjectItf engObj;
    34   SLEngineItf eng;
    35   SLObjectItf outmixObj;
    36 };
    38 #define NELEMS(A) (sizeof(A) / sizeof A[0])
    39 #define NBUFS 4
    40 #define AUDIO_STREAM_TYPE_MUSIC 3
    42 struct cubeb_stream {
    43   cubeb * context;
    44   SLObjectItf playerObj;
    45   SLPlayItf play;
    46   SLBufferQueueItf bufq;
    47   void *queuebuf[NBUFS];
    48   int queuebuf_idx;
    49   long queuebuf_len;
    50   long bytespersec;
    51   long framesize;
    52   int draining;
    53   cubeb_stream_type stream_type;
    55   cubeb_data_callback data_callback;
    56   cubeb_state_callback state_callback;
    57   void * user_ptr;
    58 };
    60 static void
    61 bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
    62 {
    63   cubeb_stream * stm = user_ptr;
    64   SLBufferQueueState state;
    65   (*stm->bufq)->GetState(stm->bufq, &state);
    67   if (stm->draining) {
    68     if (!state.count) {
    69       stm->draining = 0;
    70       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
    71     }
    72     return;
    73   }
    75   if (state.count > 1)
    76     return;
    78   SLuint32 i;
    79   for (i = state.count; i < NBUFS; i++) {
    80     void *buf = stm->queuebuf[stm->queuebuf_idx];
    81     long written = stm->data_callback(stm, stm->user_ptr,
    82                                       buf, stm->queuebuf_len / stm->framesize);
    83     if (written == CUBEB_ERROR) {
    84       (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_STOPPED);
    85       return;
    86     }
    88     if (written) {
    89       (*stm->bufq)->Enqueue(stm->bufq, buf, written * stm->framesize);
    90       stm->queuebuf_idx = (stm->queuebuf_idx + 1) % NBUFS;
    91     } else if (!i) {
    92       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
    93       return;
    94     }
    96     if ((written * stm->framesize) < stm->queuebuf_len) {
    97       stm->draining = 1;
    98       return;
    99     }
   100   }
   101 }
   103 #if defined(__ANDROID__)
   104 static SLuint32
   105 convert_stream_type_to_sl_stream(cubeb_stream_type stream_type)
   106 {
   107   switch(stream_type) {
   108     case CUBEB_STREAM_TYPE_SYSTEM:
   109       return SL_ANDROID_STREAM_SYSTEM;
   110     case CUBEB_STREAM_TYPE_MUSIC:
   111       return SL_ANDROID_STREAM_MEDIA;
   112     case CUBEB_STREAM_TYPE_NOTIFICATION:
   113       return SL_ANDROID_STREAM_NOTIFICATION;
   114     case CUBEB_STREAM_TYPE_ALARM:
   115       return SL_ANDROID_STREAM_ALARM;
   116     case CUBEB_STREAM_TYPE_VOICE_CALL:
   117       return SL_ANDROID_STREAM_VOICE;
   118     case CUBEB_STREAM_TYPE_RING:
   119       return SL_ANDROID_STREAM_RING;
   120     case CUBEB_STREAM_TYPE_ENFORCED_AUDIBLE:
   121     default:
   122       return 0xFFFFFFFF;
   123   }
   124 }
   125 #endif
   127 static void opensl_destroy(cubeb * ctx);
   129 /*static*/ int
   130 opensl_init(cubeb ** context, char const * context_name)
   131 {
   132   cubeb * ctx;
   134   *context = NULL;
   136   ctx = calloc(1, sizeof(*ctx));
   137   assert(ctx);
   139   ctx->ops = &opensl_ops;
   141   ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY);
   142   ctx->libmedia = dlopen("libmedia.so", RTLD_LAZY);
   143   if (!ctx->lib || !ctx->libmedia) {
   144     free(ctx);
   145     return CUBEB_ERROR;
   146   }
   148   /* Get the latency, in ms, from AudioFlinger */
   149   /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
   150    *                                        audio_stream_type_t streamType) */
   151   /* First, try the most recent signature. */
   152   ctx->get_output_latency =
   153     dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
   154   if (!ctx->get_output_latency) {
   155     /* in case of failure, try the legacy version. */
   156     /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
   157      *                                        int streamType) */
   158     ctx->get_output_latency =
   159       dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
   160     if (!ctx->get_output_latency) {
   161       opensl_destroy(ctx);
   162       return CUBEB_ERROR;
   163     }
   164   }
   166   typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
   167                                        SLuint32,
   168                                        const SLEngineOption *,
   169                                        SLuint32,
   170                                        const SLInterfaceID *,
   171                                        const SLboolean *);
   172   slCreateEngine_t f_slCreateEngine =
   173     (slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine");
   174   SLInterfaceID SL_IID_ENGINE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE");
   175   SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
   176   ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
   177 #if defined(__ANDROID__)
   178   ctx->SL_IID_ANDROIDCONFIGURATION = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION");
   179 #endif
   180   ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY");
   181   if (!f_slCreateEngine ||
   182       !SL_IID_ENGINE ||
   183       !SL_IID_OUTPUTMIX ||
   184       !ctx->SL_IID_BUFFERQUEUE ||
   185 #if defined(__ANDROID__)
   186       !ctx->SL_IID_ANDROIDCONFIGURATION ||
   187 #endif
   188       !ctx->SL_IID_PLAY) {
   189     opensl_destroy(ctx);
   190     return CUBEB_ERROR;
   191   }
   193   const SLEngineOption opt[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}};
   195   SLresult res;
   196   res = f_slCreateEngine(&ctx->engObj, 1, opt, 0, NULL, NULL);
   197   if (res != SL_RESULT_SUCCESS) {
   198     opensl_destroy(ctx);
   199     return CUBEB_ERROR;
   200   }
   202   res = (*ctx->engObj)->Realize(ctx->engObj, SL_BOOLEAN_FALSE);
   203   if (res != SL_RESULT_SUCCESS) {
   204     opensl_destroy(ctx);
   205     return CUBEB_ERROR;
   206   }
   208   res = (*ctx->engObj)->GetInterface(ctx->engObj, SL_IID_ENGINE, &ctx->eng);
   209   if (res != SL_RESULT_SUCCESS) {
   210     opensl_destroy(ctx);
   211     return CUBEB_ERROR;
   212   }
   214   const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
   215   const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
   216   res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom);
   217   if (res != SL_RESULT_SUCCESS) {
   218     opensl_destroy(ctx);
   219     return CUBEB_ERROR;
   220   }
   222   res = (*ctx->outmixObj)->Realize(ctx->outmixObj, SL_BOOLEAN_FALSE);
   223   if (res != SL_RESULT_SUCCESS) {
   224     opensl_destroy(ctx);
   225     return CUBEB_ERROR;
   226   }
   228   *context = ctx;
   230   return CUBEB_OK;
   231 }
   233 static char const *
   234 opensl_get_backend_id(cubeb * ctx)
   235 {
   236   return "opensl";
   237 }
   239 static int
   240 opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
   241 {
   242   assert(ctx && max_channels);
   243   /* The android mixer handles up to two channels, see
   244   http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
   245   *max_channels = 2;
   247   return CUBEB_OK;
   248 }
   250 static int
   251 opensl_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
   252 {
   253   /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html
   254    * We don't want to deal with JNI here (and we don't have Java on b2g anyways),
   255    * so we just dlopen the library and get the two symbols we need. */
   256   int rv;
   257   void * libmedia;
   258   uint32_t (*get_primary_output_samplingrate)();
   259   uint32_t (*get_output_samplingrate)(int * samplingRate, int streamType);
   260   uint32_t primary_sampling_rate;
   262   libmedia = dlopen("libmedia.so", RTLD_LAZY);
   263   if (!libmedia) {
   264     return CUBEB_ERROR;
   265   }
   267   /* uint32_t AudioSystem::getPrimaryOutputSamplingRate(void) */
   268   get_primary_output_samplingrate =
   269     dlsym(libmedia, "_ZN7android11AudioSystem28getPrimaryOutputSamplingRateEv");
   270   if (!get_primary_output_samplingrate) {
   271     /* fallback to
   272      * status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType)
   273      * if we cannot find getPrimaryOutputSamplingRate. */
   274     get_output_samplingrate =
   275       dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPj19audio_stream_type_t");
   276     if (!get_output_samplingrate) {
   277       /* Another signature exists, with a int instead of an audio_stream_type_t */
   278       get_output_samplingrate =
   279         dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPii");
   280       if (!get_output_samplingrate) {
   281         dlclose(libmedia);
   282         return CUBEB_ERROR;
   283       }
   284     }
   285   }
   287   if (get_primary_output_samplingrate) {
   288     *rate = get_primary_output_samplingrate();
   289   } else {
   290     /* We don't really know about the type, here, so we just pass music. */
   291     rv = get_output_samplingrate((int *) rate, AUDIO_STREAM_TYPE_MUSIC);
   292     if (rv) {
   293       dlclose(libmedia);
   294       return CUBEB_ERROR;
   295     }
   296   }
   298   dlclose(libmedia);
   300   /* Depending on which method we called above, we can get a zero back, yet have
   301    * a non-error return value, especially if the audio system is not
   302    * ready/shutting down (i.e. when we can't get our hand on the AudioFlinger
   303    * thread). */
   304   if (*rate == 0) {
   305     return CUBEB_ERROR;
   306   }
   308   return CUBEB_OK;
   309 }
   311 static int
   312 opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
   313 {
   314   /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html
   315    * We don't want to deal with JNI here (and we don't have Java on b2g anyways),
   316    * so we just dlopen the library and get the two symbols we need. */
   318   int rv;
   319   void * libmedia;
   320   size_t (*get_primary_output_frame_count)(void);
   321   int (*get_output_frame_count)(size_t * frameCount, int streamType);
   322   uint32_t primary_sampling_rate;
   323   size_t primary_buffer_size;
   325   rv = opensl_get_preferred_sample_rate(ctx, &primary_sampling_rate);
   327   if (rv) {
   328     return CUBEB_ERROR;
   329   }
   331   libmedia = dlopen("libmedia.so", RTLD_LAZY);
   332   if (!libmedia) {
   333     return CUBEB_ERROR;
   334   }
   336   /* JB variant */
   337   /* size_t AudioSystem::getPrimaryOutputFrameCount(void) */
   338   get_primary_output_frame_count =
   339     dlsym(libmedia, "_ZN7android11AudioSystem26getPrimaryOutputFrameCountEv");
   340   if (!get_primary_output_frame_count) {
   341     /* ICS variant */
   342     /* status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) */
   343     get_output_frame_count =
   344       dlsym(libmedia, "_ZN7android11AudioSystem19getOutputFrameCountEPii");
   345     if (!get_output_frame_count) {
   346       dlclose(libmedia);
   347       return CUBEB_ERROR;
   348     }
   349   }
   351   if (get_primary_output_frame_count) {
   352     primary_buffer_size = get_primary_output_frame_count();
   353   } else {
   354     if (get_output_frame_count(&primary_buffer_size, params.stream_type) != 0) {
   355       return CUBEB_ERROR;
   356     }
   357   }
   359   /* To get a fast track in Android's mixer, we need to be at the native
   360    * samplerate, which is device dependant. Some devices might be able to
   361    * resample when playing a fast track, but it's pretty rare. */
   362   *latency_ms = NBUFS * primary_buffer_size / (primary_sampling_rate / 1000);
   364   dlclose(libmedia);
   366   return CUBEB_OK;
   367 }
   369 static void
   370 opensl_destroy(cubeb * ctx)
   371 {
   372   if (ctx->outmixObj)
   373     (*ctx->outmixObj)->Destroy(ctx->outmixObj);
   374   if (ctx->engObj)
   375     (*ctx->engObj)->Destroy(ctx->engObj);
   376   dlclose(ctx->lib);
   377   dlclose(ctx->libmedia);
   378   free(ctx);
   379 }
   381 static void opensl_stream_destroy(cubeb_stream * stm);
   383 static int
   384 opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
   385                   cubeb_stream_params stream_params, unsigned int latency,
   386                   cubeb_data_callback data_callback, cubeb_state_callback state_callback,
   387                   void * user_ptr)
   388 {
   389   cubeb_stream * stm;
   391   assert(ctx);
   393   *stream = NULL;
   395   if (stream_params.rate < 8000 || stream_params.rate > 88200 ||
   396       stream_params.channels < 1 || stream_params.channels > 32 ||
   397       latency < 1 || latency > 2000) {
   398     return CUBEB_ERROR_INVALID_FORMAT;
   399   }
   401   SLDataFormat_PCM format;
   403   format.formatType = SL_DATAFORMAT_PCM;
   404   format.numChannels = stream_params.channels;
   405   // samplesPerSec is in milliHertz
   406   format.samplesPerSec = stream_params.rate * 1000;
   407   format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
   408   format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
   409   format.channelMask = stream_params.channels == 1 ?
   410                        SL_SPEAKER_FRONT_CENTER :
   411                        SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
   413   switch (stream_params.format) {
   414   case CUBEB_SAMPLE_S16LE:
   415     format.endianness = SL_BYTEORDER_LITTLEENDIAN;
   416     break;
   417   case CUBEB_SAMPLE_S16BE:
   418     format.endianness = SL_BYTEORDER_BIGENDIAN;
   419     break;
   420   default:
   421     return CUBEB_ERROR_INVALID_FORMAT;
   422   }
   424   stm = calloc(1, sizeof(*stm));
   425   assert(stm);
   427   stm->context = ctx;
   428   stm->data_callback = data_callback;
   429   stm->state_callback = state_callback;
   430   stm->user_ptr = user_ptr;
   432   stm->stream_type = stream_params.stream_type;
   433   stm->framesize = stream_params.channels * sizeof(int16_t);
   434   stm->bytespersec = stream_params.rate * stm->framesize;
   435   stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS);
   436   // round up to the next multiple of stm->framesize, if needed.
   437   if (stm->queuebuf_len % stm->framesize) {
   438     stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize);
   439   }
   440   int i;
   441   for (i = 0; i < NBUFS; i++) {
   442     stm->queuebuf[i] = malloc(stm->queuebuf_len);
   443     assert(stm->queuebuf[i]);
   444   }
   446   SLDataLocator_BufferQueue loc_bufq;
   447   loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
   448   loc_bufq.numBuffers = NBUFS;
   449   SLDataSource source;
   450   source.pLocator = &loc_bufq;
   451   source.pFormat = &format;
   453   SLDataLocator_OutputMix loc_outmix;
   454   loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
   455   loc_outmix.outputMix = ctx->outmixObj;
   456   SLDataSink sink;
   457   sink.pLocator = &loc_outmix;
   458   sink.pFormat = NULL;
   460 #if defined(__ANDROID__)
   461   const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_ANDROIDCONFIGURATION};
   462   const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
   463 #else
   464   const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE};
   465   const SLboolean req[] = {SL_BOOLEAN_TRUE};
   466 #endif
   467   assert(NELEMS(ids) == NELEMS(req));
   468   SLresult res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj,
   469                                                 &source, &sink, NELEMS(ids), ids, req);
   470   if (res != SL_RESULT_SUCCESS) {
   471     opensl_stream_destroy(stm);
   472     return CUBEB_ERROR;
   473   }
   475 #if defined(__ANDROID__)
   476   SLuint32 stream_type = convert_stream_type_to_sl_stream(stream_params.stream_type);
   477   if (stream_type != 0xFFFFFFFF) {
   478     SLAndroidConfigurationItf playerConfig;
   479     res = (*stm->playerObj)->GetInterface(stm->playerObj,
   480           ctx->SL_IID_ANDROIDCONFIGURATION, &playerConfig);
   481     res = (*playerConfig)->SetConfiguration(playerConfig,
   482           SL_ANDROID_KEY_STREAM_TYPE, &stream_type, sizeof(SLint32));
   483     if (res != SL_RESULT_SUCCESS) {
   484       opensl_stream_destroy(stm);
   485       return CUBEB_ERROR;
   486     }
   487   }
   488 #endif
   490   res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
   491   if (res != SL_RESULT_SUCCESS) {
   492     opensl_stream_destroy(stm);
   493     return CUBEB_ERROR;
   494   }
   496   res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_PLAY, &stm->play);
   497   if (res != SL_RESULT_SUCCESS) {
   498     opensl_stream_destroy(stm);
   499     return CUBEB_ERROR;
   500   }
   502   res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_BUFFERQUEUE,
   503                                     &stm->bufq);
   504   if (res != SL_RESULT_SUCCESS) {
   505     opensl_stream_destroy(stm);
   506     return CUBEB_ERROR;
   507   }
   509   res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm);
   510   if (res != SL_RESULT_SUCCESS) {
   511     opensl_stream_destroy(stm);
   512     return CUBEB_ERROR;
   513   }
   515   *stream = stm;
   516   return CUBEB_OK;
   517 }
   519 static void
   520 opensl_stream_destroy(cubeb_stream * stm)
   521 {
   522   if (stm->playerObj)
   523     (*stm->playerObj)->Destroy(stm->playerObj);
   524   int i;
   525   for (i = 0; i < NBUFS; i++) {
   526     free(stm->queuebuf[i]);
   527   }
   529   free(stm);
   530 }
   532 static int
   533 opensl_stream_start(cubeb_stream * stm)
   534 {
   535   /* To refill the queues before starting playback in order to avoid racing
   536   * with refills started by SetPlayState on OpenSLES ndk threads. */
   537   bufferqueue_callback(NULL, stm);
   538   SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PLAYING);
   539   if (res != SL_RESULT_SUCCESS)
   540     return CUBEB_ERROR;
   541   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
   542   return CUBEB_OK;
   543 }
   545 static int
   546 opensl_stream_stop(cubeb_stream * stm)
   547 {
   548   SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED);
   549   if (res != SL_RESULT_SUCCESS)
   550     return CUBEB_ERROR;
   551   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
   552   return CUBEB_OK;
   553 }
   555 static int
   556 opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
   557 {
   558   SLmillisecond msec;
   559   uint64_t samplerate;
   560   SLresult res;
   561   int rv;
   562   uint32_t mixer_latency;
   564   res = (*stm->play)->GetPosition(stm->play, &msec);
   565   if (res != SL_RESULT_SUCCESS)
   566     return CUBEB_ERROR;
   568   samplerate = stm->bytespersec / stm->framesize;
   570   rv = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
   571   if (rv) {
   572     return CUBEB_ERROR;
   573   }
   575   if (msec > mixer_latency) {
   576     *position = samplerate * (msec - mixer_latency) / 1000;
   577   } else {
   578     *position = 0;
   579   }
   580   return CUBEB_OK;
   581 }
   583 int
   584 opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
   585 {
   586   int rv;
   587   uint32_t mixer_latency;
   588   uint32_t samplerate;
   590   /* The latency returned by AudioFlinger is in ms, so we have to get
   591    * AudioFlinger's samplerate to convert it to frames. */
   592   rv = opensl_get_preferred_sample_rate(stm->context, &samplerate);
   593   if (rv) {
   594     return CUBEB_ERROR;
   595   }
   597   /* audio_stream_type_t is an int, so this is okay. */
   598   rv = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
   599   if (rv) {
   600     return CUBEB_ERROR;
   601   }
   603   *latency = NBUFS * stm->queuebuf_len / stm->framesize + // OpenSL latency
   604              mixer_latency * samplerate / 1000; // AudioFlinger latency
   606   return CUBEB_OK;
   607 }
   609 static struct cubeb_ops const opensl_ops = {
   610   .init = opensl_init,
   611   .get_backend_id = opensl_get_backend_id,
   612   .get_max_channel_count = opensl_get_max_channel_count,
   613   .get_min_latency = opensl_get_min_latency,
   614   .get_preferred_sample_rate = opensl_get_preferred_sample_rate,
   615   .destroy = opensl_destroy,
   616   .stream_init = opensl_stream_init,
   617   .stream_destroy = opensl_stream_destroy,
   618   .stream_start = opensl_stream_start,
   619   .stream_stop = opensl_stream_stop,
   620   .stream_get_position = opensl_stream_get_position,
   621   .stream_get_latency = opensl_stream_get_latency
   622 };

mercurial