libvlcjni.c 35.1 KB
Newer Older
Rafaël Carré's avatar
Rafaël Carré committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*****************************************************************************
 * libvlcjni.c
 *****************************************************************************
 * Copyright © 2010-2012 VLC authors and VideoLAN
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU 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.
 *****************************************************************************/

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

29
#include <vlc/vlc.h>
30
31
#include <vlc_common.h>
#include <vlc_url.h>
Ludovic Fauvet's avatar
Ludovic Fauvet committed
32
#include <vlc_fourcc.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 "utils.h"
41
42

#define LOG_TAG "VLC/JNI/main"
43
44
#include "log.h"

45
46
47
48
#define AOUT_AUDIOTRACK      0
#define AOUT_AUDIOTRACK_JAVA 1
#define AOUT_OPENSLES        2

Edward Wang's avatar
Edward Wang committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
static jint getInt(JNIEnv *env, jobject thiz, const char* field) {
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
                                          field, "I");
    return (*env)->GetIntField(env, thiz, fieldMP);
}
static void setInt(JNIEnv *env, jobject item, const char* field, jint value) {
    jclass cls;
    jfieldID fieldId;

    /* Get a reference to item's class */
    cls = (*env)->GetObjectClass(env, item);

    /* Look for the instance field s in cls */
    fieldId = (*env)->GetFieldID(env, cls, field, "I");
    if (fieldId == NULL)
        return;

    (*env)->SetIntField(env, item, fieldId, value);
}

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
static jlong getLong(JNIEnv *env, jobject thiz, const char* field) {
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
                                          field, "J");
    return (*env)->GetLongField(env, thiz, fieldMP);
}
static void setLong(JNIEnv *env, jobject item, const char* field, jlong value) {
    jclass cls;
    jfieldID fieldId;

    /* Get a reference to item's class */
    cls = (*env)->GetObjectClass(env, item);

    /* Look for the instance field s in cls */
    fieldId = (*env)->GetFieldID(env, cls, field, "J");
    if (fieldId == NULL)
        return;

    (*env)->SetLongField(env, item, fieldId, value);
}

static void setFloat(JNIEnv *env, jobject item, const char* field, jfloat value) {
    jclass cls;
    jfieldID fieldId;

    /* Get a reference to item's class */
    cls = (*env)->GetObjectClass(env, item);

    /* Look for the instance field s in cls */
    fieldId = (*env)->GetFieldID(env, cls, field, "F");
    if (fieldId == NULL)
        return;

    (*env)->SetFloatField(env, item, fieldId, value);
}
static void setString(JNIEnv *env, jobject item, const char* field, const char* text) {
    jclass cls;
    jfieldID fieldId;
    jstring jstr;

    /* Get a reference to item's class */
    cls = (*env)->GetObjectClass(env, item);

    /* Look for the instance field s in cls */
    fieldId = (*env)->GetFieldID(env, cls, field, "Ljava/lang/String;");
    if (fieldId == NULL)
        return;

    /* Create a new string and overwrite the instance field */
    jstr = (*env)->NewStringUTF(env, text);
    if (jstr == NULL)
        return;
    (*env)->SetObjectField(env, item, fieldId, jstr);
}

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
struct length_change_monitor {
    pthread_mutex_t doneMutex;
    pthread_cond_t doneCondVar;
    bool length_changed;
};

static void length_changed_callback(const libvlc_event_t *ev, void *data)
{
    struct length_change_monitor *monitor = data;
    pthread_mutex_lock(&monitor->doneMutex);
    monitor->length_changed = true;
    pthread_cond_signal(&monitor->doneCondVar);
    pthread_mutex_unlock(&monitor->doneMutex);
}

140
libvlc_media_t *new_media(jlong instance, JNIEnv *env, jobject thiz, jstring fileLocation, bool noOmx, bool noVideo)
141
{
142
143
    libvlc_instance_t *libvlc = (libvlc_instance_t*)instance;
    jboolean isCopy;
144
145
146
    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);
147
148
149
    if (!p_md)
        return NULL;

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
    if (!noOmx) {
        jclass cls = (*env)->GetObjectClass(env, thiz);
        jmethodID methodId = (*env)->GetMethodID(env, cls, "useIOMX", "()Z");
        if ((*env)->CallBooleanMethod(env, thiz, methodId)) {
            /*
             * Set higher caching values if using iomx decoding, since some omx
             * decoders have a very high latency, and if the preroll data isn't
             * enough to make the decoder output a frame, the playback timing gets
             * started too soon, and every decoded frame appears to be too late.
             * On Nexus One, the decoder latency seems to be 25 input packets
             * for 320x170 H.264, a few packets less on higher resolutions.
             * On Nexus S, the decoder latency seems to be about 7 packets.
             */
            libvlc_media_add_option(p_md, ":file-caching=1500");
            libvlc_media_add_option(p_md, ":network-caching=1500");
            libvlc_media_add_option(p_md, ":codec=iomx,all");
        }
167
168
        if (noVideo)
            libvlc_media_add_option(p_md, ":no-video");
169
    }
