libvlcjni.c 31.7 KB
Newer Older
Rafaël Carré's avatar
Rafaël Carré committed
1 2 3
/*****************************************************************************
 * libvlcjni.c
 *****************************************************************************
Edward Wang's avatar
Edward Wang committed
4
 * Copyright © 2010-2013 VLC authors and VideoLAN
Rafaël Carré's avatar
Rafaël Carré committed
5
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
6 7 8
 * 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
Rafaël Carré's avatar
Rafaël Carré committed
9 10 11 12
 * (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
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
13 14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
Rafaël Carré's avatar
Rafaël Carré committed
15
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
16 17 18
 * 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.
Rafaël Carré's avatar
Rafaël Carré committed
19 20
 *****************************************************************************/

21
#include <assert.h>
22 23
#include <dirent.h>
#include <errno.h>
24
#include <string.h>
25
#include <pthread.h>
26
#include <sys/stat.h>
27
#include <sys/types.h>
28
#include <unistd.h>
29

30
#include <vlc/vlc.h>
31 32
#include <vlc_common.h>
#include <vlc_url.h>
33

34
#include <jni.h>
35

36
#include <android/api-level.h>
37

38
#include "libvlcjni.h"
39
#include "vout.h"
40
#include "utils.h"
41
#include "native_crash_handler.h"
42

43 44
#define VOUT_ANDROID_SURFACE 0
#define VOUT_OPENGLES2       1
45
#define VOUT_ANDROID_WINDOW  2
46

47 48 49
#define AOUT_AUDIOTRACK      0
#define AOUT_OPENSLES        1

50
#define LOG_TAG "VLC/JNI/main"
51 52
#include "log.h"

Thomas Guillem's avatar
Thomas Guillem committed
53 54
struct fields fields;

55 56
#define VLC_JNI_VERSION JNI_VERSION_1_2

57
#define THREAD_NAME "libvlcjni"
58
JNIEnv *jni_get_env(const char *name);
59

60 61 62 63 64 65 66 67 68 69 70 71
static void add_media_options(libvlc_media_t *p_md, JNIEnv *env, jobjectArray mediaOptions)
{
    int stringCount = (*env)->GetArrayLength(env, mediaOptions);
    for(int i = 0; i < stringCount; i++)
    {
        jstring option = (jstring)(*env)->GetObjectArrayElement(env, mediaOptions, i);
        const char* p_st = (*env)->GetStringUTFChars(env, option, 0);
        libvlc_media_add_option(p_md, p_st); // option
        (*env)->ReleaseStringUTFChars(env, option, p_st);
    }
}

72
libvlc_media_t *new_media(JNIEnv *env, jobject thiz, jstring fileLocation, bool noOmx, bool noVideo)
73
{
74
    libvlc_instance_t *libvlc = getLibVlcInstance(env, thiz);
75
    jboolean isCopy;
76 77 78
    const char *psz_location = (*env)->GetStringUTFChars(env, fileLocation, &isCopy);
    libvlc_media_t *p_md = libvlc_media_new_location(libvlc, psz_location);
    (*env)->ReleaseStringUTFChars(env, fileLocation, psz_location);
79 80 81
    if (!p_md)
        return NULL;

82 83 84 85 86 87 88 89 90
    jclass cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodId = (*env)->GetMethodID(env, cls, "getMediaOptions", "(ZZ)[Ljava/lang/String;");
    if (methodId != NULL)
    {
        jobjectArray mediaOptions = (*env)->CallObjectMethod(env, thiz, methodId, noOmx, noVideo);
        if (mediaOptions != NULL)
        {
            add_media_options(p_md, env, mediaOptions);
            (*env)->DeleteLocalRef(env, mediaOptions);
91
        }
92
    }
93
    return p_md;
94 95
}

96 97 98 99 100
libvlc_instance_t *getLibVlcInstance(JNIEnv *env, jobject thiz)
{
    return (libvlc_instance_t*)(intptr_t)getLong(env, thiz, "mLibVlcInstance");
}

101
libvlc_media_player_t *getMediaPlayer(JNIEnv *env, jobject thiz)
102
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
103
    return (libvlc_media_player_t*)(intptr_t)getLong(env, thiz, "mInternalMediaPlayerInstance");
104 105
}

106

Rafaël Carré's avatar
Rafaël Carré committed
107 108
static void releaseMediaPlayer(JNIEnv *env, jobject thiz)
{
109 110
    libvlc_media_player_t* p_mp = getMediaPlayer(env, thiz);
    if (p_mp)
111
    {
112 113
        libvlc_media_player_stop(p_mp);
        libvlc_media_player_release(p_mp);
114
        setLong(env, thiz, "mInternalMediaPlayerInstance", 0);
115 116 117
    }
}

