[Android] [PATCH 07/14] libvlc: add VLCObject

Thomas Guillem thomas at gllm.fr
Fri Jan 16 17:12:07 CET 2015


Base class for all future VLC objects: Media, MediaList, MediaDiscoverer, and
MediaPlayer in the future.
---
 libvlc/jni/Android.mk                         |   2 +
 libvlc/jni/java_event_thread.c                | 154 +++++++++++++++++++
 libvlc/jni/java_event_thread.h                |  43 ++++++
 libvlc/jni/libvlcjni-vlcobject.c              | 178 +++++++++++++++++++++
 libvlc/jni/libvlcjni-vlcobject.h              |  83 ++++++++++
 libvlc/jni/libvlcjni.c                        |  61 +++++++-
 libvlc/jni/utils.h                            |  19 +++
 libvlc/src/org/videolan/libvlc/VLCObject.java | 212 ++++++++++++++++++++++++++
 8 files changed, 751 insertions(+), 1 deletion(-)
 create mode 100644 libvlc/jni/java_event_thread.c
 create mode 100644 libvlc/jni/java_event_thread.h
 create mode 100644 libvlc/jni/libvlcjni-vlcobject.c
 create mode 100644 libvlc/jni/libvlcjni-vlcobject.h
 create mode 100644 libvlc/src/org/videolan/libvlc/VLCObject.java

diff --git a/libvlc/jni/Android.mk b/libvlc/jni/Android.mk
index e6308d1..f2e61c1 100644
--- a/libvlc/jni/Android.mk
+++ b/libvlc/jni/Android.mk
@@ -5,6 +5,8 @@ LOCAL_MODULE    := libvlcjni
 
 LOCAL_SRC_FILES := libvlcjni.c libvlcjni-util.c libvlcjni-track.c
 LOCAL_SRC_FILES += libvlcjni-equalizer.c
+LOCAL_SRC_FILES += libvlcjni-vlcobject.c
+LOCAL_SRC_FILES += java_event_thread.c
 LOCAL_SRC_FILES += aout.c vout.c native_crash_handler.c thumbnailer.c
 ifneq ($(ANDROID_API),android-21)
 # compat functions not needed after android-21