170
    return p_md;
171
172
}

173
174
175
176
177
static libvlc_media_list_t *getMediaList(JNIEnv *env, jobject thiz)
{
    return (libvlc_media_list_t*)getLong(env, thiz, "mMediaListInstance");
}

Rafaël Carré's avatar
Rafaël Carré committed
178
static libvlc_media_player_t *getMediaPlayer(JNIEnv *env, jobject thiz)
179
{
180
181
182
183
184
185
    return (libvlc_media_player_t*)getLong(env, thiz, "mInternalMediaPlayerInstance");
}

static libvlc_media_list_player_t *getMediaListPlayer(JNIEnv *env, jobject thiz)
{
    return (libvlc_media_list_player_t*)getLong(env, thiz, "mMediaListPlayerInstance");
186
187
}

Rafaël Carré's avatar
Rafaël Carré committed
188
static void unsetMediaPlayer(JNIEnv *env, jobject thiz)
189
{
190
    setLong(env, thiz, "mInternalMediaPlayerInstance", (jlong)0);
Rafaël Carré's avatar
Rafaël Carré committed
191
192
193
194
}

static void releaseMediaPlayer(JNIEnv *env, jobject thiz)
{
195
196
    libvlc_media_list_player_t* p_mlp = getMediaListPlayer(env, thiz);
    if (p_mlp)
197
    {
198
199
200
201
        libvlc_media_list_player_stop(p_mlp);
        libvlc_media_list_player_release(p_mlp);
        /* libvlc_media_list_player_release frees the media player, so
         * we don't free it ourselves. */
Rafaël Carré's avatar
Rafaël Carré committed
202
        unsetMediaPlayer(env, thiz);
203
        setLong(env, thiz, "mInternalMediaPlayerInstance", 0);
204
205
206
    }
}

207
208
209
210
211
/* 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
 */
JavaVM *myVm;
212

213
static jobject eventManagerInstance = NULL;
214

215
216
static pthread_mutex_t vout_android_lock;
static void *vout_android_surf = NULL;
217
static void *vout_android_gui = NULL;
218

219
void *jni_LockAndGetAndroidSurface() {
220
    pthread_mutex_lock(&vout_android_lock);
Ludovic Fauvet's avatar
Ludovic Fauvet committed
221
    return vout_android_surf;
222
223
}

224
void jni_UnlockAndroidSurface() {
225
226
227
    pthread_mutex_unlock(&vout_android_lock);
}

228
void jni_SetAndroidSurfaceSize(int width, int height)
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
{
    if (vout_android_gui == NULL)
        return;

    JNIEnv *p_env;

    (*myVm)->AttachCurrentThread (myVm, &p_env, NULL);
    jclass cls = (*p_env)->GetObjectClass (p_env, vout_android_gui);
    jmethodID methodId = (*p_env)->GetMethodID (p_env, cls, "setSurfaceSize", "(II)V");

    (*p_env)->CallVoidMethod (p_env, vout_android_gui, methodId, width, height);

    (*p_env)->DeleteLocalRef(p_env, cls);
    (*myVm)->DetachCurrentThread (myVm);
}

245
246
247
static void vlc_event_callback(const libvlc_event_t *ev, void *data)
{
    JNIEnv *env;
248
    JavaVM *myVm = data;
249

250
    bool isAttached = false;
251

252
    if (eventManagerInstance == NULL)
Sébastien Toque's avatar
Sébastien Toque committed
253
        return;
254

255
    int status = (*myVm)->GetEnv(myVm, (void**) &env, JNI_VERSION_1_2);
256
    if (status < 0) {
257
        LOGD("vlc_event_callback: failed to get JNI environment, "
258
259
260
261
             "assuming native thread");
        status = (*myVm)->AttachCurrentThread(myVm, &env, NULL);
        if (status < 0)
            return;
262
        isAttached = true;
263
264
    }

265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
    /* 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" );
    jmethodID putString = (*env)->GetMethodID(env, clsBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V" );

    if(ev->type == libvlc_MediaPlayerVout) {
        /* 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);
    }

284
285
286
287
    /* Get the object class */
    jclass cls = (*env)->GetObjectClass(env, eventManagerInstance);
    if (!cls) {
        LOGE("EventManager: failed to get class reference");
288
        goto end;
289
290
291
    }

    /* Find the callback ID */
292
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(ILandroid/os/Bundle;)V");
293
    if (methodID) {
294
        (*env)->CallVoidMethod(env, eventManagerInstance, methodID, ev->type, bundle);
295
    } else {
296
297
298
        LOGE("EventManager: failed to get the callback method");
    }

299
300
301
end:
    if (isAttached)
        (*myVm)->DetachCurrentThread(myVm);
302
303
}