118
static jobject eventHandlerInstance = NULL;
119 120 121 122 123

static void vlc_event_callback(const libvlc_event_t *ev, void *data)
{
    JNIEnv *env;

124
    if (eventHandlerInstance == NULL)
Sébastien Toque's avatar
Sébastien Toque committed
125
        return;
126

127 128
    if (!(env = jni_get_env(THREAD_NAME)))
        return;
129

130 131 132 133 134 135 136 137 138 139
    /* Creating the bundle in C allows us to subscribe to more events
     * and get better flexibility for each event. For example, we can
     * have totally different types of data for each event, instead of,
     * for example, only an integer and/or string.
     */
    jclass clsBundle = (*env)->FindClass(env, "android/os/Bundle");
    jmethodID clsCtor = (*env)->GetMethodID(env, clsBundle, "<init>", "()V" );
    jobject bundle = (*env)->NewObject(env, clsBundle, clsCtor);

    jmethodID putInt = (*env)->GetMethodID(env, clsBundle, "putInt", "(Ljava/lang/String;I)V" );
140
    jmethodID putLong = (*env)->GetMethodID(env, clsBundle, "putLong", "(Ljava/lang/String;J)V" );
141
    jmethodID putFloat = (*env)->GetMethodID(env, clsBundle, "putFloat", "(Ljava/lang/String;F)V" );
142
    jmethodID putString = (*env)->GetMethodID(env, clsBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V" );
Thomas Guillem's avatar
Thomas Guillem committed
143
    (*env)->DeleteLocalRef(env, clsBundle);
144

145
    if (ev->type == libvlc_MediaPlayerPositionChanged) {
146 147 148
        jstring sData = (*env)->NewStringUTF(env, "data");
        (*env)->CallVoidMethod(env, bundle, putFloat, sData, ev->u.media_player_position_changed.new_position);
        (*env)->DeleteLocalRef(env, sData);
149 150
    } else if (ev->type == libvlc_MediaPlayerTimeChanged) {
        jstring sData = (*env)->NewStringUTF(env, "data");
151
        (*env)->CallVoidMethod(env, bundle, putLong, sData, ev->u.media_player_time_changed.new_time);
152
        (*env)->DeleteLocalRef(env, sData);
153
    } else if(ev->type == libvlc_MediaPlayerVout) {
154 155 156 157
        /* For determining the vout/ES track change */
        jstring sData = (*env)->NewStringUTF(env, "data");
        (*env)->CallVoidMethod(env, bundle, putInt, sData, ev->u.media_player_vout.new_count);
        (*env)->DeleteLocalRef(env, sData);
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
    } else if(ev->type == libvlc_MediaListItemAdded ||
              ev->type == libvlc_MediaListItemDeleted ) {
        jstring item_uri = (*env)->NewStringUTF(env, "item_uri");
        jstring item_index = (*env)->NewStringUTF(env, "item_index");
        char* mrl = libvlc_media_get_mrl(
            ev->type == libvlc_MediaListItemAdded ?
            ev->u.media_list_item_added.item :
            ev->u.media_list_item_deleted.item
            );
        jstring item_uri_value = (*env)->NewStringUTF(env, mrl);
        jint item_index_value;
        if(ev->type == libvlc_MediaListItemAdded)
            item_index_value = ev->u.media_list_item_added.index;
        else
            item_index_value = ev->u.media_list_item_deleted.index;

        (*env)->CallVoidMethod(env, bundle, putString, item_uri, item_uri_value);
        (*env)->CallVoidMethod(env, bundle, putInt, item_index, item_index_value);

        (*env)->DeleteLocalRef(env, item_uri);
        (*env)->DeleteLocalRef(env, item_uri_value);
        (*env)->DeleteLocalRef(env, item_index);
        free(mrl);
181 182 183 184 185
    } else if(ev->type == libvlc_MediaPlayerESAdded ||
              ev->type == libvlc_MediaPlayerESDeleted ) {
        jstring sData = (*env)->NewStringUTF(env, "data");
        (*env)->CallVoidMethod(env, bundle, putInt, sData, ev->u.media_player_es_changed.i_type);
        (*env)->DeleteLocalRef(env, sData);
186 187
    }

188
    /* Get the object class */
189
    jclass cls = (*env)->GetObjectClass(env, eventHandlerInstance);
190
    if (!cls) {
191
        LOGE("EventHandler: failed to get class reference");
192
        goto end;
193 194 195
    }

    /* Find the callback ID */
196
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(ILandroid/os/Bundle;)V");
197
    if (methodID) {
198
        (*env)->CallVoidMethod(env, eventHandlerInstance, methodID, ev->type, bundle);
199
    } else {
200
        LOGE("EventHandler: failed to get the callback method");
201
    }
Thomas Guillem's avatar
Thomas Guillem committed
202
    (*env)->DeleteLocalRef(env, cls);
203

204
end:
Edward Wang's avatar
Edward Wang committed
205
    (*env)->DeleteLocalRef(env, bundle);
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
}

/* Pointer to the Java virtual machine
 * Note: It's okay to use a static variable for the VM pointer since there
 * can only be one instance of this shared library in a single VM
 */
static JavaVM *myVm;
static pthread_key_t jni_env_key;

/* This function is called when a thread attached to the Java VM is canceled or
 * exited */
static void jni_detach_thread(void *data)
{
    //JNIEnv *env = data;
    (*myVm)->DetachCurrentThread(myVm);
}

JNIEnv *jni_get_env(const char *name)
{
    JNIEnv *env;

    env = pthread_getspecific(jni_env_key);
    if (env == NULL) {
        /* if GetEnv returns JNI_OK, the thread is already attached to the
         * JavaVM, so we are already in a java thread, and we don't have to
         * setup any destroy callbacks */
        if ((*myVm)->GetEnv(myVm, (void **)&env, VLC_JNI_VERSION) != JNI_OK)
        {
            /* attach the thread to the Java VM */
            JavaVMAttachArgs args;
            jint result;

            args.version = VLC_JNI_VERSION;
            args.name = name;
            args.group = NULL;

            if ((*myVm)->AttachCurrentThread(myVm, &env, &args) != JNI_OK)
                return NULL;

            /* Set the attached env to the thread-specific data area (TSD) */
            if (pthread_setspecific(jni_env_key, env) != 0)
            {
                (*myVm)->DetachCurrentThread(myVm);
                return NULL;
            }
        }
    }

    return env;
255 256
}

257
jint JNI_OnLoad(JavaVM *vm, void *reserved)
258
{
Thomas Guillem's avatar
Thomas Guillem committed
259
    JNIEnv* env = NULL;
260 261 262
    // Keep a reference on the Java VM.
    myVm = vm;

Thomas Guillem's avatar
Thomas Guillem committed
263 264
    if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
        return -1;
265 266 267 268 269 270

    /* Create a TSD area and setup a destroy callback when a thread that
     * previously set the jni_env_key is canceled or exited */
    if (pthread_key_create(&jni_env_key, jni_detach_thread) != 0)
        return -1;

271
    pthread_mutex_init(&vout_android_lock, NULL);
272
    pthread_cond_init(&vout_android_surf_attached, NULL);
273

274

Thomas Guillem's avatar
Thomas Guillem committed
275
#define GET_CLASS(clazz, str, b_globlal) do { \
Thomas Guillem's avatar
Thomas Guillem committed
276 277 278 279 280
    (clazz) = (*env)->FindClass(env, (str)); \
    if (!(clazz)) { \
        LOGE("FindClass(%s) failed", (str)); \
        return -1; \
    } \
Thomas Guillem's avatar
Thomas Guillem committed
281 282 283 284 285 286
    if (b_globlal) { \
        (clazz) = (jclass) (*env)->NewGlobalRef(env, (clazz)); \
        if (!(clazz)) { \
            LOGE("NewGlobalRef(%s) failed", (str)); \
            return -1; \
        } \
Thomas Guillem's avatar
Thomas Guillem committed
287 288 289 290 291 292 293 294 295 296 297
    } \
} 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)

