зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1410456 - get mixer latency from JNI instead of system library. r=jchen,padenot,snorp
MozReview-Commit-ID: 3YciuVu25JO --HG-- extra : rebase_source : e5b3ad2f4b38f4b6454a56434022fce7f2a649cb
This commit is contained in:
Родитель
2b7cbda791
Коммит
967d8be756
|
@ -0,0 +1,34 @@
|
|||
#ifndef _CUBEB_JNI_INSTANCES_H_
|
||||
#define _CUBEB_JNI_INSTANCES_H_
|
||||
|
||||
#include "GeneratedJNIWrappers.h"
|
||||
#include "mozilla/jni/Utils.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 mozilla::jni::GetVM();
|
||||
}
|
||||
|
||||
jobject
|
||||
cubeb_jni_get_context_instance()
|
||||
{
|
||||
auto context = mozilla::java::GeckoAppShell::GetApplicationContext();
|
||||
return context.Forget();
|
||||
}
|
||||
|
||||
#endif //_CUBEB_JNI_INSTANCES_H_
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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
|
||||
|
@ -68,8 +69,6 @@ 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__)
|
||||
|
@ -81,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. */
|
||||
|
@ -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);
|
||||
|
@ -792,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);
|
||||
}
|
||||
|
||||
|
@ -879,8 +865,8 @@ 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. The
|
||||
// default rate expected to supported from all android devices.
|
||||
// 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;
|
||||
|
@ -1043,7 +1029,7 @@ 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 &&
|
||||
preferred_sampling_rate != DEFAULT_SAMPLE_RATE) {
|
||||
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,
|
||||
|
@ -1463,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)
|
||||
|
@ -1483,12 +1470,8 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
|||
stm->lastPosition = msec;
|
||||
}
|
||||
|
||||
samplerate = stm->user_output_rate;
|
||||
|
||||
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->user_output_rate / stm->output_configured_rate;
|
||||
|
|
|
@ -77,6 +77,7 @@ if CONFIG['OS_TARGET'] == 'WINNT':
|
|||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
SOURCES += ['cubeb_opensl.c']
|
||||
SOURCES += ['cubeb_resampler.cpp']
|
||||
SOURCES += ['cubeb-jni.cpp']
|
||||
DEFINES['USE_OPENSL'] = True
|
||||
SOURCES += [
|
||||
'cubeb_audiotrack.c',
|
||||
|
|
Загрузка…
Ссылка в новой задаче