304
jint JNI_OnLoad(JavaVM *vm, void *reserved)
305
{
306
307
308
    // Keep a reference on the Java VM.
    myVm = vm;

309
310
    pthread_mutex_init(&vout_android_lock, NULL);

311
    LOGD("JNI interface loaded.");
312
    return JNI_VERSION_1_2;
313
314
}

315
316
317
318
void JNI_OnUnload(JavaVM* vm, void* reserved) {
    pthread_mutex_destroy(&vout_android_lock);
}

Sébastien Toque's avatar
Sébastien Toque committed
319
void Java_org_videolan_vlc_LibVLC_attachSurface(JNIEnv *env, jobject thiz, jobject surf, jobject gui, jint width, jint height) {
320
321
322
323
324
325
326
    jclass clz;
    jfieldID fid;

    pthread_mutex_lock(&vout_android_lock);
    clz = (*env)->GetObjectClass(env, surf);
    fid = (*env)->GetFieldID(env, clz, "mSurface", "I");
    if (fid == NULL) {
327
        jthrowable exp = (*env)->ExceptionOccurred(env);
328
329
330
331
332
333
334
335
        if (exp) {
            (*env)->DeleteLocalRef(env, exp);
            (*env)->ExceptionClear(env);
        }
        fid = (*env)->GetFieldID(env, clz, "mNativeSurface", "I");
    }
    vout_android_surf = (void*)(*env)->GetIntField(env, surf, fid);
    (*env)->DeleteLocalRef(env, clz);
336
337

    vout_android_gui = (*env)->NewGlobalRef(env, gui);
338
339
340
    pthread_mutex_unlock(&vout_android_lock);
}

Sébastien Toque's avatar
Sébastien Toque committed
341
void Java_org_videolan_vlc_LibVLC_detachSurface(JNIEnv *env, jobject thiz) {
342
343
    pthread_mutex_lock(&vout_android_lock);
    vout_android_surf = NULL;
344
345
346
    if (vout_android_gui != NULL)
        (*env)->DeleteGlobalRef(env, vout_android_gui);
    vout_android_gui = NULL;
347
348
    pthread_mutex_unlock(&vout_android_lock);
}
349

350
351
static void debug_log(void *data, int level, const char *fmt, va_list ap)
{
Rafaël Carré's avatar
Rafaël Carré committed
352
    bool *verbose = data;
353
354
355
356
357
358
359
360
361
362
363
364
365

    static const uint8_t priority[5] = {
        [LIBVLC_DEBUG]   = ANDROID_LOG_DEBUG,
        [1 /* ??? */]    = ANDROID_LOG_DEBUG,
        [LIBVLC_NOTICE]  = ANDROID_LOG_INFO,
        [LIBVLC_WARNING] = ANDROID_LOG_WARN,
        [LIBVLC_ERROR]   = ANDROID_LOG_ERROR,
    };

    int prio = ANDROID_LOG_DEBUG;
    if (level >= LIBVLC_DEBUG && level <= LIBVLC_ERROR)
        prio = priority[level];

Ludovic Fauvet's avatar
Ludovic Fauvet committed
366
    if (!*verbose && prio < ANDROID_LOG_ERROR)
367
368
        return;

369
370
371
372
    __android_log_vprint(prio, "VLC", fmt, ap);
}

static libvlc_log_subscriber_t debug_subscriber;
Rafaël Carré's avatar
Rafaël Carré committed
373
static bool verbosity;
374

375
376
void Java_org_videolan_vlc_LibVLC_changeVerbosity(JNIEnv *env, jobject thiz, jboolean verbose)
{
Rafaël Carré's avatar
Rafaël Carré committed
377
    verbosity = verbose;
378
    libvlc_log_unsubscribe(&debug_subscriber);
Rafaël Carré's avatar
Rafaël Carré committed
379
    libvlc_log_subscribe(&debug_subscriber, debug_log, &verbosity);
380
381
382
}