Thomas Guillem's avatar
Thomas Guillem committed
298 299 300 301 302 303 304 305
    jclass Version_clazz;
    jfieldID SDK_INT_fieldID;

    GET_CLASS(Version_clazz, "android/os/Build$VERSION", false);
    GET_ID(GetStaticFieldID, SDK_INT_fieldID, Version_clazz, "SDK_INT", "I");
    fields.SDK_INT = (*env)->GetStaticIntField(env, Version_clazz,
                                               SDK_INT_fieldID);

Thomas Guillem's avatar
Thomas Guillem committed
306
    GET_CLASS(fields.IllegalStateException.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
307
              "java/lang/IllegalStateException", true);
Thomas Guillem's avatar
Thomas Guillem committed
308
    GET_CLASS(fields.IllegalArgumentException.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
309
              "java/lang/IllegalArgumentException", true);
Thomas Guillem's avatar
Thomas Guillem committed
310
    GET_CLASS(fields.String.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
311
              "java/lang/String", true);
312
    GET_CLASS(fields.LibVLC.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
313
              "org/videolan/libvlc/LibVLC", true);
Thomas Guillem's avatar
Thomas Guillem committed
314
    GET_CLASS(fields.VLCObject.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
315
              "org/videolan/libvlc/VLCObject", true);
316
    GET_CLASS(fields.Media.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
317
              "org/videolan/libvlc/Media", true);
