Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multi-instance support #6

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions libmpv/src/main/cpp/event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@
#include "jni_utils.h"
#include "log.h"

static void sendPropertyUpdateToJava(JNIEnv *env, mpv_event_property *prop) {

static void sendPropertyUpdateToJava(JNIEnv *env, MPVInstance* instance, mpv_event_property *prop) {
jstring jprop = env->NewStringUTF(prop->name);
jstring jvalue = nullptr;
switch (prop->format) {
case MPV_FORMAT_NONE:
env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_eventProperty_S, jprop);
env->CallVoidMethod(instance->javaObject, mpv_MPVLib_eventProperty_S, jprop);
break;
case MPV_FORMAT_FLAG:
env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_eventProperty_Sb, jprop, *(int*)prop->data);
env->CallVoidMethod(instance->javaObject, mpv_MPVLib_eventProperty_Sb, jprop, *(int*)prop->data);
break;
case MPV_FORMAT_INT64:
env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_eventProperty_Sl, jprop, *(int64_t*)prop->data);
env->CallVoidMethod(instance->javaObject, mpv_MPVLib_eventProperty_Sl, jprop, *(int64_t*)prop->data);
break;
case MPV_FORMAT_DOUBLE:
env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_eventProperty_Sd, jprop, *(double*)prop->data);
env->CallVoidMethod(instance->javaObject, mpv_MPVLib_eventProperty_Sd, jprop, *(double*)prop->data);
break;
case MPV_FORMAT_STRING:
jvalue = env->NewStringUTF(*(const char**)prop->data);
env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_eventProperty_SS, jprop, jvalue);
env->CallVoidMethod(instance->javaObject, mpv_MPVLib_eventProperty_SS, jprop, jvalue);
break;
default:
ALOGV("sendPropertyUpdateToJava: Unknown property update format received in callback: %d!", prop->format);
Expand All @@ -36,15 +37,15 @@ static void sendPropertyUpdateToJava(JNIEnv *env, mpv_event_property *prop) {
env->DeleteLocalRef(jvalue);
}

static void sendEventToJava(JNIEnv *env, int event) {
env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_event, event);
static void sendEventToJava(JNIEnv *env, MPVInstance* instance, int event) {
env->CallVoidMethod(instance->javaObject, mpv_MPVLib_event, event);
}

static inline bool invalid_utf8(unsigned char c) {
return c == 0xc0 || c == 0xc1 || c >= 0xf5;
}

