aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--src/cubeb-jni-instances.h30
-rw-r--r--src/cubeb-jni.cpp88
-rw-r--r--src/cubeb-jni.h10
-rw-r--r--src/cubeb_opensl.c239
5 files changed, 163 insertions, 207 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4fac1b8..850081c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -181,7 +181,8 @@ endif()
check_include_files(SLES/OpenSLES.h USE_OPENSL)
if(USE_OPENSL)
target_sources(cubeb PRIVATE
- src/cubeb_opensl.c)
+ src/cubeb_opensl.c
+ src/cubeb-jni.cpp)
target_compile_definitions(cubeb PRIVATE USE_OPENSL)
target_link_libraries(cubeb PRIVATE OpenSLES)
endif()
diff --git a/src/cubeb-jni-instances.h b/src/cubeb-jni-instances.h
new file mode 100644
index 0000000..9048629
--- /dev/null
+++ b/src/cubeb-jni-instances.h
@@ -0,0 +1,30 @@
+#ifndef _CUBEB_JNI_INSTANCES_H_
+#define _CUBEB_JNI_INSTANCES_H_
+
+/*
+ * The methods in this file offer a way to pass in the required
+ * JNI instances in the cubeb library. By default they return NULL.
+ * In this case part of the cubeb API that depends on JNI
+ * will return CUBEB_ERROR_NOT_SUPPORTED. Currently only one
+ * method depends on that:
+ *
+ * cubeb_stream_get_position()
+ *
+ * Users that want to use that cubeb API method must "override"
+ * the methods bellow to return a valid instance of JavaVM
+ * and application's Context object.
+ * */
+
+JavaVM *
+cubeb_jni_get_java_vm()
+{
+ return nullptr;
+}
+
+jobject
+cubeb_jni_get_context_instance()
+{
+ return nullptr;
+}
+
+#endif //_CUBEB_JNI_INSTANCES_H_
diff --git a/src/cubeb-jni.cpp b/src/cubeb-jni.cpp
new file mode 100644
index 0000000..3eba97d
--- /dev/null
+++ b/src/cubeb-jni.cpp
@@ -0,0 +1,88 @@
+#include "jni.h"
+#include <assert.h>
+#include "cubeb-jni-instances.h"
+
+#define AUDIO_STREAM_TYPE_MUSIC 3
+
+JNIEnv *
+cubeb_jni_get_env_for_thread(JavaVM * java_vm)
+{
+ JNIEnv * env = nullptr;
+ if (!java_vm->AttachCurrentThread(&env, nullptr)) {
+ assert(env);
+ return env;
+ }
+
+ assert(false && "Failed to get JNIEnv for thread");
+ return nullptr; // unreachable
+}
+
+struct cubeb_jni {
+ JavaVM * s_java_vm = nullptr;
+ jobject s_audio_manager_obj = nullptr;
+ jclass s_audio_manager_class = nullptr;
+ jmethodID s_get_output_latency_id = nullptr;
+};
+
+extern "C"
+cubeb_jni *
+cubeb_jni_init()
+{
+ JavaVM * javaVM = cubeb_jni_get_java_vm();
+ jobject ctx_obj = cubeb_jni_get_context_instance();
+
+ if (!javaVM || !ctx_obj) {
+ return nullptr;
+ }
+
+ JNIEnv * jni_env = cubeb_jni_get_env_for_thread(javaVM);
+ assert(jni_env);
+
+ cubeb_jni * cubeb_jni_ptr = new cubeb_jni;
+ assert(cubeb_jni_ptr);
+
+ cubeb_jni_ptr->s_java_vm = javaVM;
+
+ // Find the audio manager object and make it global to call it from another method
+ jclass context_class = jni_env->FindClass("android/content/Context");
+ jfieldID audio_service_field = jni_env->GetStaticFieldID(context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
+ jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class, audio_service_field);
+ jmethodID get_system_service_id = jni_env->GetMethodID(context_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+ jobject audio_manager_obj = jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr);
+ cubeb_jni_ptr->s_audio_manager_obj = reinterpret_cast<jobject>(jni_env->NewGlobalRef(audio_manager_obj));
+
+ // Make the audio manager class a global reference in order to preserve method id
+ jclass audio_manager_class = jni_env->FindClass("android/media/AudioManager");
+ cubeb_jni_ptr->s_audio_manager_class = reinterpret_cast<jclass>(jni_env->NewGlobalRef(audio_manager_class));
+ cubeb_jni_ptr->s_get_output_latency_id = jni_env->GetMethodID (audio_manager_class, "getOutputLatency", "(I)I");
+
+ jni_env->DeleteLocalRef(ctx_obj);
+ jni_env->DeleteLocalRef(context_class);
+ jni_env->DeleteLocalRef(jstr);
+ jni_env->DeleteLocalRef(audio_manager_obj);
+ jni_env->DeleteLocalRef(audio_manager_class);
+
+ return cubeb_jni_ptr;
+}
+
+extern "C"
+int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
+{
+ assert(cubeb_jni_ptr);
+ JNIEnv * jni_env = cubeb_jni_get_env_for_thread(cubeb_jni_ptr->s_java_vm);
+ return jni_env->CallIntMethod(cubeb_jni_ptr->s_audio_manager_obj, cubeb_jni_ptr->s_get_output_latency_id, AUDIO_STREAM_TYPE_MUSIC); //param: AudioManager.STREAM_MUSIC
+}
+
+extern "C"
+void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr)
+{
+ assert(cubeb_jni_ptr);
+
+ JNIEnv * jni_env = cubeb_jni_get_env_for_thread(cubeb_jni_ptr->s_java_vm);
+ assert(jni_env);
+
+ jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_obj);
+ jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_class);
+
+ delete cubeb_jni_ptr;
+}
diff --git a/src/cubeb-jni.h b/src/cubeb-jni.h
new file mode 100644
index 0000000..8c7ddb6
--- /dev/null
+++ b/src/cubeb-jni.h
@@ -0,0 +1,10 @@
+#ifndef _CUBEB_JNI_H_
+#define _CUBEB_JNI_H_
+
+typedef struct cubeb_jni cubeb_jni;
+
+cubeb_jni * cubeb_jni_init();
+int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr);
+void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr);
+
+#endif // _CUBEB_JNI_H_
diff --git a/src/cubeb_opensl.c b/src/cubeb_opensl.c
index 8e9d4c7..2963fca 100644
--- a/src/cubeb_opensl.c
+++ b/src/cubeb_opensl.c
@@ -26,6 +26,7 @@
#include "cubeb_resampler.h"
#include "cubeb-sles.h"
#include "cubeb_array_queue.h"
+#include "cubeb-jni.h"
#if defined(__ANDROID__)
#ifdef LOG
@@ -61,14 +62,13 @@
#endif
#define DEFAULT_SAMPLE_RATE 48000
+#define DEFAULT_NUM_OF_FRAMES 480
static struct cubeb_ops const opensl_ops;
struct cubeb {
struct cubeb_ops const * ops;
void * lib;
- void * libmedia;
- int32_t (* get_output_latency)(uint32_t * latency, int stream_type);
SLInterfaceID SL_IID_BUFFERQUEUE;
SLInterfaceID SL_IID_PLAY;
#if defined(__ANDROID__)
@@ -80,11 +80,11 @@ struct cubeb {
SLObjectItf engObj;
SLEngineItf eng;
SLObjectItf outmixObj;
+ cubeb_jni * jni_obj;
};
#define NELEMS(A) (sizeof(A) / sizeof A[0])
#define NBUFS 4
-#define AUDIO_STREAM_TYPE_MUSIC 3
struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */
@@ -153,7 +153,7 @@ struct cubeb_stream {
cubeb_state_callback state_callback;
cubeb_resampler * resampler;
- unsigned int inputrate;
+ unsigned int user_output_rate;
unsigned int output_configured_rate;
unsigned int latency_frames;
int64_t lastPosition;
@@ -236,7 +236,6 @@ static void
play_callback(SLPlayItf caller, void * user_ptr, SLuint32 event)
{
cubeb_stream * stm = user_ptr;
- int draining;
assert(stm);
switch (event) {
case SL_PLAYEVENT_HEADATMARKER:
@@ -668,30 +667,11 @@ opensl_init(cubeb ** context, char const * context_name)
ctx->ops = &opensl_ops;
ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY);
- ctx->libmedia = dlopen("libmedia.so", RTLD_LAZY);
- if (!ctx->lib || !ctx->libmedia) {
+ if (!ctx->lib) {
free(ctx);
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. */
- ctx->get_output_latency =
- dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
- if (!ctx->get_output_latency) {
- /* in case of failure, try the legacy version. */
- /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
- * int streamType) */
- ctx->get_output_latency =
- dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
- if (!ctx->get_output_latency) {
- opensl_destroy(ctx);
- return CUBEB_ERROR;
- }
- }
-
typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
SLuint32,
const SLEngineOption *,
@@ -761,6 +741,11 @@ opensl_init(cubeb ** context, char const * context_name)
return CUBEB_ERROR;
}
+ ctx->jni_obj = cubeb_jni_init();
+ if (!ctx->jni_obj) {
+ LOG("Warning: jni is not initialized, cubeb_stream_get_position() is not supported");
+ }
+
*context = ctx;
LOG("Cubeb init (%p) success", ctx);
@@ -784,124 +769,6 @@ opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
return CUBEB_OK;
}
-static int
-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 r;
- void * libmedia;
- uint32_t (*get_primary_output_samplingrate)();
- uint32_t (*get_output_samplingrate)(int * samplingRate, int streamType);
-
- 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) {
- /* 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;
- }
- }
- }
-
- if (get_primary_output_samplingrate) {
- *rate = get_primary_output_samplingrate();
- } else {
- /* We don't really know about the type, here, so we just pass music. */
- r = get_output_samplingrate((int *) rate, AUDIO_STREAM_TYPE_MUSIC);
- if (r) {
- dlclose(libmedia);
- return CUBEB_ERROR;
- }
- }
-
- dlclose(libmedia);
-
- /* Depending on which method we called above, we can get a zero back, yet have
- * a non-error return value, especially if the audio system is not
- * ready/shutting down (i.e. when we can't get our hand on the AudioFlinger
- * thread). */
- if (*rate == 0) {
- return CUBEB_ERROR;
- }
-
- return CUBEB_OK;
-}
-
-static int
-opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
-{
- /* 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 r;
- 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;
-
- r = opensl_get_preferred_sample_rate(ctx, &primary_sampling_rate);
-
- if (r) {
- 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, AUDIO_STREAM_TYPE_MUSIC) != 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_frames = primary_buffer_size;
-
- dlclose(libmedia);
-
- return CUBEB_OK;
-}
-
static void
opensl_destroy(cubeb * ctx)
{
@@ -910,7 +777,8 @@ opensl_destroy(cubeb * ctx)
if (ctx->engObj)
cubeb_destroy_sles_engine(&ctx->engObj);
dlclose(ctx->lib);
- dlclose(ctx->libmedia);
+ if (ctx->jni_obj)
+ cubeb_jni_destroy(ctx->jni_obj);
free(ctx);
}
@@ -997,13 +865,9 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
// api for input device this is a safe choice.
stm->input_device_rate = stm->output_configured_rate;
} else {
- // The output preferred rate is used for input only scenario. This is
- // the correct rate to use to get a fast track for input only.
- r = opensl_get_preferred_sample_rate(stm->context, &stm->input_device_rate);
- if (r != CUBEB_OK) {
- // If everything else fail use a safe choice for Android.
- stm->input_device_rate = DEFAULT_SAMPLE_RATE;
- }
+ // The output preferred rate is used for an input only scenario.
+ // The default rate expected to be supported from all android devices.
+ stm->input_device_rate = DEFAULT_SAMPLE_RATE;
}
lDataFormat.samplesPerSec = stm->input_device_rate * 1000;
res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
@@ -1114,7 +978,7 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
assert(stm);
assert(params);
- stm->inputrate = params->rate;
+ stm->user_output_rate = params->rate;
stm->framesize = params->channels * sizeof(int16_t);
stm->lastPosition = -1;
stm->lastPositionTimeStamp = 0;
@@ -1151,20 +1015,7 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
#endif
assert(NELEMS(ids) == NELEMS(req));
- unsigned int latency_frames = stm->latency_frames;
- uint32_t preferred_sampling_rate = stm->inputrate;
-#if defined(__ANDROID__)
- if (get_android_version() >= ANDROID_VERSION_MARSHMALLOW) {
- // Reset preferred samping rate to trigger fallback to native sampling rate.
- preferred_sampling_rate = 0;
- if (opensl_get_min_latency(stm->context, *params, &latency_frames) != CUBEB_OK) {
- // Default to AudioFlinger's advertised fast track latency of 10ms.
- latency_frames = 440;
- }
- stm->latency_frames = latency_frames;
- }
-#endif
-
+ uint32_t preferred_sampling_rate = stm->user_output_rate;
SLresult res = SL_RESULT_CONTENT_UNSUPPORTED;
if (preferred_sampling_rate) {
res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
@@ -1177,12 +1028,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
}
// Sample rate not supported? Try again with primary sample rate!
- if (res == SL_RESULT_CONTENT_UNSUPPORTED) {
- if (opensl_get_preferred_sample_rate(stm->context, &preferred_sampling_rate)) {
- // If fail default is used
- preferred_sampling_rate = DEFAULT_SAMPLE_RATE;
- }
-
+ if (res == SL_RESULT_CONTENT_UNSUPPORTED &&
+ preferred_sampling_rate != DEFAULT_SAMPLE_RATE) {
+ preferred_sampling_rate = DEFAULT_SAMPLE_RATE;
format.samplesPerSec = preferred_sampling_rate * 1000;
res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
&stm->playerObj,
@@ -1200,7 +1048,7 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
stm->output_configured_rate = preferred_sampling_rate;
stm->bytespersec = stm->output_configured_rate * stm->framesize;
- stm->queuebuf_len = stm->framesize * latency_frames;
+ stm->queuebuf_len = stm->framesize * stm->latency_frames;
// Calculate the capacity of input array
stm->queuebuf_capacity = NBUFS;
@@ -1337,7 +1185,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
- stm->latency_frames = latency_frames;
+ stm->latency_frames = latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
stm->input_enabled = (input_stream_params) ? 1 : 0;
stm->output_enabled = (output_stream_params) ? 1 : 0;
stm->shutdown = 1;
@@ -1601,11 +1449,12 @@ static int
opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
SLmillisecond msec;
- uint64_t samplerate;
- SLresult res;
- int r;
- uint32_t mixer_latency;
uint32_t compensation_msec = 0;
+ SLresult res;
+
+ if (!stm->context->jni_obj) {
+ return CUBEB_ERROR_NOT_SUPPORTED;
+ }
res = (*stm->play)->GetPosition(stm->play, &msec);
if (res != SL_RESULT_SUCCESS)
@@ -1621,15 +1470,11 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
stm->lastPosition = msec;
}
- samplerate = stm->inputrate;
-
- r = stm->context->get_output_latency(&mixer_latency, AUDIO_STREAM_TYPE_MUSIC);
- if (r) {
- return CUBEB_ERROR;
- }
+ uint64_t samplerate = stm->user_output_rate;
+ uint32_t mixer_latency = cubeb_get_output_latency_from_jni(stm->context->jni_obj);
pthread_mutex_lock(&stm->mutex);
- int64_t maximum_position = stm->written * (int64_t)stm->inputrate / stm->output_configured_rate;
+ int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate / stm->output_configured_rate;
pthread_mutex_unlock(&stm->mutex);
assert(maximum_position >= 0);
@@ -1653,24 +1498,6 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
}
int
-opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
-{
- int r;
- uint32_t mixer_latency; // The latency returned by AudioFlinger is in ms.
-
- /* audio_stream_type_t is an int, so this is okay. */
- r = stm->context->get_output_latency(&mixer_latency, AUDIO_STREAM_TYPE_MUSIC);
- if (r) {
- return CUBEB_ERROR;
- }
-
- *latency = stm->latency_frames + // OpenSL latency
- mixer_latency * stm->inputrate / 1000; // AudioFlinger latency
-
- return CUBEB_OK;
-}
-
-int
opensl_stream_set_volume(cubeb_stream * stm, float volume)
{
SLresult res;
@@ -1705,8 +1532,8 @@ static struct cubeb_ops const opensl_ops = {
.init = opensl_init,
.get_backend_id = opensl_get_backend_id,
.get_max_channel_count = opensl_get_max_channel_count,
- .get_min_latency = opensl_get_min_latency,
- .get_preferred_sample_rate = opensl_get_preferred_sample_rate,
+ .get_min_latency = NULL,
+ .get_preferred_sample_rate = NULL,
.get_preferred_channel_layout = NULL,
.enumerate_devices = NULL,
.device_collection_destroy = NULL,
@@ -1717,7 +1544,7 @@ static struct cubeb_ops const opensl_ops = {
.stream_stop = opensl_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = opensl_stream_get_position,
- .stream_get_latency = opensl_stream_get_latency,
+ .stream_get_latency = NULL,
.stream_set_volume = opensl_stream_set_volume,
.stream_set_panning = NULL,
.stream_get_current_device = NULL,