Bug 940707 - Get more accurate latency numbers when using OpenSL. r=sotaro,kinetik

This dlopen libmedia.so, fetches some symbols and use that to determine the
output latency of the stream.

This also includes some fixes to get the minimal latency on Android 4.0.4, and
makes the latency a bit better.
This commit is contained in:
Paul Adenot 2013-11-28 17:32:50 +01:00
parent 60083d6518
commit 2c7b0bb860

View File

@ -33,6 +33,7 @@ struct cubeb {
#define NELEMS(A) (sizeof(A) / sizeof A[0])
#define NBUFS 4
#define AUDIO_STREAM_TYPE_MUSIC 3
struct cubeb_stream {
cubeb * context;
@ -45,6 +46,7 @@ struct cubeb_stream {
long bytespersec;
long framesize;
int draining;
cubeb_stream_type stream_type;
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
@ -224,18 +226,16 @@ opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
}
static int
opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
opensl_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
/* 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;
uint32_t (*get_primary_output_samplingrate)(void);
size_t (*get_primary_output_frame_count)(void);
uint32_t (*get_primary_output_samplingrate)();
uint32_t (*get_output_samplingrate)(int * samplingRate, int streamType);
uint32_t primary_sampling_rate;
size_t primary_buffer_size;
libmedia = dlopen("libmedia.so", RTLD_LAZY);
if (!libmedia) {
@ -246,29 +246,31 @@ opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
get_primary_output_samplingrate =
dlsym(libmedia, "_ZN7android11AudioSystem28getPrimaryOutputSamplingRateEv");
if (!get_primary_output_samplingrate) {
dlclose(libmedia);
return CUBEB_ERROR;
/* fallback to
* status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType)
* if we cannot find getPrimaryOutputSamplingRate. */
get_output_samplingrate =
dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPj19audio_stream_type_t");
if (!get_output_samplingrate) {
/* Another signature exists, with a int instead of an audio_stream_type_t */
get_output_samplingrate =
dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPii");
if (!get_output_samplingrate) {
dlclose(libmedia);
return CUBEB_ERROR;
}
}
}
/* size_t AudioSystem::getPrimaryOutputFrameCount(void) */
get_primary_output_frame_count =
dlsym(libmedia, "_ZN7android11AudioSystem26getPrimaryOutputFrameCountEv");
if (!get_primary_output_frame_count) {
dlclose(libmedia);
return CUBEB_ERROR;
}
primary_sampling_rate = get_primary_output_samplingrate();
primary_buffer_size = get_primary_output_frame_count();
/* 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. */
if (primary_sampling_rate != params.rate) {
/* If we don't know what to use, let's say 4 * 20ms buffers will do. */
*latency_ms = NBUFS * 20;
if (get_primary_output_samplingrate) {
*rate = get_primary_output_samplingrate();
} else {
*latency_ms = NBUFS * primary_buffer_size / (primary_sampling_rate / 1000);
/* We don't really know about the type, here, so we just pass music. */
rv = get_output_samplingrate((int *) rate, AUDIO_STREAM_TYPE_MUSIC);
if (rv) {
dlclose(libmedia);
return CUBEB_ERROR;
}
}
dlclose(libmedia);
@ -277,37 +279,63 @@ opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
}
static int
opensl_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
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,
* 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;
uint32_t (*get_primary_output_samplingrate)();
size_t (*get_primary_output_frame_count)(void);
int (*get_output_frame_count)(int * 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;
}
/* uint32_t AudioSystem::getPrimaryOutputSamplingRate(void) */
get_primary_output_samplingrate =
dlsym(libmedia, "_ZN7android11AudioSystem28getPrimaryOutputSamplingRateEv");
if (!get_primary_output_samplingrate) {
dlclose(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;
}
}
*rate = get_primary_output_samplingrate();
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;
}
static void
opensl_destroy(cubeb * ctx)
{
@ -370,6 +398,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->stream_type = stream_params.stream_type;
stm->framesize = stream_params.channels * sizeof(int16_t);
stm->bytespersec = stream_params.rate * stm->framesize;
stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS);
@ -504,7 +533,54 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
int
opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
*latency = NBUFS * stm->queuebuf_len;
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;
}