318
    GET_CLASS(fields.Media.Track.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
319
              "org/videolan/libvlc/Media$Track", true);
Thomas Guillem's avatar
Thomas Guillem committed
320

321 322 323 324 325
    GET_ID(GetStaticMethodID,
           fields.LibVLC.onNativeCrashID,
           fields.LibVLC.clazz,
           "onNativeCrash", "()V");

Thomas Guillem's avatar
Thomas Guillem committed
326 327 328 329 330 331 332 333 334 335
    GET_ID(GetFieldID,
           fields.VLCObject.mInstanceID,
           fields.VLCObject.clazz,
           "mInstance", "J");

    GET_ID(GetMethodID,
           fields.VLCObject.dispatchEventFromNativeID,
           fields.VLCObject.clazz,
           "dispatchEventFromNative", "(IJJ)V");

Thomas Guillem's avatar
Thomas Guillem committed
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    if (fields.SDK_INT <= 7)
    {
        LOGE("fields.SDK_INT is less than 7: using compat WeakReference");
        GET_ID(GetMethodID,
               fields.VLCObject.getWeakReferenceID,
               fields.VLCObject.clazz,
               "getWeakReference", "()Ljava/lang/Object;");
        GET_ID(GetStaticMethodID,
               fields.VLCObject.dispatchEventFromWeakNativeID,
               fields.VLCObject.clazz,
               "dispatchEventFromWeakNative", "(Ljava/lang/Object;IJJ)V");
    } else
    {
        fields.VLCObject.getWeakReferenceID = NULL;
        fields.VLCObject.dispatchEventFromWeakNativeID = NULL;
    }

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
    GET_ID(GetStaticMethodID,
           fields.Media.createAudioTrackFromNativeID,
           fields.Media.clazz,
           "createAudioTrackFromNative",
           "(Ljava/lang/String;Ljava/lang/String;IIIILjava/lang/String;Ljava/lang/String;II)"
           "Lorg/videolan/libvlc/Media$Track;");

    GET_ID(GetStaticMethodID,
           fields.Media.createVideoTrackFromNativeID,
           fields.Media.clazz,
           "createVideoTrackFromNative",
           "(Ljava/lang/String;Ljava/lang/String;IIIILjava/lang/String;Ljava/lang/String;IIIIII)"
           "Lorg/videolan/libvlc/Media$Track;");

    GET_ID(GetStaticMethodID,
           fields.Media.createSubtitleTrackFromNativeID,
           fields.Media.clazz,
           "createSubtitleTrackFromNative",
           "(Ljava/lang/String;Ljava/lang/String;IIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)"
           "Lorg/videolan/libvlc/Media$Track;");

Thomas Guillem's avatar
Thomas Guillem committed
374 375 376
#undef GET_CLASS
#undef GET_ID

377 378
    init_native_crash_handler();

379
    LOGD("JNI interface loaded.");
380
    return VLC_JNI_VERSION;
381 382
}

Thomas Guillem's avatar
Thomas Guillem committed
383 384 385 386
void JNI_OnUnload(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;

387
    pthread_mutex_destroy(&vout_android_lock);
388
    pthread_cond_destroy(&vout_android_surf_attached);
Thomas Guillem's avatar
Thomas Guillem committed
389

390 391
    destroy_native_crash_handler();

Thomas Guillem's avatar
Thomas Guillem committed
392 393 394 395 396 397 398
    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);
399
    (*env)->DeleteGlobalRef(env, fields.Media.clazz);
400

401
    pthread_key_delete(jni_env_key);
402 403
}

