Commit 3b78beb7 authored by Thomas Guillem's avatar Thomas Guillem

libvlc: add VLCObject

Base class for all future VLC objects: Media, MediaList, MediaDiscoverer, and
MediaPlayer in the future.
parent 51890080
......@@ -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
......
/*****************************************************************************
* 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);
}
/*****************************************************************************
* 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
/*****************************************************************************
* 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);
}
/*****************************************************************************
* 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
......@@ -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)
......
......@@ -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);
......
/*****************************************************************************
* 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;