diff --git a/libvlc/jni/java_event_thread.c b/libvlc/jni/java_event_thread.c
new file mode 100644
index 0000000..1dc9961
--- /dev/null
+++ b/libvlc/jni/java_event_thread.c
@@ -0,0 +1,154 @@
+/*****************************************************************************
+ * java_java_event_thread.c
+ *****************************************************************************
+ * Copyright © 2015 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <sys/queue.h>
+#include <pthread.h>
+
+#include "java_event_thread.h"
+#include "utils.h"
+
+#define LOG_TAG "JavaEventThread"
+#include "log.h"
+
+#define THREAD_NAME "JavaEventThread"
+extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
+extern void jni_detach_thread();
+extern int jni_get_env(JNIEnv **env);
+
+typedef struct event_queue_elm event_queue_elm;
+struct event_queue_elm
+{
+    java_event event;
+    TAILQ_ENTRY(event_queue_elm) next;
+};
+typedef TAILQ_HEAD(, event_queue_elm) EVENT_QUEUE;
+
+struct java_event_thread {
+    bool b_run;
+    pthread_mutex_t lock;
+    pthread_cond_t cond;
+    pthread_t thread;
+    EVENT_QUEUE queue;
+    jweak jobj;
+};
+
+static void *
+JavaEventThread_thread(void *data)
+{
+    JNIEnv *env;
+    java_event_thread *p_java_event_thread = data;
+
+    if (jni_attach_thread(&env, THREAD_NAME) < 0)
+        return NULL;
+
+    pthread_mutex_lock(&p_java_event_thread->lock);
+    while (p_java_event_thread->b_run)
+    {
+        event_queue_elm *event_elm;
+        java_event *p_jevent;
+
+        while (p_java_event_thread->b_run &&
+               !(event_elm = TAILQ_FIRST(&p_java_event_thread->queue)))
+            pthread_cond_wait(&p_java_event_thread->cond,
+                              &p_java_event_thread->lock);
+
+        if (!p_java_event_thread->b_run || event_elm == NULL)
+            continue;
+
+        p_jevent = &event_elm->event;
+        TAILQ_REMOVE(&p_java_event_thread->queue, event_elm, next);
+
+        pthread_mutex_unlock(&p_java_event_thread->lock);
+
+        (*env)->CallVoidMethod(env, p_java_event_thread->jobj,
+                               fields.VLCObject.dispatchEventFromNativeID,
+                               p_jevent->type, p_jevent->arg1, p_jevent->arg2);
+
+        free(event_elm);
+
+        pthread_mutex_lock(&p_java_event_thread->lock);
+    }
+    pthread_mutex_unlock(&p_java_event_thread->lock);
+
+    jni_detach_thread();
+
+    return NULL;
+}
+
+java_event_thread *
+JavaEventThread_create(jweak jobj)
+{
+    java_event_thread *p_java_event_thread = calloc(1, sizeof(java_event_thread));
+    if (!p_java_event_thread)
+        return NULL;
+
+    pthread_mutex_init(&p_java_event_thread->lock, NULL);
+    pthread_cond_init(&p_java_event_thread->cond, NULL);
+    TAILQ_INIT(&p_java_event_thread->queue);
+
+    p_java_event_thread->jobj = jobj;
+    p_java_event_thread->b_run = true;
+    pthread_create(&p_java_event_thread->thread, NULL,
+                   JavaEventThread_thread, p_java_event_thread);
+
+    return p_java_event_thread;
+}
+
+void
+JavaEventThread_destroy(java_event_thread *p_java_event_thread)
+{
+    event_queue_elm *event_elm, *event_elm_next;
+
+    pthread_mutex_lock(&p_java_event_thread->lock);
+    p_java_event_thread->b_run = false;
+
+    for (event_elm = TAILQ_FIRST(&p_java_event_thread->queue);
+         event_elm != NULL; event_elm = event_elm_next)
+    {
+        event_elm_next = TAILQ_NEXT(event_elm, next);
+        TAILQ_REMOVE(&p_java_event_thread->queue, event_elm, next);
+        free(event_elm);
+    }
+    pthread_cond_signal(&p_java_event_thread->cond);
+    pthread_mutex_unlock(&p_java_event_thread->lock);
+
+    pthread_join(p_java_event_thread->thread, NULL);
+
+    pthread_mutex_destroy(&p_java_event_thread->lock);
+    pthread_cond_destroy(&p_java_event_thread->cond);
+
+    free(p_java_event_thread);
+}
+
+void
+JavaEventThread_add(java_event_thread *p_java_event_thread,
+                    java_event *p_java_event)
+{
+    event_queue_elm *event_elm = calloc(1, sizeof(event_queue_elm));
+    if (!event_elm)
+        return;
+    event_elm->event = *p_java_event;
+
+    pthread_mutex_lock(&p_java_event_thread->lock);
+    TAILQ_INSERT_TAIL(&p_java_event_thread->queue, event_elm, next);
+    pthread_cond_signal(&p_java_event_thread->cond);
+    pthread_mutex_unlock(&p_java_event_thread->lock);
+}
diff --git a/libvlc/jni/java_event_thread.h b/libvlc/jni/java_event_thread.h
new file mode 100644
index 0000000..d90dd65
--- /dev/null
+++ b/libvlc/jni/java_event_thread.h
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ * java_java_event_thread.h
+ *****************************************************************************
+ * Copyright © 2015 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef JAVA_EVENT_THREAD_H
+#define JAVA_EVENT_THREAD_H
+
+#include <jni.h>
+#include <vlc/vlc.h>
+#include <vlc/libvlc_events.h>
+
+typedef struct java_event_thread java_event_thread;
+
+typedef struct java_event java_event;
+struct java_event
+{
+    int type;
+    long arg1;
+    long arg2;
+};
+
+java_event_thread *JavaEventThread_create(jweak jobj);
+void JavaEventThread_destroy(java_event_thread *p_java_event_thread);
+void JavaEventThread_add(java_event_thread *p_java_event_thread,
+                         java_event *p_java_event);
+
+#endif // JAVA_EVENT_THREAD_H
diff --git a/libvlc/jni/libvlcjni-vlcobject.c b/libvlc/jni/libvlcjni-vlcobject.c
new file mode 100644
index 0000000..2b1da7c
--- /dev/null
+++ b/libvlc/jni/libvlcjni-vlcobject.c
@@ -0,0 +1,178 @@
+/*****************************************************************************
+ * libvlcjni-vlcobject.c
+ *****************************************************************************
+ * Copyright © 2015 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <sys/queue.h>
+#include <pthread.h>
+
+#include "java_event_thread.h"
+#include "libvlcjni-vlcobject.h"
+
+struct vlcjni_object_owner
+{
+    jweak thiz;
+
+    libvlc_event_manager_t *p_event_manager;
+    const int *p_events;
+
+    java_event_thread *p_java_event_thread;
+    event_cb pf_event_cb;
+};
+
+vlcjni_object *
+VLCJniObject_getInstance(JNIEnv *env, jobject thiz)
+{
+    return (vlcjni_object*)(intptr_t) (*env)->GetLongField(env, thiz,
+                                                fields.VLCObject.mInstanceID);
+}
+
+static void
+VLCJniObject_setInstance(JNIEnv *env, jobject thiz, vlcjni_object *p_obj)
+{
+    (*env)->SetLongField(env, thiz,
+                         fields.VLCObject.mInstanceID,
+                         (jlong)(intptr_t)p_obj);
+}
+
+vlcjni_object *
+VLCJniObject_newFromLibVlc(JNIEnv *env, jobject thiz,
+                           libvlc_instance_t *p_libvlc)
+{
+    vlcjni_object *p_obj;
+    libvlc_event_manager_t *ev;
+
+    p_obj = VLCJniObject_getInstance(env, thiz);
+    if (p_obj)
+        return NULL;
+
+    p_obj = calloc(1, sizeof(vlcjni_object));
+    if (!p_obj)
+        goto error;
+
+    p_obj->p_owner = calloc(1, sizeof(vlcjni_object_owner));
+    if (!p_obj->p_owner)
+        goto error;
+
+    p_obj->p_libvlc = p_libvlc;
+    libvlc_retain(p_libvlc);
+
+    p_obj->p_owner->thiz = (*env)->NewWeakGlobalRef(env, thiz);
+    if (!p_obj->p_owner->thiz)
+        goto error;
+
+    VLCJniObject_setInstance(env, thiz, p_obj);
+
+    return p_obj;
+
+error:
+    VLCJniObject_release(env, thiz, p_obj);
+    return NULL;
+}
+
+vlcjni_object *
+VLCJniObject_newFromJavaLibVlc(JNIEnv *env, jobject thiz,
+                               jobject libVlc)
+{
+    libvlc_instance_t *p_libvlc = getLibVlcInstance(env, libVlc);
+    if (!p_libvlc)
+        return NULL;
+    return VLCJniObject_newFromLibVlc(env, thiz, p_libvlc);
+}
+
+void
+VLCJniObject_release(JNIEnv *env, jobject thiz, vlcjni_object *p_obj)
+{
+    if (p_obj)
+    {
+        if (p_obj->p_libvlc)
+            libvlc_release(p_obj->p_libvlc);
+
+        if (p_obj->p_owner && p_obj->p_owner->thiz)
+            (*env)->DeleteWeakGlobalRef(env, p_obj->p_owner->thiz);
+
+        free(p_obj->p_owner);
+        free(p_obj);
+        VLCJniObject_setInstance(env, thiz, NULL);
+    }
+}
+
+static void
+VLCJniObject_eventCallback(const libvlc_event_t *ev, void *data)
+{
+    vlcjni_object *p_obj = data;
+    java_event jevent;
+   
+    jevent.type = -1;
+    jevent.arg1 = jevent.arg2 = 0;
+
+    if (!p_obj->p_owner->pf_event_cb(p_obj, ev, &jevent))
+        return;
+
+    if (!p_obj->p_owner->p_java_event_thread)
+    {
+        p_obj->p_owner->p_java_event_thread =
+            JavaEventThread_create(p_obj->p_owner->thiz);
+        if (!p_obj->p_owner->p_java_event_thread)
+            return;
+    }
+    JavaEventThread_add(p_obj->p_owner->p_java_event_thread, &jevent);
+}
+
+void
+VLCJniObject_attachEvents(vlcjni_object *p_obj,
+                          event_cb pf_event_cb,
+                          libvlc_event_manager_t *p_event_manager,
+                          const int *p_events)
+{
+    if (!pf_event_cb || !p_event_manager || !p_events
+        || p_obj->p_owner->p_event_manager
+        || p_obj->p_owner->p_events)
+        return;
+
+    p_obj->p_owner->pf_event_cb = pf_event_cb;
+
+    p_obj->p_owner->p_event_manager = p_event_manager;
+    p_obj->p_owner->p_events = p_events;
+
+    for(int i = 0; p_obj->p_owner->p_events[i] != -1; ++i)
+        libvlc_event_attach(p_obj->p_owner->p_event_manager,
+                            p_obj->p_owner->p_events[i],
+                            VLCJniObject_eventCallback, p_obj);
+}
+
+void
+Java_org_videolan_libvlc_VLCObject_nativeDetachEvents(JNIEnv *env, jobject thiz)
+{
+    vlcjni_object *p_obj = VLCJniObject_getInstance(env, thiz);
+
+    if (!p_obj || !p_obj->p_owner->p_event_manager
+        || !p_obj->p_owner->p_events)
+        return;
+
+    for(int i = 0; p_obj->p_owner->p_events[i] != -1; ++i)
+        libvlc_event_detach(p_obj->p_owner->p_event_manager,
+                            p_obj->p_owner->p_events[i],
+                            VLCJniObject_eventCallback, p_obj);
+    p_obj->p_owner->p_event_manager = NULL;
+    p_obj->p_owner->p_events = NULL;
+
+    if (p_obj->p_owner->p_java_event_thread)
+        JavaEventThread_destroy(p_obj->p_owner->p_java_event_thread);
+}
diff --git a/libvlc/jni/libvlcjni-vlcobject.h b/libvlc/jni/libvlcjni-vlcobject.h
new file mode 100644
index 0000000..b74a8d1
--- /dev/null
+++ b/libvlc/jni/libvlcjni-vlcobject.h
@@ -0,0 +1,83 @@
+/*****************************************************************************
+ * libvlcjni-vlcobject.h
+ *****************************************************************************
+ * Copyright © 2015 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef LIBVLCJNI_VLCOBJECT_H
+#define LIBVLCJNI_VLCOBJECT_H
+
+#include <stdbool.h>
+
+#include <jni.h>
+#include <vlc/vlc.h>
+#include <vlc/libvlc_media_list.h>
+#include <vlc/libvlc_media_discoverer.h>
+
+#include "java_event_thread.h"
+#include "utils.h"
+#define LOG_TAG "VLC/JNI/VLCObject"
+#include "log.h"
+
+typedef struct vlcjni_object vlcjni_object;
+typedef struct vlcjni_object_owner vlcjni_object_owner;
+typedef struct vlcjni_object_sys vlcjni_object_sys;
+
+struct vlcjni_object
+{
+    libvlc_instance_t *p_libvlc;
+    union {
+        libvlc_media_t *p_m;
+        libvlc_media_list_t *p_ml;
+        libvlc_media_discoverer_t *p_md;
+    } u;
+    vlcjni_object_owner *p_owner; // used by vlcobject
+    vlcjni_object_sys *p_sys; // used by media, medialist, mediadiscoverer...
+};
+
+/* event manager callback dispatched to native struct implementing a
+ * vlcjni_object. If the callback returns true, the event is dispatched to Java
+ * */
+typedef bool (*event_cb)(vlcjni_object *p_obj, const libvlc_event_t *p_ev,
+                         java_event *p_java_event);
+
+
+vlcjni_object *VLCJniObject_getInstance(JNIEnv *env, jobject thiz);
+
+vlcjni_object *VLCJniObject_newFromJavaLibVlc(JNIEnv *env, jobject thiz,
+                                              jobject libVlc);
+
+vlcjni_object *VLCJniObject_newFromLibVlc(JNIEnv *env, jobject thiz,
+                                          libvlc_instance_t *p_libvlc);
+
+void VLCJniObject_release(JNIEnv *env, jobject thiz, vlcjni_object *p_obj);
+
+void VLCJniObject_attachEvents(vlcjni_object *p_obj, event_cb pf_event_cb,
+                               libvlc_event_manager_t *p_event_manager,
+                               const int *p_events);
+
+static inline void throw_IllegalStateException(JNIEnv *env, const char *p_error)
+{
+    (*env)->ThrowNew(env, fields.IllegalStateException.clazz, p_error);
+}
+
+static inline void throw_IllegalArgumentException(JNIEnv *env, const char *p_error)
+{
+    (*env)->ThrowNew(env, fields.IllegalArgumentException.clazz, p_error);
+}
+
+#endif // LIBVLCJNI_VLCOBJECT_H
diff --git a/libvlc/jni/libvlcjni.c b/libvlc/jni/libvlcjni.c
index 664092a..b14281b 100644
--- a/libvlc/jni/libvlcjni.c
+++ b/libvlc/jni/libvlcjni.c
@@ -48,6 +48,8 @@
 #define LOG_TAG "VLC/JNI/main"
 #include "log.h"
 