404
void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
405
{
406 407 408
    //only use OpenSLES if java side says we can
    jclass cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodId = (*env)->GetMethodID(env, cls, "getAout", "()I");
Thomas Guillem's avatar
Thomas Guillem committed
409
    int aout = (*env)->CallIntMethod(env, thiz, methodId);
410

411
    methodId = (*env)->GetMethodID(env, cls, "getVout", "()I");
412
    int vout = (*env)->CallIntMethod(env, thiz, methodId);
413

414 415 416
    methodId = (*env)->GetMethodID(env, cls, "timeStretchingEnabled", "()Z");
    bool enable_time_stretch = (*env)->CallBooleanMethod(env, thiz, methodId);

Edward Wang's avatar
Edward Wang committed
417 418 419
    methodId = (*env)->GetMethodID(env, cls, "frameSkipEnabled", "()Z");
    bool enable_frame_skip = (*env)->CallBooleanMethod(env, thiz, methodId);

Edward Wang's avatar
Edward Wang committed
420 421
    methodId = (*env)->GetMethodID(env, cls, "getDeblocking", "()I");
    int deblocking = (*env)->CallIntMethod(env, thiz, methodId);
Rafaël Carré's avatar
Rafaël Carré committed
422 423
    char deblockstr[2];
    snprintf(deblockstr, sizeof(deblockstr), "%d", deblocking);
Edward Wang's avatar
Edward Wang committed
424
    LOGD("Using deblocking level %d", deblocking);
425

Edward Wang's avatar
Edward Wang committed
426 427
    methodId = (*env)->GetMethodID(env, cls, "getNetworkCaching", "()I");
    int networkCaching = (*env)->CallIntMethod(env, thiz, methodId);
Rafaël Carré's avatar
Rafaël Carré committed
428
    char networkCachingstr[25];
Edward Wang's avatar
Edward Wang committed
429
    if(networkCaching > 0) {
Rafaël Carré's avatar
Rafaël Carré committed
430
        snprintf(networkCachingstr, sizeof(networkCachingstr), "--network-caching=%d", networkCaching);
Edward Wang's avatar
Edward Wang committed
431 432 433
        LOGD("Using network caching of %d ms", networkCaching);
    }

434 435 436
    methodId = (*env)->GetMethodID(env, cls, "getHttpReconnect", "()Z");
    bool enable_http_reconnect = (*env)->CallBooleanMethod(env, thiz, methodId);

437 438 439 440
    methodId = (*env)->GetMethodID(env, cls, "getChroma", "()Ljava/lang/String;");
    jstring chroma = (*env)->CallObjectMethod(env, thiz, methodId);
    const char *chromastr = (*env)->GetStringUTFChars(env, chroma, 0);
    LOGD("Chroma set to \"%s\"", chromastr);
Edward Wang's avatar
Edward Wang committed
441

442 443 444
    methodId = (*env)->GetMethodID(env, cls, "getSubtitlesEncoding", "()Ljava/lang/String;");
    jstring subsencoding = (*env)->CallObjectMethod(env, thiz, methodId);
    const char *subsencodingstr = (*env)->GetStringUTFChars(env, subsencoding, 0);
Edward Wang's avatar
Edward Wang committed
445
    LOGD("Subtitle encoding set to \"%s\"", subsencodingstr);
446

447
    methodId = (*env)->GetMethodID(env, cls, "isVerboseMode", "()Z");
448
    bool b_verbose = (*env)->CallBooleanMethod(env, thiz, methodId);
449

450 451
    methodId = (*env)->GetMethodID(env, cls, "isDirectRendering", "()Z");
    bool direct_rendering = (*env)->CallBooleanMethod(env, thiz, methodId);
452
    /* With the MediaCodec opaque mode we cannot use the OpenGL ES vout. */
453
    if (direct_rendering)
454
        vout = VOUT_ANDROID_WINDOW;
455

flx42's avatar
flx42 committed
456 457
    methodId = (*env)->GetMethodID(env, cls, "getCachePath", "()Ljava/lang/String;");
    jstring cachePath = (*env)->CallObjectMethod(env, thiz, methodId);
458 459 460 461 462
    if (cachePath) {
        const char *cache_path = (*env)->GetStringUTFChars(env, cachePath, 0);
        setenv("DVDCSS_CACHE", cache_path, 1);
        (*env)->ReleaseStringUTFChars(env, cachePath, cache_path);
    }
flx42's avatar
flx42 committed
463

464
#define MAX_ARGV 20
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
    const char *argv[MAX_ARGV];
    int argc = 0;

    /* CPU intensive plugin, setting for slow devices */
    argv[argc++] = enable_time_stretch ? "--audio-time-stretch" : "--no-audio-time-stretch";
    /* avcodec-skiploopfilter */
    argv[argc++] = "--avcodec-skiploopfilter";
    argv[argc++] = deblockstr;
    /* avcodec-skip-frame */
    argv[argc++] = "--avcodec-skip-frame";
    argv[argc++] = enable_frame_skip ? "2" : "0";
    /* avcodec-skip-idct */
    argv[argc++] = "--avcodec-skip-idct";
    argv[argc++] = enable_frame_skip ? "2" : "0";
    /* Remove me when UTF-8 is enforced by law */
    argv[argc++] = "--subsdec-encoding";
    argv[argc++] = subsencodingstr;
    /* Enable statistics */
    argv[argc++] = "--stats";
    /* XXX: why can't the default be fine ? #7792 */
    if (networkCaching > 0)
        argv[argc++] = networkCachingstr;
    /* Android audio API */
    argv[argc++] = aout == AOUT_OPENSLES ? "--aout=opensles" :
        (aout == AOUT_AUDIOTRACK ? "--aout=android_audiotrack" : "--aout=dummy");
    /* Android video API  */
    argv[argc++] = vout == VOUT_ANDROID_WINDOW ? "--vout=androidwindow" :
        (vout == VOUT_OPENGLES2 ? "--vout=gles2" : "--vout=androidsurface");
    /* chroma */
    argv[argc++] = "--androidsurface-chroma";
    argv[argc++] = chromastr != NULL && chromastr[0] != 0 ? chromastr : "RV32";
    /* direct rendering */
    if (!direct_rendering) {
        argv[argc++] = "--no-mediacodec-dr";
#ifdef HAVE_IOMX_DR
        argv[argc++] = "--no-omxil-dr";
#endif
    }
503
    argv[argc++] = "--spdif";
504 505
    argv[argc++] = b_verbose ? "-vvv" : "-vv";

506 507 508 509 510 511
    /* Reconnect on lost HTTP streams, e.g. network change */
    if (enable_http_reconnect)
        argv[argc++] = "--http-reconnect";

    assert(MAX_ARGV >= argc);
    libvlc_instance_t *instance = libvlc_new(argc, argv);
512

Ludovic Fauvet's avatar
Ludovic Fauvet committed
513
    setLong(env, thiz, "mLibVlcInstance", (jlong)(intptr_t) instance);
514

515
    (*env)->ReleaseStringUTFChars(env, chroma, chromastr);
516 517
    (*env)->ReleaseStringUTFChars(env, subsencoding, subsencodingstr);

518 519
    if (!instance)
    {
520
        jclass exc = (*env)->FindClass(env, "org/videolan/libvlc/LibVlcException");
521 522 523 524
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
525 526
}

527
void Java_org_videolan_libvlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
528
{
529
    releaseMediaPlayer(env, thiz);
Edward Wang's avatar
Edward Wang committed
530
    jlong libVlcInstance = getLong(env, thiz, "mLibVlcInstance");
531 532 533
    if (!libVlcInstance)
        return; // Already destroyed

Ludovic Fauvet's avatar
Ludovic Fauvet committed
534
    libvlc_instance_t *instance = (libvlc_instance_t*)(intptr_t) libVlcInstance;
535 536
    libvlc_release(instance);

Edward Wang's avatar
Edward Wang committed
537
    setLong(env, thiz, "mLibVlcInstance", 0);
538 539
}

540
void Java_org_videolan_libvlc_LibVLC_detachEventHandler(JNIEnv *env, jobject thiz)
541
{
542 543 544
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
545 546 547
    }
}

