libvlcjni.c 26.4 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"
ivoire's avatar
ivoire committed
39
#include "aout.h"
40
#include "vout.h"
41
#include "utils.h"
42
#include "native_crash_handler.h"
43

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

48
#define LOG_TAG "VLC/JNI/main"
49 50
#include "log.h"

Thomas Guillem's avatar
Thomas Guillem committed
51 52
struct fields fields;

53 54
#define VLC_JNI_VERSION JNI_VERSION_1_2

55 56 57 58 59
#define THREAD_NAME "libvlcjni"
int jni_attach_thread(JNIEnv **env, const char *thread_name);
void jni_detach_thread();
int jni_get_env(JNIEnv **env);

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 119 120 121
/* 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
 */
Thomas Guillem's avatar
Thomas Guillem committed
122
static JavaVM *myVm;
123

124
static jobject eventHandlerInstance = NULL;
125 126 127 128 129

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

130
    bool isAttached = false;
131

132
    if (eventHandlerInstance == NULL)
Sébastien Toque's avatar
Sébastien Toque committed
133
        return;
134

135 136
    if (jni_get_env(&env) < 0) {
        if (jni_attach_thread(&env, THREAD_NAME) < 0)
137
            return;
138
        isAttached = true;
139 140
    }

141 142 143 144 145 146 147 148 149 150
    /* 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" );
151
    jmethodID putLong = (*env)->GetMethodID(env, clsBundle, "putLong", "(Ljava/lang/String;J)V" );
152
    jmethodID putFloat = (*env)->GetMethodID(env, clsBundle, "putFloat", "(Ljava/lang/String;F)V" );
153 154
    jmethodID putString = (*env)->GetMethodID(env, clsBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V" );

155
    if (ev->type == libvlc_MediaPlayerPositionChanged) {
156 157 158
        jstring sData = (*env)->NewStringUTF(env, "data");
        (*env)->CallVoidMethod(env, bundle, putFloat, sData, ev->u.media_player_position_changed.new_position);
        (*env)->DeleteLocalRef(env, sData);
159 160
    } else if (ev->type == libvlc_MediaPlayerTimeChanged) {
        jstring sData = (*env)->NewStringUTF(env, "data");
161
        (*env)->CallVoidMethod(env, bundle, putLong, sData, ev->u.media_player_time_changed.new_time);
162
        (*env)->DeleteLocalRef(env, sData);
163
    } else if(ev->type == libvlc_MediaPlayerVout) {
164 165 166 167
        /* 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);
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
    } 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);
191 192
    }

193
    /* Get the object class */
194
    jclass cls = (*env)->GetObjectClass(env, eventHandlerInstance);
195
    if (!cls) {
196
        LOGE("EventHandler: failed to get class reference");
197
        goto end;
198 199 200
    }

    /* Find the callback ID */
201
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(ILandroid/os/Bundle;)V");
202
    if (methodID) {
203
        (*env)->CallVoidMethod(env, eventHandlerInstance, methodID, ev->type, bundle);
204
    } else {
205
        LOGE("EventHandler: failed to get the callback method");
206 207
    }

208
end:
Edward Wang's avatar
Edward Wang committed
209
    (*env)->DeleteLocalRef(env, bundle);
210
    if (isAttached)
211
        jni_detach_thread();
212 213
}

214
jint JNI_OnLoad(JavaVM *vm, void *reserved)
215
{
Thomas Guillem's avatar
Thomas Guillem committed
216
    JNIEnv* env = NULL;
217 218 219
    // Keep a reference on the Java VM.
    myVm = vm;

Thomas Guillem's avatar
Thomas Guillem committed
220 221
    if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
        return -1;
222
    pthread_mutex_init(&vout_android_lock, NULL);
223
    pthread_cond_init(&vout_android_surf_attached, NULL);
224

Thomas Guillem's avatar
Thomas Guillem committed
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 255 256 257 258 259 260 261 262 263 264 265 266 267
#define GET_CLASS(clazz, str) do { \
    (clazz) = (*env)->FindClass(env, (str)); \
    if (!(clazz)) { \
        LOGE("FindClass(%s) failed", (str)); \
        return -1; \
    } \
    (clazz) = (jclass) (*env)->NewGlobalRef(env, (clazz)); \
    if (!(clazz)) { \
        LOGE("NewGlobalRef(%s) failed", (str)); \
        return -1; \
    } \
} while (0)

