Ejemplo n.º 1
0
static int
opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
{
  /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html
   * We don't want to deal with JNI here (and we don't have Java on b2g anyways),
   * so we just dlopen the library and get the two symbols we need. */

  int rv;
  void * libmedia;
  size_t (*get_primary_output_frame_count)(void);
  int (*get_output_frame_count)(size_t * frameCount, int streamType);
  uint32_t primary_sampling_rate;
  size_t primary_buffer_size;

  rv = opensl_get_preferred_sample_rate(ctx, &primary_sampling_rate);

  if (rv) {
    return CUBEB_ERROR;
  }

  libmedia = dlopen("libmedia.so", RTLD_LAZY);
  if (!libmedia) {
    return CUBEB_ERROR;
  }

  /* JB variant */
  /* size_t AudioSystem::getPrimaryOutputFrameCount(void) */
  get_primary_output_frame_count =
    dlsym(libmedia, "_ZN7android11AudioSystem26getPrimaryOutputFrameCountEv");
  if (!get_primary_output_frame_count) {
    /* ICS variant */
    /* status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) */
    get_output_frame_count =
      dlsym(libmedia, "_ZN7android11AudioSystem19getOutputFrameCountEPii");
    if (!get_output_frame_count) {
      dlclose(libmedia);
      return CUBEB_ERROR;
    }
  }

  if (get_primary_output_frame_count) {
    primary_buffer_size = get_primary_output_frame_count();
  } else {
    if (get_output_frame_count(&primary_buffer_size, params.stream_type) != 0) {
      return CUBEB_ERROR;
    }
  }

  /* To get a fast track in Android's mixer, we need to be at the native
   * samplerate, which is device dependant. Some devices might be able to
   * resample when playing a fast track, but it's pretty rare. */
  *latency_ms = NBUFS * primary_buffer_size / (primary_sampling_rate / 1000);

  dlclose(libmedia);

  return CUBEB_OK;
}
Ejemplo n.º 2
0
int
opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
  int rv;
  void * libmedia;
  int32_t (*get_output_latency)(uint32_t * latency, int stream_type);
  uint32_t mixer_latency;
  uint32_t samplerate;

  /* The latency returned by AudioFlinger is in ms, so we have to get
   * AudioFlinger's samplerate to convert it to frames. */
  rv = opensl_get_preferred_sample_rate(stm->context, &samplerate);
  if (rv) {
    return CUBEB_ERROR;
  }

  libmedia = dlopen("libmedia.so", RTLD_LAZY);
  if (!libmedia) {
    return CUBEB_ERROR;
  }

  /* Get the latency, in ms, from AudioFlinger */
  /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
   *                                        audio_stream_type_t streamType) */
  /* First, try the most recent signature. */
    get_output_latency =
      dlsym(libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
  if (!get_output_latency) {
    /* in case of failure, try the legacy version. */
    /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
     *                                        int streamType) */
    get_output_latency =
      dlsym(libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
    if (!get_output_latency) {
      dlclose(libmedia);
      return CUBEB_ERROR;
    }
  }

  /* audio_stream_type_t is an int, so this is okay. */
  rv = get_output_latency(&mixer_latency, stm->stream_type);

  if (rv) {
    dlclose(libmedia);
    return CUBEB_ERROR;
  }

  *latency = NBUFS * stm->queuebuf_len / stm->framesize + // OpenSL latency
             mixer_latency * samplerate / 1000; // AudioFlinger latency

  dlclose(libmedia);

  return CUBEB_OK;
}
Ejemplo n.º 3
0
static int
opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
                  cubeb_stream_params stream_params, unsigned int latency,
                  cubeb_data_callback data_callback, cubeb_state_callback state_callback,
                  void * user_ptr)
{
  cubeb_stream * stm;

  assert(ctx);

  *stream = NULL;

  if (stream_params.channels < 1 || stream_params.channels > 32 ||
      latency < 1 || latency > 2000) {
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  SLDataFormat_PCM format;

  format.formatType = SL_DATAFORMAT_PCM;
  format.numChannels = stream_params.channels;
  // samplesPerSec is in milliHertz
  format.samplesPerSec = stream_params.rate * 1000;
  format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
  format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
  format.channelMask = stream_params.channels == 1 ?
                       SL_SPEAKER_FRONT_CENTER :
                       SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;

  switch (stream_params.format) {
  case CUBEB_SAMPLE_S16LE:
    format.endianness = SL_BYTEORDER_LITTLEENDIAN;
    break;
  case CUBEB_SAMPLE_S16BE:
    format.endianness = SL_BYTEORDER_BIGENDIAN;
    break;
  default:
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  stm = calloc(1, sizeof(*stm));
  assert(stm);

  stm->context = ctx;
  stm->data_callback = data_callback;
  stm->state_callback = state_callback;
  stm->user_ptr = user_ptr;

  stm->inputrate = stream_params.rate;
  stm->latency = latency;
  stm->stream_type = stream_params.stream_type;
  stm->framesize = stream_params.channels * sizeof(int16_t);

  int r = pthread_mutex_init(&stm->mutex, NULL);
  assert(r == 0);

  SLDataLocator_BufferQueue loc_bufq;
  loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
  loc_bufq.numBuffers = NBUFS;
  SLDataSource source;
  source.pLocator = &loc_bufq;
  source.pFormat = &format;

  SLDataLocator_OutputMix loc_outmix;
  loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
  loc_outmix.outputMix = ctx->outmixObj;
  SLDataSink sink;
  sink.pLocator = &loc_outmix;
  sink.pFormat = NULL;

#if defined(__ANDROID__)
  const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE,
                               ctx->SL_IID_VOLUME,
                               ctx->SL_IID_ANDROIDCONFIGURATION};
  const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
#else
  const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_VOLUME};
  const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
#endif
  assert(NELEMS(ids) == NELEMS(req));
  SLresult res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj,
                                                &source, &sink, NELEMS(ids), ids, req);

  uint32_t preferred_sampling_rate = stm->inputrate;
  // Sample rate not supported? Try again with primary sample rate!
  if (res == SL_RESULT_CONTENT_UNSUPPORTED) {
    if (opensl_get_preferred_sample_rate(ctx, &preferred_sampling_rate)) {
      opensl_stream_destroy(stm);
      return CUBEB_ERROR;
    }

    format.samplesPerSec = preferred_sampling_rate * 1000;
    res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj,
                                         &source, &sink, NELEMS(ids), ids, req);
  }

  if (res != SL_RESULT_SUCCESS) {
    opensl_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  stm->outputrate = preferred_sampling_rate;
  stm->bytespersec = preferred_sampling_rate * stm->framesize;
  stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS);
  // round up to the next multiple of stm->framesize, if needed.
  if (stm->queuebuf_len % stm->framesize) {
    stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize);
  }

  stm->resampler = cubeb_resampler_create(stm, stream_params,
                                          preferred_sampling_rate,
                                          data_callback,
                                          stm->queuebuf_len / stm->framesize,
                                          user_ptr,
                                          CUBEB_RESAMPLER_QUALITY_DEFAULT);

  if (!stm->resampler) {
    opensl_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  int i;
  for (i = 0; i < NBUFS; i++) {
    stm->queuebuf[i] = malloc(stm->queuebuf_len);
    assert(stm->queuebuf[i]);
  }

#if defined(__ANDROID__)
  SLuint32 stream_type = convert_stream_type_to_sl_stream(stream_params.stream_type);
  if (stream_type != 0xFFFFFFFF) {
    SLAndroidConfigurationItf playerConfig;
    res = (*stm->playerObj)->GetInterface(stm->playerObj,
          ctx->SL_IID_ANDROIDCONFIGURATION, &playerConfig);
    res = (*playerConfig)->SetConfiguration(playerConfig,
          SL_ANDROID_KEY_STREAM_TYPE, &stream_type, sizeof(SLint32));
    if (res != SL_RESULT_SUCCESS) {
      opensl_stream_destroy(stm);
      return CUBEB_ERROR;
    }
  }
#endif

  res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
  if (res != SL_RESULT_SUCCESS) {
    opensl_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_PLAY, &stm->play);
  if (res != SL_RESULT_SUCCESS) {
    opensl_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_BUFFERQUEUE,
                                    &stm->bufq);
  if (res != SL_RESULT_SUCCESS) {
    opensl_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_VOLUME,
                                        &stm->volume);

  if (res != SL_RESULT_SUCCESS) {
    opensl_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  res = (*stm->play)->RegisterCallback(stm->play, play_callback, stm);
  if (res != SL_RESULT_SUCCESS) {
    opensl_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  res = (*stm->play)->SetCallbackEventsMask(stm->play, (SLuint32)SL_PLAYEVENT_HEADATMARKER);
  if (res != SL_RESULT_SUCCESS) {
    opensl_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm);
  if (res != SL_RESULT_SUCCESS) {
    opensl_stream_destroy(stm);
    return CUBEB_ERROR;
  }

  *stream = stm;
  return CUBEB_OK;
}