libvlcjni.c 31.9 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

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);
    }
463

464 465 466 467
    methodId = (*env)->GetMethodID(env, cls, "isHdmiAudioEnabled", "()Z");
    bool hdmi_audio = (*env)->CallBooleanMethod(env, thiz, methodId);

#define MAX_ARGV 22
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 503 504 505
    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
    }
506 507 508 509 510
    if (hdmi_audio) {
        argv[argc++] = "--spdif";
        argv[argc++] = "--audiotrack-audio-channels";
        argv[argc++] = "8"; // 7.1 maximum
    }
511 512
    argv[argc++] = b_verbose ? "-vvv" : "-vv";

513 514 515 516 517 518
    /* 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);
519

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

522
    (*env)->ReleaseStringUTFChars(env, chroma, chromastr);
523 524
    (*env)->ReleaseStringUTFChars(env, subsencoding, subsencodingstr);

525 526
    if (!instance)
    {
527
        jclass exc = (*env)->FindClass(env, "org/videolan/libvlc/LibVlcException");
528 529 530 531
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
532 533
}

534
void Java_org_videolan_libvlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
535
{
536
    releaseMediaPlayer(env, thiz);
Edward Wang's avatar
Edward Wang committed
537
    jlong libVlcInstance = getLong(env, thiz, "mLibVlcInstance");
538 539 540
    if (!libVlcInstance)
        return; // Already destroyed

Ludovic Fauvet's avatar
Ludovic Fauvet committed
541
    libvlc_instance_t *instance = (libvlc_instance_t*)(intptr_t) libVlcInstance;
542 543
    libvlc_release(instance);

Edward Wang's avatar
Edward Wang committed
544
    setLong(env, thiz, "mLibVlcInstance", 0);
545 546
}

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

555
void Java_org_videolan_libvlc_LibVLC_setEventHandler(JNIEnv *env, jobject thiz, jobject eventHandler)
556
{
557 558 559
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
560 561
    }

562
    eventHandlerInstance = getEventHandlerReference(env, thiz, eventHandler);
563 564
}

565
void Java_org_videolan_libvlc_LibVLC_playMRL(JNIEnv *env, jobject thiz,
566 567
                                             jstring mrl, jobjectArray mediaOptions)
{
568
    jclass cls;
569 570 571
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

572 573
    libvlc_instance_t *p_instance = getLibVlcInstance(env, thiz);

574
    /* Create a media player playing environment */
575
    libvlc_media_player_t *mp = libvlc_media_player_new(p_instance);
Rafaël Carré's avatar
Rafaël Carré committed
576
    libvlc_media_player_set_video_title_display(mp, libvlc_position_disable, 0);
577
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz); // freed in aout_close
578

579 580
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
581 582 583 584 585
    static const libvlc_event_type_t mp_events[] = {
        libvlc_MediaPlayerPlaying,
        libvlc_MediaPlayerPaused,
        libvlc_MediaPlayerEndReached,
        libvlc_MediaPlayerStopped,
586
        libvlc_MediaPlayerVout,
587
        libvlc_MediaPlayerPositionChanged,
588
        libvlc_MediaPlayerTimeChanged,
589 590 591
        libvlc_MediaPlayerEncounteredError,
        libvlc_MediaPlayerESAdded,
        libvlc_MediaPlayerESDeleted,
592
    };
593
    for(int i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); i++)
594 595
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

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

599 600 601 602
    cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodID = (*env)->GetMethodID(env, cls, "applyEqualizer", "()V");
    (*env)->CallVoidMethod(env, thiz, methodID);

603 604
    const char* p_mrl = (*env)->GetStringUTFChars(env, mrl, 0);

605
    libvlc_media_t* p_md = libvlc_media_new_location(p_instance, p_mrl);
606
    /* media options */
607
    if (mediaOptions != NULL)
608
        add_media_options(p_md, env, mediaOptions);
609

610 611
    (*env)->ReleaseStringUTFChars(env, mrl, p_mrl);