#define GET_ID(get, id, clazz, str, args) do { \
    (id) = (*env)->get(env, (clazz), (str), (args)); \
    if (!(id)) { \
        LOGE(#get"(%s) failed", (str)); \
        return -1; \
    } \
} while (0)

    GET_CLASS(fields.IllegalStateException.clazz,
              "java/lang/IllegalStateException");
    GET_CLASS(fields.IllegalArgumentException.clazz,
              "java/lang/IllegalArgumentException");
    GET_CLASS(fields.String.clazz,
              "java/lang/String");
    GET_CLASS(fields.VLCObject.clazz,
              "org/videolan/libvlc/VLCObject");

    GET_ID(GetFieldID,
           fields.VLCObject.mInstanceID,
           fields.VLCObject.clazz,
           "mInstance", "J");

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

#undef GET_CLASS
#undef GET_ID

268
    LOGD("JNI interface loaded.");
269
    return VLC_JNI_VERSION;
270 271
}

Thomas Guillem's avatar
Thomas Guillem committed
272 273 274 275
void JNI_OnUnload(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;

276
    pthread_mutex_destroy(&vout_android_lock);
277
    pthread_cond_destroy(&vout_android_surf_attached);
Thomas Guillem's avatar
Thomas Guillem committed
278 279 280 281 282 283 284 285

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

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
int jni_attach_thread(JNIEnv **env, const char *thread_name)
{
    JavaVMAttachArgs args;
    jint result;

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

    result = (*myVm)->AttachCurrentThread(myVm, env, &args);
    return result == JNI_OK ? 0 : -1;
}

void jni_detach_thread()
{
    (*myVm)->DetachCurrentThread(myVm);
}

int jni_get_env(JNIEnv **env)
{
308
    return (*myVm)->GetEnv(myVm, (void **)env, VLC_JNI_VERSION) == JNI_OK ? 0 : -1;
309 310
}

Rafaël Carré's avatar
Rafaël Carré committed
311 312
// FIXME: use atomics
static bool verbosity;
313

314
void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
315
{
316 317 318
    //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
319
    int aout = (*env)->CallIntMethod(env, thiz, methodId);
320

321
    methodId = (*env)->GetMethodID(env, cls, "getVout", "()I");
322
    int vout = (*env)->CallIntMethod(env, thiz, methodId);
323

324 325 326
    methodId = (*env)->GetMethodID(env, cls, "timeStretchingEnabled", "()Z");
    bool enable_time_stretch = (*env)->CallBooleanMethod(env, thiz, methodId);

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

Edward Wang's avatar
Edward Wang committed
330 331
    methodId = (*env)->GetMethodID(env, cls, "getDeblocking", "()I");
    int deblocking = (*env)->CallIntMethod(env, thiz, methodId);
Rafaël Carré's avatar
Rafaël Carré committed
332 333
    char deblockstr[2];
    snprintf(deblockstr, sizeof(deblockstr), "%d", deblocking);
Edward Wang's avatar
Edward Wang committed
334
    LOGD("Using deblocking level %d", deblocking);
335

Edward Wang's avatar
Edward Wang committed
336 337
    methodId = (*env)->GetMethodID(env, cls, "getNetworkCaching", "()I");
    int networkCaching = (*env)->CallIntMethod(env, thiz, methodId);
Rafaël Carré's avatar
Rafaël Carré committed
338
    char networkCachingstr[25];
Edward Wang's avatar
Edward Wang committed
339
    if(networkCaching > 0) {
Rafaël Carré's avatar
Rafaël Carré committed
340
        snprintf(networkCachingstr, sizeof(networkCachingstr), "--network-caching=%d", networkCaching);
Edward Wang's avatar
Edward Wang committed
341 342 343
        LOGD("Using network caching of %d ms", networkCaching);
    }

344 345 346
    methodId = (*env)->GetMethodID(env, cls, "getHttpReconnect", "()Z");
    bool enable_http_reconnect = (*env)->CallBooleanMethod(env, thiz, methodId);

347 348 349 350
    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
351

352 353 354
    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
355
    LOGD("Subtitle encoding set to \"%s\"", subsencodingstr);
356

357 358 359
    methodId = (*env)->GetMethodID(env, cls, "isVerboseMode", "()Z");
    verbosity = (*env)->CallBooleanMethod(env, thiz, methodId);

360 361
    methodId = (*env)->GetMethodID(env, cls, "isDirectRendering", "()Z");
    bool direct_rendering = (*env)->CallBooleanMethod(env, thiz, methodId);
362
    /* With the MediaCodec opaque mode we cannot use the OpenGL ES vout. */
363
    if (direct_rendering)
364
        vout = VOUT_ANDROID_WINDOW;
365

flx42's avatar
flx42 committed
366 367
    methodId = (*env)->GetMethodID(env, cls, "getCachePath", "()Ljava/lang/String;");
    jstring cachePath = (*env)->CallObjectMethod(env, thiz, methodId);
368 369 370 371 372
    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
373

374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
#define MAX_ARGV 18
    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
    }
    /* 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);
419

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

422
    (*env)->ReleaseStringUTFChars(env, chroma, chromastr);
423 424
    (*env)->ReleaseStringUTFChars(env, subsencoding, subsencodingstr);

425 426
    if (!instance)
    {
427
        jclass exc = (*env)->FindClass(env, "org/videolan/libvlc/LibVlcException");
428 429 430 431
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
432

Rafaël Carré's avatar
Rafaël Carré committed
433
    libvlc_log_set(instance, debug_log, &verbosity);
434 435

    init_native_crash_handler(env, thiz);
436 437
}

438
void Java_org_videolan_libvlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
439
{
440 441
    destroy_native_crash_handler(env);

442
    releaseMediaPlayer(env, thiz);
Edward Wang's avatar
Edward Wang committed
443
    jlong libVlcInstance = getLong(env, thiz, "mLibVlcInstance");
444 445 446
    if (!libVlcInstance)
        return; // Already destroyed

Ludovic Fauvet's avatar
Ludovic Fauvet committed
447
    libvlc_instance_t *instance = (libvlc_instance_t*)(intptr_t) libVlcInstance;
Rafaël Carré's avatar
Rafaël Carré committed
448
    libvlc_log_unset(instance);
449 450
    libvlc_release(instance);

Edward Wang's avatar
Edward Wang committed
451
    setLong(env, thiz, "mLibVlcInstance", 0);
452 453
}

454
void Java_org_videolan_libvlc_LibVLC_detachEventHandler(JNIEnv *env, jobject thiz)
455
{
456 457 458
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
459 460 461
    }
}

462
void Java_org_videolan_libvlc_LibVLC_setEventHandler(JNIEnv *env, jobject thiz, jobject eventHandler)
463
{
464 465 466
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
467 468
    }

469
    eventHandlerInstance = getEventHandlerReference(env, thiz, eventHandler);
470 471
}

472
void Java_org_videolan_libvlc_LibVLC_playMRL(JNIEnv *env, jobject thiz,
473 474
                                             jstring mrl, jobjectArray mediaOptions)
{
475 476 477
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

478 479
    libvlc_instance_t *p_instance = getLibVlcInstance(env, thiz);

480
    /* Create a media player playing environment */
481
    libvlc_media_player_t *mp = libvlc_media_player_new(p_instance);
Rafaël Carré's avatar
Rafaël Carré committed
482
    libvlc_media_player_set_video_title_display(mp, libvlc_position_disable, 0);
483
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz); // freed in aout_close
484