548
void Java_org_videolan_libvlc_LibVLC_setEventHandler(JNIEnv *env, jobject thiz, jobject eventHandler)
549
{
550 551 552
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
553 554
    }

555
    eventHandlerInstance = getEventHandlerReference(env, thiz, eventHandler);
556 557
}

558
void Java_org_videolan_libvlc_LibVLC_playMRL(JNIEnv *env, jobject thiz,
559 560
                                             jstring mrl, jobjectArray mediaOptions)
{
561
    jclass cls;
562 563 564
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

565 566
    libvlc_instance_t *p_instance = getLibVlcInstance(env, thiz);

567
    /* Create a media player playing environment */
568
    libvlc_media_player_t *mp = libvlc_media_player_new(p_instance);
Rafaël Carré's avatar
Rafaël Carré committed
569
    libvlc_media_player_set_video_title_display(mp, libvlc_position_disable, 0);
570
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz); // freed in aout_close
571

572 573
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
574 575 576 577 578
    static const libvlc_event_type_t mp_events[] = {
        libvlc_MediaPlayerPlaying,
        libvlc_MediaPlayerPaused,
        libvlc_MediaPlayerEndReached,
        libvlc_MediaPlayerStopped,
579
        libvlc_MediaPlayerVout,
580
        libvlc_MediaPlayerPositionChanged,
581
        libvlc_MediaPlayerTimeChanged,
582 583 584
        libvlc_MediaPlayerEncounteredError,
        libvlc_MediaPlayerESAdded,
        libvlc_MediaPlayerESDeleted,
585
    };
586
    for(int i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); i++)
587 588
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

589
    /* Keep a pointer to this media player */
Ludovic Fauvet's avatar
Ludovic Fauvet committed
590
    setLong(env, thiz, "mInternalMediaPlayerInstance", (jlong)(intptr_t)mp);
591

592 593 594 595
    cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodID = (*env)->GetMethodID(env, cls, "applyEqualizer", "()V");
    (*env)->CallVoidMethod(env, thiz, methodID);

596 597
    const char* p_mrl = (*env)->GetStringUTFChars(env, mrl, 0);

598
    libvlc_media_t* p_md = libvlc_media_new_location(p_instance, p_mrl);
599
    /* media options */
600
    if (mediaOptions != NULL)
601
        add_media_options(p_md, env, mediaOptions);
602

603 604
    (*env)->ReleaseStringUTFChars(env, mrl, p_mrl);