void Java_org_videolan_vlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz, jboolean verbose)
383
{
384
385
386
387
388
    //only use OpenSLES if java side says we can
    jclass cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodId = (*env)->GetMethodID(env, cls, "getAout", "()I");
    bool use_opensles = (*env)->CallIntMethod(env, thiz, methodId) == AOUT_OPENSLES;

389
390
391
    methodId = (*env)->GetMethodID(env, cls, "timeStretchingEnabled", "()Z");
    bool enable_time_stretch = (*env)->CallBooleanMethod(env, thiz, methodId);

Rafaël Carré's avatar
Rafaël Carré committed
392
393
    verbosity = verbose;
    libvlc_log_subscribe(&debug_subscriber, debug_log, &verbosity);
394

395
    /* Don't add any invalid options, otherwise it causes LibVLC to crash */
396
    const char *argv[] = {
397
        "-I", "dummy",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
398
399
400
        "--no-osd",
        "--no-video-title-show",
        "--no-stats",
401
402
        "--no-plugins-cache",
        "--no-drop-late-frames",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
403
        "--avcodec-fast",
404
        enable_time_stretch ? "--audio-time-stretch" : "--no-audio-time-stretch",
405
        use_opensles ? "--aout=opensles" : "--aout=android_audiotrack",
406
    };
407
    libvlc_instance_t *instance = libvlc_new(sizeof(argv) / sizeof(*argv), argv);
408

Edward Wang's avatar
Edward Wang committed
409
    setLong(env, thiz, "mLibVlcInstance", (jlong) instance);
410

411
412
    if (!instance)
    {
Sébastien Toque's avatar
Sébastien Toque committed
413
        jclass exc = (*env)->FindClass(env, "org/videolan/vlc/LibVlcException");
414
415
416
417
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
418
419
420
421
422
423
424
425
426
427

    /* Initialize media list (a.k.a. playlist/history) */
    libvlc_media_list_t* pointer = libvlc_media_list_new( instance );
    if(!pointer) {
        jclass exc = (*env)->FindClass(env, "org/videolan/vlc/LibVlcException");
        (*env)->ThrowNew(env, exc, "Unable to create LibVLC media list");
        return;
    }

    setLong(env, thiz, "mMediaListInstance", (jlong)pointer);
428
429
}

430
431
432
433
434
435
jstring Java_org_videolan_vlc_LibVLC_nativeToURI(JNIEnv *env, jobject thiz, jstring path)
{
    jboolean isCopy;
    /* Get C string */
    const char* psz_path = (*env)->GetStringUTFChars(env, path, &isCopy);
    /* Convert the path to URI */
436
437
438
439
440
    char* psz_location;
    if(unlikely( strstr( psz_path, "://" ) ))
        psz_location = strdup(psz_path);
    else
        psz_location = vlc_path2uri(psz_path, "file");
441
442
443
444
445
446
447
    /* Box into jstring */
    jstring t = (*env)->NewStringUTF(env, psz_location);
    /* Clean up */
    (*env)->ReleaseStringUTFChars(env, path, psz_path);
    free(psz_location);
    return t;
}
448

Sébastien Toque's avatar
Sébastien Toque committed
449
void Java_org_videolan_vlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
450
{
451
    releaseMediaPlayer(env, thiz);
Edward Wang's avatar
Edward Wang committed
452
    jlong libVlcInstance = getLong(env, thiz, "mLibVlcInstance");
453
454
455
456
457
    if (!libVlcInstance)
        return; // Already destroyed

    libvlc_instance_t *instance = (libvlc_instance_t*) libVlcInstance;
    libvlc_release(instance);
458
    libvlc_log_unsubscribe(&debug_subscriber);
459

Edward Wang's avatar
Edward Wang committed
460
    setLong(env, thiz, "mLibVlcInstance", 0);
461
462
}

Sébastien Toque's avatar
Sébastien Toque committed
463
void Java_org_videolan_vlc_LibVLC_detachEventManager(JNIEnv *env, jobject thiz)
464
465
466
467
468
469
470
{
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }
}

Sébastien Toque's avatar
Sébastien Toque committed
471
void Java_org_videolan_vlc_LibVLC_setEventManager(JNIEnv *env, jobject thiz, jobject eventManager)
472
{
473
474
475
476
477
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }

478
479
480
481
482
483
    jclass cls = (*env)->GetObjectClass(env, eventManager);
    if (!cls) {
        LOGE("setEventManager: failed to get class reference");
        return;
    }

484
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(ILandroid/os/Bundle;)V");
485
486
487
488
    if (!methodID) {
        LOGE("setEventManager: failed to get the callback method");
        return;
    }
489

490
491
492
    eventManagerInstance = (*env)->NewGlobalRef(env, eventManager);
}

Sébastien Toque's avatar
Sébastien Toque committed
493
jobjectArray Java_org_videolan_vlc_LibVLC_readMediaMeta(JNIEnv *env,
494
                                                        jobject thiz, jlong instance, jstring mrl)