485
    // If AOUT_AUDIOTRACK_JAVA, use amem
486 487 488
    jclass cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodId = (*env)->GetMethodID(env, cls, "getAout", "()I");
    if ( (*env)->CallIntMethod(env, thiz, methodId) == AOUT_AUDIOTRACK_JAVA )
489
    {
490
        libvlc_audio_set_callbacks(mp, aout_play, aout_pause, NULL, NULL, NULL,
Sébastien Toque's avatar
Sébastien Toque committed
491 492
                                   (void*) myJavaLibVLC);
        libvlc_audio_set_format_callbacks(mp, aout_open, aout_close);
493 494
    }

495 496
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
497 498 499 500 501
    static const libvlc_event_type_t mp_events[] = {
        libvlc_MediaPlayerPlaying,
        libvlc_MediaPlayerPaused,
        libvlc_MediaPlayerEndReached,
        libvlc_MediaPlayerStopped,
502
        libvlc_MediaPlayerVout,
503
        libvlc_MediaPlayerPositionChanged,
504
        libvlc_MediaPlayerTimeChanged,
505
        libvlc_MediaPlayerEncounteredError
506
    };
507
    for(int i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); i++)
508 509
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

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

513 514 515 516
    cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodID = (*env)->GetMethodID(env, cls, "applyEqualizer", "()V");
    (*env)->CallVoidMethod(env, thiz, methodID);

517 518
    const char* p_mrl = (*env)->GetStringUTFChars(env, mrl, 0);

519
    libvlc_media_t* p_md = libvlc_media_new_location(p_instance, p_mrl);