612 613 614
    /* 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
615 616
        libvlc_MediaParsedChanged,
        libvlc_MediaMetaChanged,
617 618 619 620
    };
    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);

621 622
    libvlc_media_player_set_media(mp, p_md);
    libvlc_media_player_play(mp);
623 624
}

625
jfloat Java_org_videolan_libvlc_LibVLC_getRate(JNIEnv *env, jobject thiz) {
Edward Wang's avatar
Edward Wang committed
626 627 628 629 630 631 632
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        return libvlc_media_player_get_rate(mp);
    else
        return 1.00;
}

633
void Java_org_videolan_libvlc_LibVLC_setRate(JNIEnv *env, jobject thiz, jfloat rate) {
Edward Wang's avatar
Edward Wang committed
634 635 636 637 638
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        libvlc_media_player_set_rate(mp, rate);
}

639
jboolean Java_org_videolan_libvlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
640
{
641
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
642
    if (mp)
643
        return !!libvlc_media_player_is_playing(mp);
644 645
    else
        return 0;
646 647
}

648
jboolean Java_org_videolan_libvlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
649
{
650 651 652
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_seekable(mp);
653
    return 0;
654 655
}

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

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

670
void Java_org_videolan_libvlc_LibVLC_stop(JNIEnv *env, jobject thiz)
671
{
672
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
673
    if (mp)
674
        libvlc_media_player_stop(mp);
675 676
}

677 678 679 680 681 682 683 684
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;
}

685
jint Java_org_videolan_libvlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
686
{
687 688 689
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
690 691 692
    return -1;
}

693
jint Java_org_videolan_libvlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
694
{
695 696
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
697
        //Returns 0 if the volume was set, -1 if it was out of range or error
698
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
699 700 701
    return -1;
}

702
jlong Java_org_videolan_libvlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
703
{
704 705 706
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
707 708 709
    return -1;
}

710
void Java_org_videolan_libvlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
711
{
712 713 714
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
715 716
}

717
jfloat Java_org_videolan_libvlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
718
{
719 720 721
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
722 723 724
    return -1;
}

725
void Java_org_videolan_libvlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
726
{
727 728 729
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
730 731
}

732
jlong Java_org_videolan_libvlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
733
{
734 735 736
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
737 738 739
    return -1;
}

740
jstring Java_org_videolan_libvlc_LibVLC_version(JNIEnv* env, jobject thiz)
741
{
742 743
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
744

745
jstring Java_org_videolan_libvlc_LibVLC_compiler(JNIEnv* env, jobject thiz)
746 747 748 749
{
    return (*env)->NewStringUTF(env, libvlc_get_compiler());
}

750
jstring Java_org_videolan_libvlc_LibVLC_changeset(JNIEnv* env, jobject thiz)
751 752 753
{
    return (*env)->NewStringUTF(env, libvlc_get_changeset());
}
754

755 756 757
jstring Java_org_videolan_libvlc_LibVLC_getMeta(JNIEnv *env, jobject thiz, int meta)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
758 759
    char *psz_meta;
    jstring string = NULL;
760 761 762 763 764 765 766
    if (!mp)
        return NULL;

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

767 768 769 770 771
    psz_meta = libvlc_media_get_meta(p_mp, meta);
    if (psz_meta) {
        string = (*env)->NewStringUTF(env, psz_meta);
        free(psz_meta);
    }
772
    libvlc_media_release(p_mp);
773
    return string;
774 775
}

776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
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);
}

791 792 793 794 795 796 797 798
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;
}

799 800 801 802 803 804 805
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
806

807 808 809 810 811 812 813 814
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;
}

815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
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;
}

830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
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
851 852 853 854 855 856 857
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
858 859 860 861 862 863 864 865 866

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);
}

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 893 894 895 896 897 898 899
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) {
900 901 902 903 904 905
    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,
906 907
        getLibVlcInstance(env, thiz),
        children,
908 909 910
        p_md);
    libvlc_media_release(p_md);
    return ret;
911 912
}

Thomas Guillem's avatar
Thomas Guillem committed
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
// 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;
}
933 934 935 936 937

/* used by opensles module */
int aout_get_native_sample_rate(void)
{
    JNIEnv *p_env;
938 939
    if (!(p_env = jni_get_env(THREAD_NAME)))
        return -1;
940 941 942 943 944
    jclass cls = (*p_env)->FindClass (p_env, "android/media/AudioTrack");
    jmethodID method = (*p_env)->GetStaticMethodID (p_env, cls, "getNativeOutputSampleRate", "(I)I");
    int sample_rate = (*p_env)->CallStaticIntMethod (p_env, cls, method, 3); // AudioManager.STREAM_MUSIC
    return sample_rate;
}