+struct fields fields;
+
 #define VLC_JNI_VERSION JNI_VERSION_1_2
 
 #define THREAD_NAME "libvlcjni"
@@ -211,19 +213,76 @@ end:
 
 jint JNI_OnLoad(JavaVM *vm, void *reserved)
 {
+    JNIEnv* env = NULL;
     // Keep a reference on the Java VM.
     myVm = vm;
 
+    if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
+        return -1;
     pthread_mutex_init(&vout_android_lock, NULL);
     pthread_cond_init(&vout_android_surf_attached, NULL);
 
+#define GET_CLASS(clazz, str) do { \
+    (clazz) = (*env)->FindClass(env, (str)); \
+    if (!(clazz)) { \
+        LOGE("FindClass(%s) failed", (str)); \
+        return -1; \
+    } \
+    (clazz) = (jclass) (*env)->NewGlobalRef(env, (clazz)); \
+    if (!(clazz)) { \
+        LOGE("NewGlobalRef(%s) failed", (str)); \
+        return -1; \
+    } \
+} while (0)
+
+#define GET_ID(get, id, clazz, str, args) do { \
+    (id) = (*env)->get(env, (clazz), (str), (args)); \
+    if (!(id)) { \
+        LOGE(#get"(%s) failed", (str)); \
+        return -1; \
+    } \
+} while (0)
+
+    GET_CLASS(fields.IllegalStateException.clazz,
+              "java/lang/IllegalStateException");
+    GET_CLASS(fields.IllegalArgumentException.clazz,
+              "java/lang/IllegalArgumentException");
+    GET_CLASS(fields.String.clazz,
+              "java/lang/String");
+    GET_CLASS(fields.VLCObject.clazz,
+              "org/videolan/libvlc/VLCObject");
+
+    GET_ID(GetFieldID,
+           fields.VLCObject.mInstanceID,
+           fields.VLCObject.clazz,
+           "mInstance", "J");
+
+    GET_ID(GetMethodID,
+           fields.VLCObject.dispatchEventFromNativeID,
+           fields.VLCObject.clazz,
+           "dispatchEventFromNative", "(IJJ)V");
+
+#undef GET_CLASS
+#undef GET_ID
+
     LOGD("JNI interface loaded.");
     return VLC_JNI_VERSION;
 }
 