Rafaël Carré's avatar
Rafaël Carré committed
495
496
497
498
499
{
    jobjectArray array = (*env)->NewObjectArray(env, 8,
            (*env)->FindClass(env, "java/lang/String"),
            (*env)->NewStringUTF(env, ""));

500
    libvlc_media_t *m = new_media(instance, env, thiz, mrl, false, false);
501
502
    if (!m)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
503
        LOGE("readMediaMeta: Could not create the media!");
504
        return array;
505
506
507
    }

    libvlc_media_parse(m);
Rafaël Carré's avatar
Rafaël Carré committed
508

509
    static const char str[][7] = {
Rafaël Carré's avatar
Rafaël Carré committed
510
511
        "artist", "album", "title", "genre",
    };
512
    static const libvlc_meta_t meta_id[] = {
Rafaël Carré's avatar
Rafaël Carré committed
513
514
515
516
517
        libvlc_meta_Artist,
        libvlc_meta_Album,
        libvlc_meta_Title,
        libvlc_meta_Genre,
    };
518
    for (int i=0; i < sizeof(str) / sizeof(*str); i++) {
Rafaël Carré's avatar
Rafaël Carré committed
519
520
        char *meta = libvlc_media_get_meta(m, meta_id[i]);
        if (!meta)
521
            meta = strdup("");
Rafaël Carré's avatar
Rafaël Carré committed
522
523
524
525
526
527
528
529
530
531
532
533
534

        jstring k = (*env)->NewStringUTF(env, str[i]);
        (*env)->SetObjectArrayElement(env, array, 2*i, k);
        jstring v = (*env)->NewStringUTF(env, meta);
        (*env)->SetObjectArrayElement(env, array, 2*i+1, v);

        free(meta);
   }

   libvlc_media_release(m);
   return array;
}

Sébastien Toque's avatar
Sébastien Toque committed
535
void Java_org_videolan_vlc_LibVLC_readMedia(JNIEnv *env, jobject thiz,
536
                                            jlong instance, jstring mrl, jboolean novideo)
537
{
538
539
540
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

541
    /* Create a new item */
542
    libvlc_media_t *m = new_media(instance, env, thiz, mrl, false, novideo);
543
544
    if (!m)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
545
        LOGE("readMedia: Could not create the media!");
546
547
        return;
    }
548

549
550
    libvlc_media_list_t* p_mlist = getMediaList(env, thiz);

551
    /* Create a media player playing environment */
552
    libvlc_media_list_player_t* p_mlp = libvlc_media_list_player_new((libvlc_instance_t*)instance);
553
554
    libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)instance);

555
556
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz);

557
558
559
560
    libvlc_media_list_lock(p_mlist);
    libvlc_media_list_add_media(p_mlist, m);
    int position = libvlc_media_list_index_of_item(p_mlist, m);
    libvlc_media_list_unlock(p_mlist);
ivoire's avatar
ivoire committed
561

562
563
564
565
    //if AOUT_AUDIOTRACK_JAVA, we use amem
    jclass cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodId = (*env)->GetMethodID(env, cls, "getAout", "()I");
    if ( (*env)->CallIntMethod(env, thiz, methodId) == AOUT_AUDIOTRACK_JAVA )
566
    {
Sébastien Toque's avatar
Sébastien Toque committed
567
568
569
        libvlc_audio_set_callbacks(mp, aout_play, NULL, NULL, NULL, NULL,
                                   (void*) myJavaLibVLC);
        libvlc_audio_set_format_callbacks(mp, aout_open, aout_close);
570
571
    }

572
573
574
    /* No need to keep the media now */
    libvlc_media_release(m);

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

587
588
589
    libvlc_media_list_player_set_media_list(p_mlp, p_mlist);
    libvlc_media_list_player_set_media_player(p_mlp, mp);

590
    /* Keep a pointer to this media player */
591
592
    setLong(env, thiz, "mMediaListPlayerInstance", (jlong)p_mlp);
    setLong(env, thiz, "mInternalMediaPlayerInstance", (jlong)mp);
593

594
    libvlc_media_list_player_play_item_at_index(p_mlp, position);
595
596
}

Edward Wang's avatar
Edward Wang committed
597
598
599
600
601
602
603
604
605
606
607
608
609
610
jfloat Java_org_videolan_vlc_LibVLC_getRate(JNIEnv *env, jobject thiz) {
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        return libvlc_media_player_get_rate(mp);
    else
        return 1.00;
}

void Java_org_videolan_vlc_LibVLC_setRate(JNIEnv *env, jobject thiz, jfloat rate) {
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        libvlc_media_player_set_rate(mp, rate);
}