static void sendLogMessageToJava(JNIEnv *env, mpv_event_log_message *msg) {
static void sendLogMessageToJava(JNIEnv *env, MPVInstance* instance, mpv_event_log_message *msg) {
// filter the most obvious cases of invalid utf-8
int invalid = 0;
for (int i = 0; msg->text[i]; i++)
Expand All @@ -55,7 +56,7 @@ static void sendLogMessageToJava(JNIEnv *env, mpv_event_log_message *msg) {
jstring jprefix = env->NewStringUTF(msg->prefix);
jstring jtext = env->NewStringUTF(msg->text);

env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_logMessage_SiS,
env->CallVoidMethod(instance->javaObject, mpv_MPVLib_logMessage_SiS,
jprefix, (jint) msg->log_level, jtext);

if (jprefix)
Expand All @@ -65,8 +66,9 @@ static void sendLogMessageToJava(JNIEnv *env, mpv_event_log_message *msg) {
}

void *event_thread(void *arg) {
auto instance = static_cast<MPVInstance*>(arg);
JNIEnv *env = nullptr;
acquire_jni_env(g_vm, &env);
acquire_jni_env(instance->vm, &env);
if (!env)
die("failed to acquire java env");

Expand All @@ -75,9 +77,9 @@ void *event_thread(void *arg) {
mpv_event_property *mp_property;
mpv_event_log_message *msg;

mp_event = mpv_wait_event(g_mpv, -1.0);
mp_event = mpv_wait_event(instance->mpv, -1.0);

if (g_event_thread_request_exit)
if (instance->event_thread_request_exit)
break;

if (mp_event->event_id == MPV_EVENT_NONE)
Expand All @@ -87,20 +89,20 @@ void *event_thread(void *arg) {
case MPV_EVENT_LOG_MESSAGE:
msg = (mpv_event_log_message*)mp_event->data;
ALOGV("[%s:%s] %s", msg->prefix, msg->level, msg->text);
sendLogMessageToJava(env, msg);
sendLogMessageToJava(env, instance, msg);
break;
case MPV_EVENT_PROPERTY_CHANGE:
mp_property = (mpv_event_property*)mp_event->data;
sendPropertyUpdateToJava(env, mp_property);
sendPropertyUpdateToJava(env, instance, mp_property);
break;
default:
ALOGV("event: %s\n", mpv_event_name(mp_event->event_id));
sendEventToJava(env, mp_event->event_id);
sendEventToJava(env, instance, mp_event->event_id);
break;
}
}

g_vm->DetachCurrentThread();
instance->vm->DetachCurrentThread();

return nullptr;
}
13 changes: 10 additions & 3 deletions libmpv/src/main/cpp/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

#include <atomic>

extern JavaVM *g_vm;
extern mpv_handle *g_mpv;
extern std::atomic<bool> g_event_thread_request_exit;
// create a new struct to hold the mpv_handle and other related data
struct MPVInstance {
mpv_handle *mpv;
JavaVM *vm;
pthread_t event_thread_id;
std::atomic<bool> event_thread_request_exit;
jobject javaObject;
bool methods_initialized;
jobject surface;
};
24 changes: 10 additions & 14 deletions libmpv/src/main/cpp/jni_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ jmethodID java_GLSurfaceView_requestRender;
jclass mpv_MPVLib;
jmethodID mpv_MPVLib_eventProperty_S, mpv_MPVLib_eventProperty_Sb, mpv_MPVLib_eventProperty_Sl, mpv_MPVLib_eventProperty_Sd, mpv_MPVLib_eventProperty_SS, mpv_MPVLib_event, mpv_MPVLib_logMessage_SiS;

void init_methods_cache(JNIEnv *env) {
static bool methods_initialized = false;
if (methods_initialized)
return;

bool init_methods_cache(JNIEnv *env, jobject instance) {
#define FIND_CLASS(name) reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(name)))
java_Integer = FIND_CLASS("java/lang/Integer");
java_Integer_init = env->GetMethodID(java_Integer, "<init>", "(I)V");
Expand All @@ -37,15 +33,15 @@ void init_methods_cache(JNIEnv *env) {
java_Boolean_init = env->GetMethodID(java_Boolean, "<init>", "(Z)V");
java_Boolean_booleanValue = env->GetMethodID(java_Boolean, "booleanValue", "()Z");

mpv_MPVLib = FIND_CLASS("dev/jdtech/mpv/MPVLib");
mpv_MPVLib_eventProperty_S = env->GetStaticMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;)V"); // eventProperty(String)
mpv_MPVLib_eventProperty_Sb = env->GetStaticMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;Z)V"); // eventProperty(String, boolean)
mpv_MPVLib_eventProperty_Sl = env->GetStaticMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;J)V"); // eventProperty(String, long)
mpv_MPVLib_eventProperty_Sd = env->GetStaticMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;D)V"); // eventProperty(String, double)
mpv_MPVLib_eventProperty_SS = env->GetStaticMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;Ljava/lang/String;)V"); // eventProperty(String, String)
mpv_MPVLib_event = env->GetStaticMethodID(mpv_MPVLib, "event", "(I)V"); // event(int)
mpv_MPVLib_logMessage_SiS = env->GetStaticMethodID(mpv_MPVLib, "logMessage", "(Ljava/lang/String;ILjava/lang/String;)V"); // logMessage(String, int, String)
mpv_MPVLib = env->GetObjectClass(instance);
mpv_MPVLib_eventProperty_S = env->GetMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;)V"); // eventProperty(String)
mpv_MPVLib_eventProperty_Sb = env->GetMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;Z)V"); // eventProperty(String, boolean)
mpv_MPVLib_eventProperty_Sl = env->GetMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;J)V"); // eventProperty(String, long)
mpv_MPVLib_eventProperty_Sd = env->GetMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;D)V"); // eventProperty(String, double)
mpv_MPVLib_eventProperty_SS = env->GetMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;Ljava/lang/String;)V"); // eventProperty(String, String)
mpv_MPVLib_event = env->GetMethodID(mpv_MPVLib, "event", "(I)V"); // event(int)
mpv_MPVLib_logMessage_SiS = env->GetMethodID(mpv_MPVLib, "logMessage", "(Ljava/lang/String;ILjava/lang/String;)V"); // logMessage(String, int, String)
#undef FIND_CLASS

methods_initialized = true;
return true;
}
2 changes: 1 addition & 1 deletion libmpv/src/main/cpp/jni_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#define jni_func(return_type, name, ...) JNIEXPORT return_type JNICALL jni_func_name(name) (JNIEnv *env, jobject obj, ##__VA_ARGS__)

bool acquire_jni_env(JavaVM *vm, JNIEnv **env);
void init_methods_cache(JNIEnv *env);
bool init_methods_cache(JNIEnv *env, jobject instance);

