[Android] [PATCH 4/5] Support multiple video player instances.

Edward Wang edward.c.wang at compdigitec.com
Fri Jul 25 07:17:07 CEST 2014


On 2014-07-24 22:14, Paulo Vitor Magacho da Silva <pvmagacho at gmail.com> 
wrote:
> ---
>   vlc-android/jni/libvlcjni.c                        |  39 ++++--
>   vlc-android/jni/utils.h                            |  12 ++
>   vlc-android/jni/vout.c                             | 135 +++++++++++----------
>   vlc-android/jni/vout.h                             |   4 -
>   vlc-android/src/org/videolan/libvlc/LibVLC.java    |  33 +++--
>   vlc-android/src/org/videolan/libvlc/MediaList.java |   2 +-
>   .../org/videolan/vlc/gui/PreferencesActivity.java  |   2 +-
>   7 files changed, 139 insertions(+), 88 deletions(-)

Most notably, you have to keep track of which objects belong to which 
libVLC, and that could get messy.

I'd like to test this out first, but that will be for tomorrow.

Some comments inline below.

>
> diff --git a/vlc-android/jni/libvlcjni.c b/vlc-android/jni/libvlcjni.c
> index 6a08d85..1f11b20 100644
> --- a/vlc-android/jni/libvlcjni.c
> +++ b/vlc-android/jni/libvlcjni.c
> @@ -200,16 +200,12 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
>       // Keep a reference on the Java VM.
>       myVm = vm;
>   
> -    pthread_mutex_init(&vout_android_lock, NULL);
> -    pthread_cond_init(&vout_android_surf_attached, NULL);
> -
>       LOGD("JNI interface loaded.");
>       return JNI_VERSION_1_2;
>   }
>   
>   void JNI_OnUnload(JavaVM* vm, void* reserved) {
> -    pthread_mutex_destroy(&vout_android_lock);
> -    pthread_cond_destroy(&vout_android_surf_attached);
> +    LOGD("JNI interface un-loaded.");
>   }
>   
>   // FIXME: use atomics
> @@ -217,6 +213,18 @@ static bool verbosity;
>   
>   void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
>   {
> +    android_surf_value_t *object = (android_surf_value_t *) malloc(sizeof(android_surf_value_t));

More descriptive variable name than 'object' possible?

> +
> +    object->vout_android_surf = NULL;
> +    object->vout_android_gui = NULL;
> +    object->vout_android_java_surf = NULL;
> +    object->vout_android_subtitles_surf = NULL;
> +
> +    pthread_mutex_init(&object->vout_android_lock, NULL);
> +    pthread_cond_init(&object->vout_android_surf_attached, NULL);
> +
> +    setLong(env, thiz, "mAndroidSurfaceValue", (jlong)(intptr_t)object);
> +
>       //only use OpenSLES if java side says we can
>       jclass cls = (*env)->GetObjectClass(env, thiz);
>       jmethodID methodId = (*env)->GetMethodID(env, cls, "getAout", "()I");
> @@ -318,12 +326,23 @@ void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
>   
>       libvlc_log_set(instance, debug_log, &verbosity);
>   
> -    init_native_crash_handler(env, thiz);
> +    // init_native_crash_handler(env, thiz);

Why?

>   }
>   
>   void Java_org_videolan_libvlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
>   {
> -    destroy_native_crash_handler(env);
> +    // clear surface lock and condition
> +    android_surf_value_t *object = (android_surf_value_t *)(intptr_t)getLong(env, thiz, "mAndroidSurfaceValue");
> +
> +    if (object != NULL) {
> +        pthread_mutex_destroy(&object->vout_android_lock);
> +        pthread_cond_destroy(&object->vout_android_surf_attached);
> +
> +        free(object);
> +    }
> +    setLong(env, thiz, "mAndroidSurfaceValue", 0);
> +
> +    // destroy_native_crash_handler(env);
Ditto.

>   
>       releaseMediaPlayer(env, thiz);
>       jlong libVlcInstance = getLong(env, thiz, "mLibVlcInstance");
> @@ -413,9 +432,13 @@ void Java_org_videolan_libvlc_LibVLC_playMRL(JNIEnv *env, jobject thiz, jlong in
>               (*env)->ReleaseStringUTFChars(env, option, p_st);
>           }
>       }
> -
> +