Sébastien Toque's avatar
Sébastien Toque committed
611
jboolean Java_org_videolan_vlc_LibVLC_hasVideoTrack(JNIEnv *env, jobject thiz,
612
                                                    jint i_instance, jstring fileLocation)
613
{
614
    /* Create a new item and assign it to the media player. */
615
    libvlc_media_t *p_m = new_media(i_instance, env, thiz, fileLocation, false, false);
616
617
    if (p_m == NULL)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
618
        LOGE("Could not create the media!");
Rafaël Carré's avatar
Rafaël Carré committed
619
        return JNI_FALSE;
620
621
622
623
624
    }

    /* Get the tracks information of the media. */
    libvlc_media_track_info_t *p_tracks;
    libvlc_media_parse(p_m);
625

626
627
628
629
630
631
632
633
634
635
    libvlc_media_player_t* p_mp = libvlc_media_player_new_from_media(p_m);

    struct length_change_monitor* monitor;
    monitor = malloc(sizeof(struct length_change_monitor));
    if (!monitor) return 0;

    /* Initialize pthread variables. */
    pthread_mutex_init(&monitor->doneMutex, NULL);
    pthread_cond_init(&monitor->doneCondVar, NULL);
    monitor->length_changed = false;
Rafaël Carré's avatar
Rafaël Carré committed
636

637
638
639
640
641
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(p_mp);
    libvlc_event_attach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
    libvlc_media_player_play( p_mp );

    pthread_mutex_lock(&monitor->doneMutex);
642
643
644
645
646
647
648
649
650

    struct timespec deadline;
    clock_gettime(CLOCK_REALTIME, &deadline);
    deadline.tv_sec += 2; /* If "VLC can't open the file", return */
    int mp_alive = 1;
    while( !monitor->length_changed && mp_alive ) {
        pthread_cond_timedwait(&monitor->doneCondVar, &monitor->doneMutex, &deadline);
        mp_alive = libvlc_media_player_will_play(p_mp);
    }
651
652
    pthread_mutex_unlock(&monitor->doneMutex);

653
654
655
656
657
    int i_nbTracks;
    if( mp_alive )
        i_nbTracks = libvlc_video_get_track_count(p_mp);
    else
        i_nbTracks = -1;
658
659
    LOGI("Number of video tracks: %d",i_nbTracks);

Edward Wang's avatar
Edward Wang committed
660
    libvlc_event_detach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
661
662
    libvlc_media_player_stop(p_mp);
    libvlc_media_player_release(p_mp);
Rafaël Carré's avatar
Rafaël Carré committed
663
664
    libvlc_media_release(p_m);

Edward Wang's avatar
Edward Wang committed
665
666
667
668
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);

669
670
    if(i_nbTracks > 0)
        return JNI_TRUE;
671
672
    else if(i_nbTracks < 0)
        (*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/IOException"), "VLC can't open the file");
673
674
    else
        return JNI_FALSE;
675
676
}

677
jobjectArray Java_org_videolan_vlc_LibVLC_readTracksInfo(JNIEnv *env, jobject thiz,
678
                                                         jlong instance, jstring mrl)
679
680
681
682
683
{
    /* get java class */
    jclass cls = (*env)->FindClass( env, "org/videolan/vlc/TrackInfo" );
    if ( !cls )
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
684
        LOGE("Failed to load class (org/videolan/vlc/TrackInfo)" );
685
686
687
688
689
690
691
692
693
694
695
696
        return NULL;
    }

    /* get java class contructor */
    jmethodID clsCtor = (*env)->GetMethodID( env, cls, "<init>", "()V" );
    if ( !clsCtor )
    {
        LOGE("Failed to find class constructor (org/videolan/vlc/TrackInfo)" );
        return NULL;
    }

    /* Create a new item and assign it to the media player. */
697
    libvlc_media_t *p_m = new_media(instance, env, thiz, mrl, false, false);
698
699
    if (p_m == NULL)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
700
        LOGE("Could not create the media!");
701
702
703
704
705
706
707
708
        return NULL;
    }

    /* Get the tracks information of the media. */
    libvlc_media_track_info_t *p_tracks;
    libvlc_media_parse(p_m);

    int i_nbTracks = libvlc_media_get_tracks_info(p_m, &p_tracks);
709
    jobjectArray array = (*env)->NewObjectArray(env, i_nbTracks + 1, cls, NULL);