extern jclass java_Integer, java_Double, java_Boolean;
extern jmethodID java_Integer_init, java_Integer_intValue, java_Double_init, java_Double_doubleValue, java_Boolean_init, java_Boolean_booleanValue;
Expand Down
82 changes: 43 additions & 39 deletions libmpv/src/main/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,80 +16,84 @@ extern "C" {
#include "log.h"
#include "jni_utils.h"
#include "event.h"
#include "globals.h"

#define ARRAYLEN(a) (sizeof(a)/sizeof(a[0]))

// Update the JNI functions to accept a jlong parameter, which will be a
// pointer to the MPVInstance
extern "C" {
jni_func(void, create, jobject appctx);
jni_func(void, init);
jni_func(void, destroy);

jni_func(void, command, jobjectArray jarray);
jni_func(jlong, nativeCreate, jobject thiz, jobject appctx);
jni_func(void, nativeInit, jlong instance);
jni_func(void, nativeDestroy, jlong instance);
jni_func(void, nativeCommand, jlong instance, jobjectArray jarray);
};

JavaVM *g_vm;
mpv_handle *g_mpv;
std::atomic<bool> g_event_thread_request_exit(false);

static pthread_t event_thread_id;

static void prepare_environment(JNIEnv *env, jobject appctx) {
static void prepare_environment(JNIEnv *env, jobject appctx, MPVInstance* instance) {
setlocale(LC_NUMERIC, "C");

if (!env->GetJavaVM(&g_vm) && g_vm)
av_jni_set_java_vm(g_vm, nullptr);
init_methods_cache(env);
if (!env->GetJavaVM(&instance->vm) && instance->vm)
av_jni_set_java_vm(instance->vm, nullptr);
if (!instance->methods_initialized) {
instance->methods_initialized = init_methods_cache(env, instance->javaObject);
}
}

jni_func(void, create, jobject appctx) {
prepare_environment(env, appctx);

if (g_mpv)
die("mpv is already initialized");
jni_func(jlong, nativeCreate, jobject thiz, jobject appctx) {
auto instance = new MPVInstance();
instance->event_thread_request_exit = false;
instance->javaObject = env->NewGlobalRef(thiz);
prepare_environment(env, appctx, instance);

g_mpv = mpv_create();
if (!g_mpv)
instance->mpv = mpv_create();
if (!instance->mpv) {
delete instance;
die("context init failed");
}

mpv_request_log_messages(g_mpv, "v");
mpv_request_log_messages(instance->mpv, "v");
return reinterpret_cast<jlong>(instance);
}

jni_func(void, init) {
if (!g_mpv)
jni_func(void, nativeInit, jlong instance) {
auto mpv_instance = reinterpret_cast<MPVInstance*>(instance);
if (!mpv_instance->mpv)
die("mpv is not created");

if (mpv_initialize(g_mpv) < 0)
if (mpv_initialize(mpv_instance->mpv) < 0)
die("mpv init failed");

g_event_thread_request_exit = false;
pthread_create(&event_thread_id, nullptr, event_thread, nullptr);
mpv_instance->event_thread_request_exit = false;
pthread_create(&mpv_instance->event_thread_id, nullptr, event_thread, mpv_instance);
}

jni_func(void, destroy) {
if (!g_mpv)
jni_func(void, nativeDestroy, jlong instance) {
auto mpv_instance = reinterpret_cast<MPVInstance*>(instance);
if (!mpv_instance->mpv)
die("mpv destroy called but it's already destroyed");

// poke event thread and wait for it to exit
g_event_thread_request_exit = true;
mpv_wakeup(g_mpv);
pthread_join(event_thread_id, nullptr);
mpv_instance->event_thread_request_exit = true;
mpv_wakeup(mpv_instance->mpv);
pthread_join(mpv_instance->event_thread_id, nullptr);

mpv_terminate_destroy(g_mpv);
g_mpv = nullptr;
mpv_terminate_destroy(mpv_instance->mpv);
env->DeleteGlobalRef(mpv_instance->javaObject);
delete mpv_instance;
}

jni_func(void, command, jobjectArray jarray) {
jni_func(void, nativeCommand, jlong instance, jobjectArray jarray) {
auto mpv_instance = reinterpret_cast<MPVInstance*>(instance);
const char *arguments[128] = { 0 };
int len = env->GetArrayLength(jarray);
if (!g_mpv)
if (!mpv_instance->mpv)
die("Cannot run command: mpv is not initialized");
if (len >= ARRAYLEN(arguments))
die("Cannot run command: too many arguments");

for (int i = 0; i < len; ++i)
arguments[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), nullptr);

mpv_command(g_mpv, arguments);
mpv_command(mpv_instance->mpv, arguments);

for (int i = 0; i < len; ++i)
env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), arguments[i]);
Expand Down
Loading