605 606 607
    /* Connect the media event manager. */
    libvlc_event_manager_t *ev_media = libvlc_media_event_manager(p_md);
    static const libvlc_event_type_t mp_media_events[] = {
Alexandre Perraud's avatar
Alexandre Perraud committed
608 609
        libvlc_MediaParsedChanged,
        libvlc_MediaMetaChanged,
610 611 612 613
    };
    for(int i = 0; i < (sizeof(mp_media_events) / sizeof(*mp_media_events)); i++)
        libvlc_event_attach(ev_media, mp_media_events[i], vlc_event_callback, myVm);

614 615
    libvlc_media_player_set_media(mp, p_md);
    libvlc_media_player_play(mp);
616 617
}

618
jfloat Java_org_videolan_libvlc_LibVLC_getRate(JNIEnv *env, jobject thiz) {
Edward Wang's avatar
Edward Wang committed
619 620 621 622 623 624 625
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        return libvlc_media_player_get_rate(mp);
    else
        return 1.00;
}

626
void Java_org_videolan_libvlc_LibVLC_setRate(JNIEnv *env, jobject thiz, jfloat rate) {
Edward Wang's avatar
Edward Wang committed
627 628 629 630 631
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        libvlc_media_player_set_rate(mp, rate);
}

632
jboolean Java_org_videolan_libvlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
633
{
634
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
635
    if (mp)
636
        return !!libvlc_media_player_is_playing(mp);
637 638
    else
        return 0;
639 640
}

641
jboolean Java_org_videolan_libvlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
642
{
643 644 645
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_seekable(mp);
646
    return 0;
647 648
}

649
void Java_org_videolan_libvlc_LibVLC_play(JNIEnv *env, jobject thiz)
650
{
651
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
652
    if (mp)
653
        libvlc_media_player_play(mp);
654 655
}

656
void Java_org_videolan_libvlc_LibVLC_pause(JNIEnv *env, jobject thiz)
657
{
658
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
659
    if (mp)
660
        libvlc_media_player_pause(mp);
661 662
}

663
void Java_org_videolan_libvlc_LibVLC_stop(JNIEnv *env, jobject thiz)
664
{
665
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
666
    if (mp)
667
        libvlc_media_player_stop(mp);
668 669
}

670 671 672 673 674 675 676 677
jint Java_org_videolan_libvlc_LibVLC_getPlayerState(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_media_player_get_state(mp);
    return -1;
}

678
jint Java_org_videolan_libvlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
679
{
680 681 682
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
683 684 685
    return -1;
}

686
jint Java_org_videolan_libvlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
687
{
688 689
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
690
        //Returns 0 if the volume was set, -1 if it was out of range or error
691
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
692 693 694
    return -1;
}

695
jlong Java_org_videolan_libvlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
696
{
697 698 699
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
700 701 702
    return -1;
}

703
void Java_org_videolan_libvlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
704
{
705 706 707
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
708 709
}

710
jfloat Java_org_videolan_libvlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
711
{
712 713 714
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
715 716 717
    return -1;
}

718
void Java_org_videolan_libvlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
719
{
720 721 722
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
723 724
}

725
jlong Java_org_videolan_libvlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
726
{
727 728 729
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
730 731 732
    return -1;
}

733
jstring Java_org_videolan_libvlc_LibVLC_version(JNIEnv* env, jobject thiz)
734
{
735 736
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
737

738
jstring Java_org_videolan_libvlc_LibVLC_compiler(JNIEnv* env, jobject thiz)
739 740 741 742
{
    return (*env)->NewStringUTF(env, libvlc_get_compiler());
}

743
jstring Java_org_videolan_libvlc_LibVLC_changeset(JNIEnv* env, jobject thiz)
744 745 746
{
    return (*env)->NewStringUTF(env, libvlc_get_changeset());
}
747

748 749 750
jstring Java_org_videolan_libvlc_LibVLC_getMeta(JNIEnv *env, jobject thiz, int meta)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
751 752
    char *psz_meta;
    jstring string = NULL;
753 754 755 756 757 758 759
    if (!mp)
        return NULL;

    libvlc_media_t *p_mp = libvlc_media_player_get_media(mp);
    if (!p_mp)
        return NULL;

760 761 762 763 764
    psz_meta = libvlc_media_get_meta(p_mp, meta);
    if (psz_meta) {
        string = (*env)->NewStringUTF(env, psz_meta);
        free(psz_meta);
    }
765
    libvlc_media_release(p_mp);
766
    return string;
767 768
}

769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
jint Java_org_videolan_libvlc_LibVLC_getTitle(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_title(mp);
    return -1;
}

void Java_org_videolan_libvlc_LibVLC_setTitle(JNIEnv *env, jobject thiz, jint title)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_title(mp, title);
}