520
    /* media options */
521
    if (mediaOptions != NULL)
522
        add_media_options(p_md, env, mediaOptions);
523

524 525
    (*env)->ReleaseStringUTFChars(env, mrl, p_mrl);

526 527 528
    /* 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
529 530
        libvlc_MediaParsedChanged,
        libvlc_MediaMetaChanged,
531 532 533 534
    };
    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);

535 536
    libvlc_media_player_set_media(mp, p_md);
    libvlc_media_player_play(mp);
537 538
}

539
jfloat Java_org_videolan_libvlc_LibVLC_getRate(JNIEnv *env, jobject thiz) {
Edward Wang's avatar
Edward Wang committed
540 541 542 543 544 545 546
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        return libvlc_media_player_get_rate(mp);
    else
        return 1.00;
}

547
void Java_org_videolan_libvlc_LibVLC_setRate(JNIEnv *env, jobject thiz, jfloat rate) {
Edward Wang's avatar
Edward Wang committed
548 549 550 551 552
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        libvlc_media_player_set_rate(mp, rate);
}

553
jboolean Java_org_videolan_libvlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
554
{
555
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
556
    if (mp)
557
        return !!libvlc_media_player_is_playing(mp);
558 559
    else
        return 0;
560 561
}

562
jboolean Java_org_videolan_libvlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
563
{
564 565 566
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_seekable(mp);
567
    return 0;
568 569
}

570
void Java_org_videolan_libvlc_LibVLC_play(JNIEnv *env, jobject thiz)
571
{
572
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
573
    if (mp)
574
        libvlc_media_player_play(mp);
575 576
}

577
void Java_org_videolan_libvlc_LibVLC_pause(JNIEnv *env, jobject thiz)
578
{
579
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
580
    if (mp)
581
        libvlc_media_player_pause(mp);
582 583
}

584
void Java_org_videolan_libvlc_LibVLC_stop(JNIEnv *env, jobject thiz)
585
{
586
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
587
    if (mp)
588
        libvlc_media_player_stop(mp);
589 590
}

591 592 593 594 595 596 597 598
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;
}

599
jint Java_org_videolan_libvlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
600
{
601 602 603
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
604 605 606
    return -1;
}

607
jint Java_org_videolan_libvlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
608
{
609 610
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
611
        //Returns 0 if the volume was set, -1 if it was out of range or error
612
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
613 614 615
    return -1;
}

616
jlong Java_org_videolan_libvlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
617
{
618 619 620
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
621 622 623
    return -1;
}

624
void Java_org_videolan_libvlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
625
{
626 627 628
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
629 630
}

631
jfloat Java_org_videolan_libvlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
632
{
633 634 635
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
636 637 638
    return -1;
}

639
void Java_org_videolan_libvlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
640
{
641 642 643
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
644 645
}

646
jlong Java_org_videolan_libvlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
647
{
648 649 650
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
651 652 653
    return -1;
}

654
jstring Java_org_videolan_libvlc_LibVLC_version(JNIEnv* env, jobject thiz)
655
{
656 657
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
658

659
jstring Java_org_videolan_libvlc_LibVLC_compiler(JNIEnv* env, jobject thiz)
660 661 662 663
{
    return (*env)->NewStringUTF(env, libvlc_get_compiler());
}

664
jstring Java_org_videolan_libvlc_LibVLC_changeset(JNIEnv* env, jobject thiz)
665 666 667
{
    return (*env)->NewStringUTF(env, libvlc_get_changeset());
}
668

669 670 671
jstring Java_org_videolan_libvlc_LibVLC_getMeta(JNIEnv *env, jobject thiz, int meta)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
672 673
    char *psz_meta;
    jstring string = NULL;
674 675 676 677 678 679 680
    if (!mp)
        return NULL;

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

681 682 683 684 685 686
    psz_meta = libvlc_media_get_meta(p_mp, meta);
    if (psz_meta) {
        string = (*env)->NewStringUTF(env, psz_meta);
        free(psz_meta);
    }
    return string;
687 688
}

689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
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);
}

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
711 712 713 714 715 716 717 718

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
719 720 721 722 723 724 725 726 727

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

728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
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) {
    return (jint)expand_media_internal(env,
        getLibVlcInstance(env, thiz),
        children,
        (libvlc_media_t*)libvlc_media_player_get_media(getMediaPlayer(env, thiz)));
}

Thomas Guillem's avatar
Thomas Guillem committed
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
// 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;
}