-void JNI_OnUnload(JavaVM* vm, void* reserved) {
+void JNI_OnUnload(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env = NULL;
+
     pthread_mutex_destroy(&vout_android_lock);
     pthread_cond_destroy(&vout_android_surf_attached);
+
+    if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
+        return;
+
+    (*env)->DeleteGlobalRef(env, fields.IllegalStateException.clazz);
+    (*env)->DeleteGlobalRef(env, fields.IllegalArgumentException.clazz);
+    (*env)->DeleteGlobalRef(env, fields.String.clazz);
+    (*env)->DeleteGlobalRef(env, fields.VLCObject.clazz);
 }
 
 int jni_attach_thread(JNIEnv **env, const char *thread_name)
diff --git a/libvlc/jni/utils.h b/libvlc/jni/utils.h
index 5da3514..14879bc 100644
--- a/libvlc/jni/utils.h
+++ b/libvlc/jni/utils.h
@@ -21,6 +21,25 @@
 #ifndef LIBVLCJNI_UTILS_H
 #define LIBVLCJNI_UTILS_H
 
+struct fields {
+    struct {
+        jclass clazz;
+    } IllegalStateException;
+    struct {
+        jclass clazz;
+    } IllegalArgumentException;
+    struct {
+        jclass clazz;
+    } String;
+    struct {
+        jclass clazz;
+        jfieldID mInstanceID;
+        jmethodID dispatchEventFromNativeID;
+    } VLCObject;
+};
+
+extern struct fields fields;
+
 libvlc_media_t *new_media(JNIEnv *env, jobject thiz, jstring fileLocation, bool noOmx, bool noVideo);
 
 libvlc_instance_t *getLibVlcInstance(JNIEnv *env, jobject thiz);
diff --git a/libvlc/src/org/videolan/libvlc/VLCObject.java b/libvlc/src/org/videolan/libvlc/VLCObject.java
new file mode 100644
index 0000000..c6816cf
--- /dev/null
+++ b/libvlc/src/org/videolan/libvlc/VLCObject.java
@@ -0,0 +1,212 @@
+/*****************************************************************************
+ * VLCObject.java
+ *****************************************************************************
+ * Copyright © 2015 VLC authors, VideoLAN and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+package org.videolan.libvlc;
+
+import android.os.Handler;
+import android.os.Looper;
+
+public abstract class VLCObject {
+    private final static String TAG = "LibVLC/VlcObject";
+
+    public static class Events {
+        public static final int MediaMetaChanged                  = 0;
+        public static final int MediaSubItemAdded               = 1;
+        public static final int MediaDurationChanged            = 2;
+        public static final int MediaParsedChanged                = 3;
+        //public static final int MediaFreed                      = 4;
+        public static final int MediaStateChanged               = 5;
+        public static final int MediaSubItemTreeAdded           = 6;
+
+        //public static final int MediaPlayerMediaChanged         = 0x100;
+        //public static final int MediaPlayerNothingSpecial       = 0x101;
+        //public static final int MediaPlayerOpening              = 0x102;
+        //public static final int MediaPlayerBuffering            = 0x103;
+        //public static final int MediaPlayerPlaying                = 0x104;
+        //public static final int MediaPlayerPaused                 = 0x105;
+        //public static final int MediaPlayerStopped                = 0x106;
+        //public static final int MediaPlayerForward              = 0x107;
+        //public static final int MediaPlayerBackward             = 0x108;
+        //public static final int MediaPlayerEndReached             = 0x109;
+        //public static final int MediaPlayerEncounteredError       = 0x10a;
+        //public static final int MediaPlayerTimeChanged            = 0x10b;
+        //public static final int MediaPlayerPositionChanged        = 0x10c;
+        //public static final int MediaPlayerSeekableChanged      = 0x10d;
+        //public static final int MediaPlayerPausableChanged      = 0x10e;
+        //public static final int MediaPlayerTitleChanged         = 0x10f;
+        //public static final int MediaPlayerSnapshotTaken        = 0x110;
+        //public static final int MediaPlayerLengthChanged        = 0x111;
+        //public static final int MediaPlayerVout                   = 0x112;
+
+        public static final int MediaListItemAdded              = 0x200;
+        //public static final int MediaListWillAddItem            = 0x201;
+        public static final int MediaListItemDeleted            = 0x202;
+        //public static final int MediaListWillDeleteItem         = 0x203;
+
+        //public static final int MediaListViewItemAdded          = 0x300;
+        //public static final int MediaListViewWillAddItem        = 0x301;
+        //public static final int MediaListViewItemDeleted        = 0x302;
+        //public static final int MediaListViewWillDeleteItem     = 0x303;
+
+        //public static final int MediaListPlayerPlayed           = 0x400;
+        //public static final int MediaListPlayerNextItemSet      = 0x401;
+        //public static final int MediaListPlayerStopped          = 0x402;
+
+        public static final int MediaDiscovererStarted          = 0x500;
+        public static final int MediaDiscovererEnded            = 0x501;
+
+        //public static final int VlmMediaAdded                   = 0x600;
+        //public static final int VlmMediaRemoved                 = 0x601;
+        //public static final int VlmMediaChanged                 = 0x602;
+        //public static final int VlmMediaInstanceStarted         = 0x603;
+        //public static final int VlmMediaInstanceStopped         = 0x604;
+        //public static final int VlmMediaInstanceStatusInit      = 0x605;
+        //public static final int VlmMediaInstanceStatusOpening   = 0x606;
+        //public static final int VlmMediaInstanceStatusPlaying   = 0x607;
+        //public static final int VlmMediaInstanceStatusPause     = 0x608;
+        //public static final int VlmMediaInstanceStatusEnd       = 0x609;
+        //public static final int VlmMediaInstanceStatusError     = 0x60a;
+    }
+
+    /**
+     * Event used by EventListener
+     * Can be casted to inherited class Event (like {@link MediaList.Event}).
+     */
+    public static class Event {
+        /**
+         * @see Events
+         */
+        public final int type;
+        protected Event(int type) {
+            this.type = type;
+        }
+    }
+
+    /**
+     * Listener for libvlc events
+     *
+     * @see Event
+     */
+    public interface EventListener {
+        public void onEvent(Event event);
+    }
+
+    private static class EventRunnable implements Runnable {
+        private final EventListener listener;
+        private final Event event;
+
+        private EventRunnable(EventListener listener, Event event) {
+            this.listener = listener;
+            this.event = event;
+        }
+        @Override
+        public void run() {
+            listener.onEvent(event);
+        }
+    }
+
+    private EventListener mEventListener = null;
+    private Handler mHandler = null;
+    private int mNativeRefCount = 1;
+
+    /**
+     * Returns true if native object is released
+     */
+    public synchronized boolean isReleased() {
+        return mNativeRefCount == 0;
+    }
+
+    /**
+     * Increment internal ref count of the native object.
+     */
+    public synchronized final void retain() {
+        if (mNativeRefCount > 0)
+            mNativeRefCount++;
+    }
+
+    /**
+     * Release the native object if ref count is 1.
+     *
+     * After this call, native calls are not possible anymore.
+     * You can still call others methods to retrieve cached values.
+     * For example: if you parse, then release a media, you'll still be able to retrieve all Metas or Tracks infos.
+     */
+    public final void release() {
+        int refCount = -1;
+        synchronized (this) {
+            if (mNativeRefCount == 0)
+                return;
+            if (mNativeRefCount > 0) {
+                refCount = --mNativeRefCount;
+            }
+            // clear event list
+            if (refCount == 0)
+                setEventListener(null);
+        }
+        if (refCount == 0) {
+            // detach events when not synchronized since onEvent is executed synchronized
+            nativeDetachEvents();
+            synchronized (this) {
+                onReleaseNative();
+            }
+        }
+    }
+
+    /**
+     * Set an event listener.
+     *
+     * @param listener see {@link EventListener}
+     */
+    public synchronized final void setEventListener(EventListener listener) {
+        if (mHandler != null)
+            mHandler.removeCallbacksAndMessages(null);
+        mEventListener = listener;
+        if (mEventListener != null && mHandler == null)
+            mHandler = new Handler(Looper.getMainLooper());
+    }
+
+    /**
+     * Called when libvlc send events.
+     *
+     * @param eventType
+     * @param arg1
+     * @param arg2
+     * @return Event that will be dispatched to listeners
+     */
+    protected abstract Event onEventNative(int eventType, long arg1, long arg2);
+
+    /**
+     * Called when native object is released (refcount is 0).
+     *
+     * This is where you must release native resources.
+     */
+    protected abstract void onReleaseNative();
+
+    /* JNI */
+    private long mInstance = 0; // Read-only, reserved for JNI
+    private synchronized void dispatchEventFromNative(int eventType, long arg1, long arg2) {
+        if (isReleased())
+            return;
+        final Event event = onEventNative(eventType, arg1, arg2);
+        if (event != null && mEventListener != null && mHandler != null)
+            mHandler.post(new EventRunnable(mEventListener, event));
+    }
+    private final native void nativeDetachEvents();
+}
-- 
2.1.3



More information about the Android mailing list