784 785 786 787 788 789 790 791
jint Java_org_videolan_libvlc_LibVLC_getChapterCount(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_chapter_count(mp);
    return -1;
}

792 793 794 795 796 797 798
jint Java_org_videolan_libvlc_LibVLC_getChapterCountForTitle(JNIEnv *env, jobject thiz, jint title)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_chapter_count_for_title(mp, title);
    return -1;
}
Edward Wang's avatar
Edward Wang committed
799

800 801 802 803 804 805 806 807
jint Java_org_videolan_libvlc_LibVLC_getChapter(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_chapter(mp);
    return -1;
}

808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
jstring Java_org_videolan_libvlc_LibVLC_getChapterDescription(JNIEnv *env, jobject thiz, jint title)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    libvlc_track_description_t *description;
    jstring string = NULL;
    if (!mp)
        return NULL;
    description = libvlc_video_get_chapter_description(mp, title);
    if (description) {
        string = (*env)->NewStringUTF(env, description->psz_name);
        free(description);
    }
    return string;
}

823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
void Java_org_videolan_libvlc_LibVLC_setChapter(JNIEnv *env, jobject thiz, jint chapter)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_chapter(mp, chapter);
}

void Java_org_videolan_libvlc_LibVLC_previousChapter(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_previous_chapter(mp);
}

void Java_org_videolan_libvlc_LibVLC_nextChapter(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_next_chapter(mp);
}

Edward Wang's avatar
Edward Wang committed
844 845 846 847 848 849 850
jint Java_org_videolan_libvlc_LibVLC_getTitleCount(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_title_count(mp);
    return -1;
}
Geoffrey Métais's avatar
Geoffrey Métais committed
851 852 853 854 855 856 857 858 859

void Java_org_videolan_libvlc_LibVLC_playerNavigate(JNIEnv *env, jobject thiz, jint navigate)
{
    unsigned nav = navigate;
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_navigate(mp, (unsigned) nav);
}

860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
static int expand_media_internal(JNIEnv *env, libvlc_instance_t* p_instance, jobject arrayList, libvlc_media_t* p_md) {
    if(!p_md) {
        return -1;
    }
    libvlc_media_list_t* p_subitems = libvlc_media_subitems(p_md);
    libvlc_media_release(p_md);
    if(p_subitems) {
        // Expand any subitems if needed
        int subitem_count = libvlc_media_list_count(p_subitems);
        if(subitem_count > 0) {
            LOGD("Found %d subitems, expanding", subitem_count);
            jclass arrayListClass; jmethodID methodAdd;
            arrayListGetIDs(env, &arrayListClass, &methodAdd, NULL);

            for(int i = subitem_count - 1; i >= 0; i--) {
                libvlc_media_t* p_subitem = libvlc_media_list_item_at_index(p_subitems, i);
                char* p_subitem_uri = libvlc_media_get_mrl(p_subitem);
                arrayListStringAdd(env, arrayListClass, methodAdd, arrayList, p_subitem_uri);
                free(p_subitem_uri);
            }
        }
        libvlc_media_list_release(p_subitems);
        if(subitem_count > 0) {
            return 0;
        } else {
            return -1;
        }
    } else {
        return -1;
    }
}

jint Java_org_videolan_libvlc_LibVLC_expandMedia(JNIEnv *env, jobject thiz, jobject children) {
893 894 895 896 897 898
    jint ret;
    libvlc_media_t *p_md = libvlc_media_player_get_media(getMediaPlayer(env, thiz));

    if (!p_md)
        return -1;
    ret = (jint)expand_media_internal(env,
899 900
        getLibVlcInstance(env, thiz),
        children,
901 902 903
        p_md);
    libvlc_media_release(p_md);
    return ret;
904 905
}

Thomas Guillem's avatar
Thomas Guillem committed
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
// TODO: remove static variables
static int i_window_width = 0;
static int i_window_height = 0;

void Java_org_videolan_libvlc_LibVLC_setWindowSize(JNIEnv *env, jobject thiz, jint width, jint height)
{
    pthread_mutex_lock(&vout_android_lock);
    i_window_width = width;
    i_window_height = height;
    pthread_mutex_unlock(&vout_android_lock);
}

int jni_GetWindowSize(int *width, int *height)
{
    pthread_mutex_lock(&vout_android_lock);
    *width = i_window_width;
    *height = i_window_height;
    pthread_mutex_unlock(&vout_android_lock);
    return 0;
}
926 927 928 929 930

/* used by opensles module */
int aout_get_native_sample_rate(void)
{
    JNIEnv *p_env;
931 932
    if (!(p_env = jni_get_env(THREAD_NAME)))
        return -1;
933 934 935