?

>       (*env)->ReleaseStringUTFChars(env, mrl, p_mrl);
>   
> +    // set the Android surface value structure
> +    android_surf_value_t *object = (android_surf_value_t *)(intptr_t)getLong(env, thiz, "mAndroidSurfaceValue");
> +    libvlc_media_player_set_surfacevalue(mp, object);
> +
>       /* Connect the media event manager. */

What about events? How does each instance deal with its own events?

>       libvlc_event_manager_t *ev_media = libvlc_media_event_manager(p_md);
>       static const libvlc_event_type_t mp_media_events[] = {
> diff --git a/vlc-android/jni/utils.h b/vlc-android/jni/utils.h
> index f8502d9..ccf23aa 100644
> --- a/vlc-android/jni/utils.h
> +++ b/vlc-android/jni/utils.h
> @@ -21,6 +21,18 @@
>   #ifndef LIBVLCJNI_UTILS_H
>   #define LIBVLCJNI_UTILS_H
>   
> +#include <pthread.h>
> +
> +typedef struct android_surf_value_t {
> +    pthread_mutex_t vout_android_lock;
> +    pthread_cond_t vout_android_surf_attached;
> +    void *vout_android_surf;
> +    void *vout_android_gui;
> +    jobject vout_android_java_surf;
> +    jobject vout_android_subtitles_surf;
> +    bool vout_video_player_activity_created;
> +} android_surf_value_t;
> +
>   libvlc_media_t *new_media(jlong instance, JNIEnv *env, jobject thiz, jstring fileLocation, bool noOmx, bool noVideo);
>   
>   libvlc_media_player_t *getMediaPlayer(JNIEnv *env, jobject thiz);
> diff --git a/vlc-android/jni/vout.c b/vlc-android/jni/vout.c
> index f2f4346..74e1f1e 100644
> --- a/vlc-android/jni/vout.c
> +++ b/vlc-android/jni/vout.c
> @@ -23,96 +23,94 @@
>   
>   #include <jni.h>
>   
> +#include "utils.h"
> +
>   /** Unique Java VM instance, as defined in libvlcjni.c */
>   extern JavaVM *myVm;
>   
> -pthread_mutex_t vout_android_lock;
> -pthread_cond_t vout_android_surf_attached;
> -static void *vout_android_surf = NULL;
> -static void *vout_android_gui = NULL;
> -static jobject vout_android_java_surf = NULL;
> -static jobject vout_android_subtitles_surf = NULL;
> -static bool vout_video_player_activity_created = false;
> -
> -void *jni_LockAndGetSubtitlesSurface() {
> -    pthread_mutex_lock(&vout_android_lock);
> -    while (vout_android_subtitles_surf == NULL)
> -        pthread_cond_wait(&vout_android_surf_attached, &vout_android_lock);
> -    return vout_android_subtitles_surf;
> +void *jni_LockAndGetSubtitlesSurface(android_surf_value_t *object) {
> +    pthread_mutex_lock(&object->vout_android_lock);
> +    while (object->vout_android_subtitles_surf == NULL)
> +        pthread_cond_wait(&object->vout_android_surf_attached, &object->vout_android_lock);
> +    return object->vout_android_subtitles_surf;
>   }
>   
> -void *jni_LockAndGetAndroidSurface() {
> -    pthread_mutex_lock(&vout_android_lock);
> -    while (vout_android_surf == NULL)
> -        pthread_cond_wait(&vout_android_surf_attached, &vout_android_lock);
> -    return vout_android_surf;
> +void *jni_LockAndGetAndroidSurface(android_surf_value_t *object) {
> +    pthread_mutex_lock(&object->vout_android_lock);
> +    while (object->vout_android_surf == NULL)
> +        pthread_cond_wait(&object->vout_android_surf_attached, &object->vout_android_lock);
> +    return object->vout_android_surf;
>   }
>   
> -jobject jni_LockAndGetAndroidJavaSurface() {
> -    pthread_mutex_lock(&vout_android_lock);
> -    while (vout_android_java_surf == NULL)
> -        pthread_cond_wait(&vout_android_surf_attached, &vout_android_lock);
> -    return vout_android_java_surf;
> +jobject jni_LockAndGetAndroidJavaSurface(android_surf_value_t *object) {
> +    pthread_mutex_lock(&object->vout_android_lock);
> +    while (object->vout_android_java_surf == NULL)
> +        pthread_cond_wait(&object->vout_android_surf_attached, &object->vout_android_lock);
> +    return object->vout_android_java_surf;
>   }
>   
> -void jni_UnlockAndroidSurface() {
> -    pthread_mutex_unlock(&vout_android_lock);
> +void jni_UnlockAndroidSurface(android_surf_value_t *object) {
> +    pthread_mutex_unlock(&object->vout_android_lock);
>   }
>   
> -void jni_EventHardwareAccelerationError()
> +void jni_EventHardwareAccelerationError(android_surf_value_t *object)
>   {
> -    if (vout_android_gui == NULL)
> +    if (object->vout_android_gui == NULL)
>           return;
>   
>       JNIEnv *env;
>       (*myVm)->AttachCurrentThread(myVm, &env, NULL);
>   
> -    jclass cls = (*env)->GetObjectClass(env, vout_android_gui);
> +    jclass cls = (*env)->GetObjectClass(env, object->vout_android_gui);
>       jmethodID methodId = (*env)->GetMethodID(env, cls, "eventHardwareAccelerationError", "()V");
> -    (*env)->CallVoidMethod(env, vout_android_gui, methodId);
> +    (*env)->CallVoidMethod(env, object->vout_android_gui, methodId);
>   
>       (*env)->DeleteLocalRef(env, cls);
>       (*myVm)->DetachCurrentThread(myVm);
>   }
>   
> -void jni_SetAndroidSurfaceSizeEnv(JNIEnv *p_env, int width, int height, int visible_width, int visible_height, int sar_num, int sar_den)
> +void jni_SetAndroidSurfaceSizeEnv(JNIEnv *p_env, android_surf_value_t *object, int width, int height, int visible_width, int visible_height, int sar_num, int sar_den)
>   {
> -    if (vout_android_gui == NULL)
> +    if (object->vout_android_gui == NULL)
>           return;
>   
> -    jclass cls = (*p_env)->GetObjectClass (p_env, vout_android_gui);
> +    jclass cls = (*p_env)->GetObjectClass (p_env, object->vout_android_gui);
>       jmethodID methodId = (*p_env)->GetMethodID (p_env, cls, "setSurfaceSize", "(IIIIII)V");
>   
> -    (*p_env)->CallVoidMethod (p_env, vout_android_gui, methodId, width, height, visible_width, visible_height, sar_num, sar_den);
> +    (*p_env)->CallVoidMethod (p_env, object->vout_android_gui, methodId, width, height, visible_width, visible_height, sar_num, sar_den);
>   
>       (*p_env)->DeleteLocalRef(p_env, cls);
>   }
>   
> -void jni_SetAndroidSurfaceSize(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den)
> +void jni_SetAndroidSurfaceSize(android_surf_value_t *object, int width, int height, int visible_width, int visible_height, int sar_num, int sar_den)
>   {
>       JNIEnv *p_env;
>   
>       (*myVm)->AttachCurrentThread (myVm, &p_env, NULL);
> -    jni_SetAndroidSurfaceSizeEnv(p_env, width, height, visible_width, visible_height, sar_num, sar_den);
> +    jni_SetAndroidSurfaceSizeEnv(p_env, object, width, height, visible_width, visible_height, sar_num, sar_den);
>   
>       (*myVm)->DetachCurrentThread (myVm);
>   }
>   
> -bool jni_IsVideoPlayerActivityCreated() {
> -    pthread_mutex_lock(&vout_android_lock);
> -    bool result = vout_video_player_activity_created;
> -    pthread_mutex_unlock(&vout_android_lock);
> +bool jni_IsVideoPlayerActivityCreated(android_surf_value_t *object) {
> +    pthread_mutex_lock(&object->vout_android_lock);
> +    bool result = object->vout_video_player_activity_created;
> +    pthread_mutex_unlock(&object->vout_android_lock);
>       return result;
>   }
>   
>   void Java_org_videolan_libvlc_LibVLC_eventVideoPlayerActivityCreated(JNIEnv *env, jobject thiz, jboolean created) {
> -    pthread_mutex_lock(&vout_android_lock);
> -    vout_video_player_activity_created = created;
> -    pthread_mutex_unlock(&vout_android_lock);
> +    android_surf_value_t * object = (android_surf_value_t *)(intptr_t)getLong(env, thiz, "mAndroidSurfaceValue");
> +
> +    pthread_mutex_lock(&object->vout_android_lock);
> +    object->vout_video_player_activity_created = created;
> +    pthread_mutex_unlock(&object->vout_android_lock);
>   }
>   
>   void Java_org_videolan_libvlc_LibVLC_attachSurface(JNIEnv *env, jobject thiz, jobject surf, jobject gui) {
> -    pthread_mutex_lock(&vout_android_lock);
> +    android_surf_value_t * object = (android_surf_value_t *)(intptr_t)getLong(env, thiz, "mAndroidSurfaceValue");
> +
> +    pthread_mutex_lock(&object->vout_android_lock);
>       jclass clz;
>       jfieldID fid;
>   
> @@ -131,39 +129,46 @@ void Java_org_videolan_libvlc_LibVLC_attachSurface(JNIEnv *env, jobject thiz, jo
>               }
>               fid = (*env)->GetFieldID(env, clz, "mNativeSurface", "I");
>           }
> -        vout_android_surf = (void*)(*env)->GetIntField(env, surf, fid);
> +
> +        object->vout_android_surf = (void*)(*env)->GetIntField(env, surf, fid);
>           (*env)->DeleteLocalRef(env, clz);
>       }
> -    vout_android_gui = (*env)->NewGlobalRef(env, gui);
> -    vout_android_java_surf = (*env)->NewGlobalRef(env, surf);
> -    pthread_cond_signal(&vout_android_surf_attached);
> -    pthread_mutex_unlock(&vout_android_lock);
> +    object->vout_android_gui = (*env)->NewGlobalRef(env, gui);
> +    object->vout_android_java_surf = (*env)->NewGlobalRef(env, surf);
> +    pthread_cond_signal(&object->vout_android_surf_attached);
> +    pthread_mutex_unlock(&object->vout_android_lock);
>   }
>   
>   void Java_org_videolan_libvlc_LibVLC_detachSurface(JNIEnv *env, jobject thiz) {
> -    pthread_mutex_lock(&vout_android_lock);
> -    vout_android_surf = NULL;
> -    if (vout_android_gui != NULL)
> -        (*env)->DeleteGlobalRef(env, vout_android_gui);
> -    if (vout_android_java_surf != NULL)
> -        (*env)->DeleteGlobalRef(env, vout_android_java_surf);
> -    vout_android_gui = NULL;
> -    vout_android_java_surf = NULL;
> -    pthread_mutex_unlock(&vout_android_lock);
> +    android_surf_value_t *object = (android_surf_value_t *)(intptr_t)getLong(env, thiz, "mAndroidSurfaceValue");
> +
> +    pthread_mutex_lock(&object->vout_android_lock);
> +    object->vout_android_surf = NULL;
> +    if (object->vout_android_gui != NULL)
> +        (*env)->DeleteGlobalRef(env, object->vout_android_gui);
> +    if (object->vout_android_java_surf != NULL)
> +        (*env)->DeleteGlobalRef(env, object->vout_android_java_surf);
> +    object->vout_android_gui = NULL;
> +    object->vout_android_java_surf = NULL;
> +    pthread_mutex_unlock(&object->vout_android_lock);
>   }
>   
>   void Java_org_videolan_libvlc_LibVLC_attachSubtitlesSurface(JNIEnv *env, jobject thiz, jobject surf) {
> -    pthread_mutex_lock(&vout_android_lock);
> -    vout_android_subtitles_surf = (*env)->NewGlobalRef(env, surf);
> -    pthread_cond_signal(&vout_android_surf_attached);
> -    pthread_mutex_unlock(&vout_android_lock);
> +    android_surf_value_t *object = (android_surf_value_t *)(intptr_t)getLong(env, thiz, "mAndroidSurfaceValue");
> +
> +    pthread_mutex_lock(&object->vout_android_lock);
> +    object->vout_android_subtitles_surf = (*env)->NewGlobalRef(env, surf);
> +    pthread_cond_signal(&object->vout_android_surf_attached);
> +    pthread_mutex_unlock(&object->vout_android_lock);
>   }
>   
>   void Java_org_videolan_libvlc_LibVLC_detachSubtitlesSurface(JNIEnv *env, jobject thiz) {
> -    pthread_mutex_lock(&vout_android_lock);
> -    (*env)->DeleteGlobalRef(env, vout_android_subtitles_surf);
> -    vout_android_subtitles_surf = NULL;
> -    pthread_mutex_unlock(&vout_android_lock);
> +    android_surf_value_t *object = (android_surf_value_t *)(intptr_t)getLong(env, thiz, "mAndroidSurfaceValue");
> +
> +    pthread_mutex_lock(&object->vout_android_lock);
> +    (*env)->DeleteGlobalRef(env, object->vout_android_subtitles_surf);
> +    object->vout_android_subtitles_surf = NULL;
> +    pthread_mutex_unlock(&object->vout_android_lock);
>   }
>   
>   static int mouse_x = -1;
> diff --git a/vlc-android/jni/vout.h b/vlc-android/jni/vout.h
> index c3d4fd7..d14957a 100644
> --- a/vlc-android/jni/vout.h
> +++ b/vlc-android/jni/vout.h
> @@ -21,8 +21,4 @@
>   #ifndef LIBVLCJNI_VOUT_H
>   #define LIBVLCJNI_VOUT_H
>   
> -/* vout lock initialized in vout.c */
> -pthread_mutex_t vout_android_lock;
> -pthread_cond_t vout_android_surf_attached;
> -
>   #endif // LIBVLCJNI_VOUT_H
> diff --git a/vlc-android/src/org/videolan/libvlc/LibVLC.java b/vlc-android/src/org/videolan/libvlc/LibVLC.java
> index 5ac201e..abc50a7 100644
> --- a/vlc-android/src/org/videolan/libvlc/LibVLC.java
> +++ b/vlc-android/src/org/videolan/libvlc/LibVLC.java
> @@ -51,6 +51,9 @@ public class LibVLC {
>       private int mInternalMediaPlayerIndex = 0; // Read-only, reserved for JNI
>       private long mInternalMediaPlayerInstance = 0; // Read-only, reserved for JNI
>   
> +    private long mAndroidSurfaceValue = 0; // Read-only, reserved for JNI
> +    private long mInternalSurface = 0; // Read-only, reserved for JNI
> +
>       private MediaList mMediaList; // Pointer to media list being followed
>       private MediaList mPrimaryList; // Primary/default media list; see getPrimaryMediaList()
>   
> @@ -128,7 +131,7 @@ public class LibVLC {
>       public static LibVLC getInstance() throws LibVlcException {
>           synchronized (LibVLC.class) {
>               if (sInstance == null) {
> -                /* First call */
> +                // First call //

Unneeded change.

>                   sInstance = new LibVLC();
>               }
>           }
> @@ -149,10 +152,9 @@ public class LibVLC {
>       }
>   
>       /**
> -     * Constructor
> -     * It is private because this class is a singleton.
> +     * Constructor.
>        */
> -    private LibVLC() {
> +    public LibVLC() {
>           mAout = new AudioOutput();
>       }
>   
> @@ -218,7 +220,7 @@ public class LibVLC {
>        */
>       public native void setSurface(Surface f);
>   
> -    public static synchronized void restart(Context context) {
> +    public static synchronized void restartInstance(Context context) {
>           if (sInstance != null) {
>               try {
>                   sInstance.destroy();
> @@ -226,6 +228,15 @@ public class LibVLC {
>               } catch (LibVlcException lve) {
>                   Log.e(TAG, "Unable to reinit libvlc: " + lve);
>               }
> +       }
> +    }
> +
> +    public synchronized void restart(Context context) {
> +        try {
> +            this.destroy();
> +            this.init(context);
> +        } catch (LibVlcException lve) {
> +            Log.e(TAG, "Unable to reinit libvlc: " + lve);
>           }
>       }
>   
> @@ -341,7 +352,7 @@ public class LibVLC {
>           applyEqualizer();
>       }
>   
> -    private void applyEqualizer()
> +    protected void applyEqualizer()

Why?

>       {
>           setNativeEqualizer(mInternalMediaPlayerInstance, this.equalizer);
>       }
> @@ -381,7 +392,9 @@ public class LibVLC {
>   
>               File cacheDir = context.getCacheDir();
>               mCachePath = (cacheDir != null) ? cacheDir.getAbsolutePath() : null;
> -            nativeInit();
> +            synchronized (LibVLC.class) {
> +                nativeInit();
> +            }

Why?

>               mMediaList = mPrimaryList = new MediaList(this);
>               setEventHandler(EventHandler.getInstance());
>               mIsInitialized = true;
> @@ -394,7 +407,9 @@ public class LibVLC {
>        */
>       public void destroy() {
>           Log.v(TAG, "Destroying LibVLC instance");
> -        nativeDestroy();
> +        synchronized (LibVLC.class) {
> +            nativeDestroy();
> +        }

Ditto.

>           detachEventHandler();
>           mIsInitialized = false;
>       }
> @@ -647,7 +662,7 @@ public class LibVLC {
>       public native int getSpuTracksCount();
>   
>       public static native String nativeToURI(String path);
> -
> +

?

>       public native static void sendMouseEvent( int action, int button, int x, int y);
>   
>       /**
> diff --git a/vlc-android/src/org/videolan/libvlc/MediaList.java b/vlc-android/src/org/videolan/libvlc/MediaList.java
> index 85b2d2a..1889481 100644
> --- a/vlc-android/src/org/videolan/libvlc/MediaList.java
> +++ b/vlc-android/src/org/videolan/libvlc/MediaList.java
> @@ -32,7 +32,7 @@ public class MediaList {
>   
>       /* Since the libvlc_media_t is not created until the media plays, we have
>        * to cache them here. */
> -    private static class MediaHolder {
> +    private class MediaHolder {

Why?

>           Media m;
>           boolean noVideo; // default false
>           boolean noHardwareAcceleration; // default false
> diff --git a/vlc-android/src/org/videolan/vlc/gui/PreferencesActivity.java b/vlc-android/src/org/videolan/vlc/gui/PreferencesActivity.java
> index 4c9539f..eedadf5 100644
> --- a/vlc-android/src/org/videolan/vlc/gui/PreferencesActivity.java
> +++ b/vlc-android/src/org/videolan/vlc/gui/PreferencesActivity.java
> @@ -291,7 +291,7 @@ public class PreferencesActivity extends PreferenceActivity implements OnSharedP
>                   || key.equalsIgnoreCase("enable_verbose_mode")
>                   || key.equalsIgnoreCase("network_caching")) {
>               VLCInstance.updateLibVlcSettings(sharedPreferences);
> -            LibVLC.restart(this);
> +            LibVLC.restartInstance(this);
>           }
>       }
>   



More information about the Android mailing list