710
711
712
713

    unsigned i;
    if (array != NULL)
    {
714
        for (i = 0; i <= i_nbTracks; ++i)
715
716
717
718
719
        {
            jobject item = (*env)->NewObject(env, cls, clsCtor);
            if (item == NULL)
                continue;
            (*env)->SetObjectArrayElement(env, array, i, item);
720
721
722
723
724
725
726
727
728
729

            // use last track for metadata
            if (i == i_nbTracks)
            {
                setInt(env, item, "Type", 3 /* TYPE_META */);
                setLong(env, item, "Length", libvlc_media_get_duration(p_m));
                setString(env, item, "Title", libvlc_media_get_meta(p_m, libvlc_meta_Title));
                setString(env, item, "Artist", libvlc_media_get_meta(p_m, libvlc_meta_Artist));
                setString(env, item, "Album", libvlc_media_get_meta(p_m, libvlc_meta_Album));
                setString(env, item, "Genre", libvlc_media_get_meta(p_m, libvlc_meta_Genre));
730
                setString(env, item, "ArtworkURL", libvlc_media_get_meta(p_m, libvlc_meta_ArtworkURL));
731
732
733
                continue;
            }

734
735
736
            setInt(env, item, "Id", p_tracks[i].i_id);
            setInt(env, item, "Type", p_tracks[i].i_type);
            setString(env, item, "Codec", (const char*)vlc_fourcc_GetDescription(0,p_tracks[i].i_codec));
737
            setString(env, item, "Language", p_tracks[i].psz_language);
738
739
740
741
742

            if (p_tracks[i].i_type == libvlc_track_video)
            {
                setInt(env, item, "Height", p_tracks[i].u.video.i_height);
                setInt(env, item, "Width", p_tracks[i].u.video.i_width);
743
                setFloat(env, item, "Framerate", p_tracks[i].u.video.f_frame_rate);
744
745
746
747
748
749
750
751
752
            }
            if (p_tracks[i].i_type == libvlc_track_audio)
            {
                setInt(env, item, "Channels", p_tracks[i].u.audio.i_channels);
                setInt(env, item, "Samplerate", p_tracks[i].u.audio.i_rate);
            }
        }
    }

753
    libvlc_media_tracks_info_release(p_tracks, i_nbTracks);
754
755
756
757
    libvlc_media_release(p_m);
    return array;
}

758
759
jlong Java_org_videolan_vlc_LibVLC_getLengthFromLocation(JNIEnv *env, jobject thiz,
                                                     jint i_instance, jstring fileLocation)
760
{
761
762
763
764
765
    jlong length = 0;
    struct length_change_monitor *monitor;
    monitor = malloc(sizeof(*monitor));
    if (!monitor)
        return 0;
766
767

    /* Initialize pthread variables. */
768
769
770
    pthread_mutex_init(&monitor->doneMutex, NULL);
    pthread_cond_init(&monitor->doneCondVar, NULL);
    monitor->length_changed = false;
771

772
    /* Create a new item and assign it to the media player. */
773
    libvlc_media_t *m = new_media(i_instance, env, thiz, fileLocation, false, false);
774
    if (m == NULL)
775
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
776
        LOGE("Could not create the media to play!");
777
        goto end;
778
779
    }

780
    /* Create a media player playing environment */
781
    libvlc_media_player_t *mp = libvlc_media_player_new_from_media (m);
782
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
783
    libvlc_event_attach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
784
785
    libvlc_media_release (m);
    libvlc_media_player_play( mp );
786
787
788
789
790
791
792

    pthread_mutex_lock(&monitor->doneMutex);
    while (!monitor->length_changed)
        pthread_cond_wait(&monitor->doneCondVar, &monitor->doneMutex);
    pthread_mutex_unlock(&monitor->doneMutex);

    length = libvlc_media_player_get_length( mp );
793
    libvlc_media_player_stop( mp );
794
    libvlc_media_player_release( mp );
795

796
797
798
799
end:
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);
800
801

    return length;
802
803
}

Sébastien Toque's avatar
Sébastien Toque committed
804
jboolean Java_org_videolan_vlc_LibVLC_hasMediaPlayer(JNIEnv *env, jobject thiz)
805
{
806
    return !!getMediaListPlayer(env, thiz);
807
808
}

Sébastien Toque's avatar
Sébastien Toque committed
809
jboolean Java_org_videolan_vlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
810
{
811
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
812
    if (mp)
813
814
815
        return !!libvlc_media_list_player_is_playing(mp);
    else
        return 0;
816
817
}

Sébastien Toque's avatar
Sébastien Toque committed
818
jboolean Java_org_videolan_vlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
819
{
820
821
822
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_seekable(mp);
823
    return 0;
824
825
}

Sébastien Toque's avatar
Sébastien Toque committed
826
void Java_org_videolan_vlc_LibVLC_play(JNIEnv *env, jobject thiz)
827
{
828
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
829
    if (mp)
830
        libvlc_media_list_player_play(mp);
831
832
}

Sébastien Toque's avatar
Sébastien Toque committed
833
void Java_org_videolan_vlc_LibVLC_pause(JNIEnv *env, jobject thiz)
834
{
835
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
836
    if (mp)
837
        libvlc_media_list_player_pause(mp);
838
839
}

Sébastien Toque's avatar
Sébastien Toque committed
840
void Java_org_videolan_vlc_LibVLC_stop(JNIEnv *env, jobject thiz)
841
{
842
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
843
    if (mp)
844
        libvlc_media_list_player_stop(mp);
845
846
}

Edward Wang's avatar
Edward Wang committed
847
848
849
850
851
852
853
854
855
856
857
858
859
860
void Java_org_videolan_vlc_LibVLC_previous(JNIEnv *env, jobject thiz)
{
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
    if (mp)
        libvlc_media_list_player_previous(mp);
}

void Java_org_videolan_vlc_LibVLC_next(JNIEnv *env, jobject thiz)
{
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
    if (mp)
        libvlc_media_list_player_next(mp);
}

Sébastien Toque's avatar
Sébastien Toque committed
861
jint Java_org_videolan_vlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
862
{
863
864
865
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
866
867
868
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
869
jint Java_org_videolan_vlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
870
{
871
872
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
873
        //Returns 0 if the volume was set, -1 if it was out of range or error
874
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
875
876
877
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
878
jlong Java_org_videolan_vlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
879
{
880
881
882
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
883
884
885
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
886
void Java_org_videolan_vlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
887
{
888
889
890
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
891
892
}

Sébastien Toque's avatar
Sébastien Toque committed
893
jfloat Java_org_videolan_vlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
894
{
895
896
897
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
898
899
900
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
901
void Java_org_videolan_vlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
902
{
903
904
905
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
906
907
}

Sébastien Toque's avatar
Sébastien Toque committed
908
jlong Java_org_videolan_vlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
909
{
910
911
912
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
913
914
915
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
916
jstring Java_org_videolan_vlc_LibVLC_version(JNIEnv* env, jobject thiz)
917
{
918
919
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
920

Sébastien Toque's avatar
Sébastien Toque committed
921
jstring Java_org_videolan_vlc_LibVLC_compiler(JNIEnv* env, jobject thiz)
922
923
924
925
{
    return (*env)->NewStringUTF(env, libvlc_get_compiler());
}

Sébastien Toque's avatar
Sébastien Toque committed
926
jstring Java_org_videolan_vlc_LibVLC_changeset(JNIEnv* env, jobject thiz)
927
928
929
{
    return (*env)->NewStringUTF(env, libvlc_get_changeset());
}
930

Sébastien Toque's avatar
Sébastien Toque committed
931
jint Java_org_videolan_vlc_LibVLC_getAudioTracksCount(JNIEnv *env, jobject thiz)
932
933
934
935
936
937
938
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_track_count(mp);
    return -1;
}

939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
jobjectArray Java_org_videolan_vlc_LibVLC_getAudioTrackDescription(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (!mp)
        return NULL;

    int i_nbTracks = libvlc_audio_get_track_count(mp) - 1;
    if (i_nbTracks < 0)
        i_nbTracks = 0;
    jobjectArray array = (*env)->NewObjectArray(env, i_nbTracks,
            (*env)->FindClass(env, "java/lang/String"),
            NULL);

    libvlc_track_description_t *first = libvlc_audio_get_track_description(mp);
    libvlc_track_description_t *desc = first != NULL ? first->p_next : NULL;
    unsigned i;
    for (i = 0; i < i_nbTracks; ++i)
    {
        jstring name = (*env)->NewStringUTF(env, desc->psz_name);
        (*env)->SetObjectArrayElement(env, array, i, name);
        desc = desc->p_next;
    }
    libvlc_track_description_list_release(first);
    return array;
}

jint Java_org_videolan_vlc_LibVLC_getAudioTrack(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_audio_get_track(mp);
    return -1;
}

jint Java_org_videolan_vlc_LibVLC_setAudioTrack(JNIEnv *env, jobject thiz, jint index)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_audio_set_track(mp, index);
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
981
jint Java_org_videolan_vlc_LibVLC_getVideoTracksCount(JNIEnv *env, jobject thiz)
982
983
984
985
986
987
988
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_video_get_track_count(mp);
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
989
990
991
992
993
994
995
996
997
998
999
1000
jobjectArray Java_org_videolan_vlc_LibVLC_getSpuTrackDescription(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (!mp)
        return NULL;

    int i_nbTracks = libvlc_video_get_spu_count(mp);
    jobjectArray array = (*env)->NewObjectArray(env, i_nbTracks,
            (*env)->FindClass(env, "java/lang/String"),
            NULL);

    libvlc_track_description_t *first = libvlc_video_get_spu_description(mp);