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

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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);
}

64
libvlc_media_t *new_media(jint instance, JNIEnv *env, jobject thiz, jstring fileLocation, bool noOmx, bool noVideo)
65
{
66
67
    libvlc_instance_t *libvlc = (libvlc_instance_t*)instance;
    jboolean isCopy;
68
69
70
    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);
71
72
73
    if (!p_md)
        return NULL;

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
    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");
        }
91
92
        if (noVideo)
            libvlc_media_add_option(p_md, ":no-video");
93
    }
94
    return p_md;
95
96
}

Rafaël Carré's avatar
Rafaël Carré committed
97
static libvlc_media_player_t *getMediaPlayer(JNIEnv *env, jobject thiz)
98
99
100
101
{
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
                                          "mMediaPlayerInstance", "I");
102
    return (libvlc_media_player_t*)(*env)->GetIntField(env, thiz, fieldMP);
103
104
}

Rafaël Carré's avatar
Rafaël Carré committed
105
static void unsetMediaPlayer(JNIEnv *env, jobject thiz)
106
107
108
109
{
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
                                          "mMediaPlayerInstance", "I");
Rafaël Carré's avatar
Rafaël Carré committed
110
111
112
113
114
115
    (*env)->SetIntField(env, thiz, fieldMP, 0);
}

static void releaseMediaPlayer(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
Rafaël Carré's avatar
Rafaël Carré committed
116
    if (mp)
117
118
119
    {
        libvlc_media_player_stop(mp);
        libvlc_media_player_release(mp);
Rafaël Carré's avatar
Rafaël Carré committed
120
        unsetMediaPlayer(env, thiz);
121
122
123
    }
}

124
125
126
127
128
/* 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;
129

130
static jobject eventManagerInstance = NULL;
131

132
133
static pthread_mutex_t vout_android_lock;
static void *vout_android_surf = NULL;
134
static void *vout_android_gui = NULL;
135

136
void *jni_LockAndGetAndroidSurface() {
137
    pthread_mutex_lock(&vout_android_lock);
Ludovic Fauvet's avatar
Ludovic Fauvet committed
138
    return vout_android_surf;
139
140
}

141
void jni_UnlockAndroidSurface() {
142
143
144
    pthread_mutex_unlock(&vout_android_lock);
}

145
void jni_SetAndroidSurfaceSize(int width, int height)
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
{
    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);
}

162
163
164
static void vlc_event_callback(const libvlc_event_t *ev, void *data)
{
    JNIEnv *env;
165
    JavaVM *myVm = data;
166

167
168
169
170
171
    int ev_opt_data = 0;
    if(ev->type == libvlc_MediaPlayerVout) {
        /* For determining the vout/ES track change */
        ev_opt_data = ev->u.media_player_vout.new_count;
    }
172
    bool isAttached = false;
173

174
    if (eventManagerInstance == NULL)
Sébastien Toque's avatar
Sébastien Toque committed
175
        return;
176

177
    int status = (*myVm)->GetEnv(myVm, (void**) &env, JNI_VERSION_1_2);
178
    if (status < 0) {
179
        LOGD("vlc_event_callback: failed to get JNI environment, "
180
181
182
183
             "assuming native thread");
        status = (*myVm)->AttachCurrentThread(myVm, &env, NULL);
        if (status < 0)
            return;
184
        isAttached = true;
185
186
187
188
189
190
    }

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

    /* Find the callback ID */
195
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(II)V");
196
    if (methodID) {
197
        (*env)->CallVoidMethod(env, eventManagerInstance, methodID, ev->type, ev_opt_data);
198
    } else {
199
200
201
        LOGE("EventManager: failed to get the callback method");
    }

202
203
204
end:
    if (isAttached)
        (*myVm)->DetachCurrentThread(myVm);
205
206
}

207
jint JNI_OnLoad(JavaVM *vm, void *reserved)
208
{
209
210
211
    // Keep a reference on the Java VM.
    myVm = vm;

212
213
    pthread_mutex_init(&vout_android_lock, NULL);

214
    LOGD("JNI interface loaded.");
215
    return JNI_VERSION_1_2;
216
217
}

218
219
220
221
void JNI_OnUnload(JavaVM* vm, void* reserved) {
    pthread_mutex_destroy(&vout_android_lock);
}

Sébastien Toque's avatar
Sébastien Toque committed
222
void Java_org_videolan_vlc_LibVLC_attachSurface(JNIEnv *env, jobject thiz, jobject surf, jobject gui, jint width, jint height) {
223
224
225
226
227
228
229
    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) {
230
        jthrowable exp = (*env)->ExceptionOccurred(env);
231
232
233
234
235
236
237
238
        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);
239
240

    vout_android_gui = (*env)->NewGlobalRef(env, gui);
241
242
243
    pthread_mutex_unlock(&vout_android_lock);
}

Sébastien Toque's avatar
Sébastien Toque committed
244
void Java_org_videolan_vlc_LibVLC_detachSurface(JNIEnv *env, jobject thiz) {
245
246
    pthread_mutex_lock(&vout_android_lock);
    vout_android_surf = NULL;
247
248
249
    if (vout_android_gui != NULL)
        (*env)->DeleteGlobalRef(env, vout_android_gui);
    vout_android_gui = NULL;
250
251
    pthread_mutex_unlock(&vout_android_lock);
}
252

253
254
static void debug_log(void *data, int level, const char *fmt, va_list ap)
{
Rafaël Carré's avatar
Rafaël Carré committed
255
    bool *verbose = data;
256
257
258
259
260
261
262
263
264
265
266
267
268

    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
269
    if (!*verbose && prio < ANDROID_LOG_ERROR)
270
271
        return;

272
273
274
275
    __android_log_vprint(prio, "VLC", fmt, ap);
}

static libvlc_log_subscriber_t debug_subscriber;
Rafaël Carré's avatar
Rafaël Carré committed
276
static bool verbosity;
277

278
279
void Java_org_videolan_vlc_LibVLC_changeVerbosity(JNIEnv *env, jobject thiz, jboolean verbose)
{
Rafaël Carré's avatar
Rafaël Carré committed
280
    verbosity = verbose;
281
    libvlc_log_unsubscribe(&debug_subscriber);
Rafaël Carré's avatar
Rafaël Carré committed
282
    libvlc_log_subscribe(&debug_subscriber, debug_log, &verbosity);
283
284
285
}

void Java_org_videolan_vlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz, jboolean verbose)
286
{
287
288
289
290
291
    //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;

292
293
294
    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
295
296
    verbosity = verbose;
    libvlc_log_subscribe(&debug_subscriber, debug_log, &verbosity);
297

298
    /* Don't add any invalid options, otherwise it causes LibVLC to crash */
299
    const char *argv[] = {
300
        "-I", "dummy",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
301
302
303
        "--no-osd",
        "--no-video-title-show",
        "--no-stats",
304
305
        "--no-plugins-cache",
        "--no-drop-late-frames",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
306
        "--avcodec-fast",
307
        enable_time_stretch ? "--audio-time-stretch" : "--no-audio-time-stretch",
308
        use_opensles ? "--aout=opensles" : "--aout=android_audiotrack",
309
    };
310
    libvlc_instance_t *instance = libvlc_new(sizeof(argv) / sizeof(*argv), argv);
311
312

    jclass clazz = (*env)->GetObjectClass(env, thiz);
313
    jfieldID field = (*env)->GetFieldID(env, clazz, "mLibVlcInstance", "I");
314
    (*env)->SetIntField(env, thiz, field, (jint) instance);
315

316
317
    if (!instance)
    {
Sébastien Toque's avatar
Sébastien Toque committed
318
        jclass exc = (*env)->FindClass(env, "org/videolan/vlc/LibVlcException");
319
320
321
322
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
323
324
}

325
326
327
328
329
330
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 */
331
332
333
334
335
    char* psz_location;
    if(unlikely( strstr( psz_path, "://" ) ))
        psz_location = strdup(psz_path);
    else
        psz_location = vlc_path2uri(psz_path, "file");
336
337
338
339
340
341
342
    /* Box into jstring */
    jstring t = (*env)->NewStringUTF(env, psz_location);
    /* Clean up */
    (*env)->ReleaseStringUTFChars(env, path, psz_path);
    free(psz_location);
    return t;
}
343

Sébastien Toque's avatar
Sébastien Toque committed
344
void Java_org_videolan_vlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
345
{
346
    releaseMediaPlayer(env, thiz);
347
348
349
350
351
352
353
354
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID field = (*env)->GetFieldID(env, clazz, "mLibVlcInstance", "I");
    jint libVlcInstance = (*env)->GetIntField(env, thiz, field);
    if (!libVlcInstance)
        return; // Already destroyed

    libvlc_instance_t *instance = (libvlc_instance_t*) libVlcInstance;
    libvlc_release(instance);
355
    libvlc_log_unsubscribe(&debug_subscriber);
356
357

    (*env)->SetIntField(env, thiz, field, 0);
358
359
}

Sébastien Toque's avatar
Sébastien Toque committed
360
void Java_org_videolan_vlc_LibVLC_detachEventManager(JNIEnv *env, jobject thiz)
361
362
363
364
365
366
367
{
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }
}

Sébastien Toque's avatar
Sébastien Toque committed
368
void Java_org_videolan_vlc_LibVLC_setEventManager(JNIEnv *env, jobject thiz, jobject eventManager)
369
{
370
371
372
373
374
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }

375
376
377
378
379
380
    jclass cls = (*env)->GetObjectClass(env, eventManager);
    if (!cls) {
        LOGE("setEventManager: failed to get class reference");
        return;
    }

381
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(II)V");
382
383
384
385
    if (!methodID) {
        LOGE("setEventManager: failed to get the callback method");
        return;
    }
386

387
388
389
    eventManagerInstance = (*env)->NewGlobalRef(env, eventManager);
}

390
391
392
393
394
395
396
397
398
399
void setInt(JNIEnv *env, jobject item, const char* field, int 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");
Rafaël Carré's avatar
Rafaël Carré committed
400
401
    if (fieldId == NULL)
        return;
402
403
404
405

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

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
void setLong(JNIEnv *env, jobject item, const char* field, long 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);
}

422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
void SetFloat(JNIEnv *env, jobject item, const char* field, float 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);
}

438
439
440
441
442
443
444
445
446
447
448
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;");
Rafaël Carré's avatar
Rafaël Carré committed
449
450
    if (fieldId == NULL)
        return;
451
452
453
454
455
456
457
458

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

Sébastien Toque's avatar
Sébastien Toque committed
459
460
jobjectArray Java_org_videolan_vlc_LibVLC_readMediaMeta(JNIEnv *env,
                                                        jobject thiz, jint instance, jstring mrl)
Rafaël Carré's avatar
Rafaël Carré committed
461
462
463
464
465
{
    jobjectArray array = (*env)->NewObjectArray(env, 8,
            (*env)->FindClass(env, "java/lang/String"),
            (*env)->NewStringUTF(env, ""));

466
    libvlc_media_t *m = new_media(instance, env, thiz, mrl, false, false);
467
468
    if (!m)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
469
        LOGE("readMediaMeta: Could not create the media!");
470
        return array;
471
472
473
    }

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

475
    static const char str[][7] = {
Rafaël Carré's avatar
Rafaël Carré committed
476
477
        "artist", "album", "title", "genre",
    };
478
    static const libvlc_meta_t meta_id[] = {
Rafaël Carré's avatar
Rafaël Carré committed
479
480
481
482
483
        libvlc_meta_Artist,
        libvlc_meta_Album,
        libvlc_meta_Title,
        libvlc_meta_Genre,
    };
484
    for (int i=0; i < sizeof(str) / sizeof(*str); i++) {
Rafaël Carré's avatar
Rafaël Carré committed
485
486
        char *meta = libvlc_media_get_meta(m, meta_id[i]);
        if (!meta)
487
            meta = strdup("");
Rafaël Carré's avatar
Rafaël Carré committed
488
489
490
491
492
493
494
495
496
497
498
499
500

        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
501
void Java_org_videolan_vlc_LibVLC_readMedia(JNIEnv *env, jobject thiz,
502
                                            jint instance, jstring mrl, jboolean novideo)
503
{
504
505
506
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

507
    /* Create a new item */
508
    libvlc_media_t *m = new_media(instance, env, thiz, mrl, false, novideo);
509
510
    if (!m)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
511
        LOGE("readMedia: Could not create the media!");
512
513
        return;
    }
514

515
    /* Create a media player playing environment */
516
517
    libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)instance);

518
519
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz);

520
    libvlc_media_player_set_media(mp, m);
ivoire's avatar
ivoire committed
521

522
523
524
525
    //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 )
526
    {
Sébastien Toque's avatar
Sébastien Toque committed
527
528
529
        libvlc_audio_set_callbacks(mp, aout_play, NULL, NULL, NULL, NULL,
                                   (void*) myJavaLibVLC);
        libvlc_audio_set_format_callbacks(mp, aout_open, aout_close);
530
531
    }

532
533
534
    /* No need to keep the media now */
    libvlc_media_release(m);

535
536
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
537
538
539
540
541
    static const libvlc_event_type_t mp_events[] = {
        libvlc_MediaPlayerPlaying,
        libvlc_MediaPlayerPaused,
        libvlc_MediaPlayerEndReached,
        libvlc_MediaPlayerStopped,
542
        libvlc_MediaPlayerVout,
543
    };
544
    int i;
545
546
547
    for (i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); ++i)
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

548
549
550
551
552
553
    /* Keep a pointer to this media player */
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID field = (*env)->GetFieldID(env, clazz,
                                        "mMediaPlayerInstance", "I");
    (*env)->SetIntField(env, thiz, field, (jint) mp);

554
555
556
    libvlc_media_player_play(mp);
}

Edward Wang's avatar
Edward Wang committed
557
558
559
560
561
562
563
564
565
566
567
568
569
570
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
571
jboolean Java_org_videolan_vlc_LibVLC_hasVideoTrack(JNIEnv *env, jobject thiz,
572
                                                    jint i_instance, jstring fileLocation)
573
{
574
    /* Create a new item and assign it to the media player. */
575
    libvlc_media_t *p_m = new_media(i_instance, env, thiz, fileLocation, false, false);
576
577
    if (p_m == NULL)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
578
        LOGE("Could not create the media!");
Rafaël Carré's avatar
Rafaël Carré committed
579
        return JNI_FALSE;
580
581
582
583
584
    }

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

586
587
588
589
590
591
592
593
594
595
    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
596

597
598
599
600
601
    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);
602
603
604
605
606
607
608
609
610

    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);
    }
611
612
    pthread_mutex_unlock(&monitor->doneMutex);

613
614
615
616
617
    int i_nbTracks;
    if( mp_alive )
        i_nbTracks = libvlc_video_get_track_count(p_mp);
    else
        i_nbTracks = -1;
618
619
    LOGI("Number of video tracks: %d",i_nbTracks);

Edward Wang's avatar
Edward Wang committed
620
    libvlc_event_detach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
621
622
    libvlc_media_player_stop(p_mp);
    libvlc_media_player_release(p_mp);
Rafaël Carré's avatar
Rafaël Carré committed
623
624
    libvlc_media_release(p_m);

Edward Wang's avatar
Edward Wang committed
625
626
627
628
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);

629
630
    if(i_nbTracks > 0)
        return JNI_TRUE;
631
632
    else if(i_nbTracks < 0)
        (*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/IOException"), "VLC can't open the file");
633
634
    else
        return JNI_FALSE;
635
636
}

637
638
639
640
641
642
643
jobjectArray Java_org_videolan_vlc_LibVLC_readTracksInfo(JNIEnv *env, jobject thiz,
                                                         jint instance, jstring mrl)
{
    /* get java class */
    jclass cls = (*env)->FindClass( env, "org/videolan/vlc/TrackInfo" );
    if ( !cls )
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
644
        LOGE("Failed to load class (org/videolan/vlc/TrackInfo)" );
645
646
647
648
649
650
651
652
653
654
655
656
        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. */
657
    libvlc_media_t *p_m = new_media(instance, env, thiz, mrl, false, false);
658
659
    if (p_m == NULL)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
660
        LOGE("Could not create the media!");
661
662
663
664
665
666
667
668
        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);
669
    jobjectArray array = (*env)->NewObjectArray(env, i_nbTracks + 1, cls, NULL);
670
671
672
673

    unsigned i;
    if (array != NULL)
    {
674
        for (i = 0; i <= i_nbTracks; ++i)
675
676
677
678
679
        {
            jobject item = (*env)->NewObject(env, cls, clsCtor);
            if (item == NULL)
                continue;
            (*env)->SetObjectArrayElement(env, array, i, item);
680
681
682
683
684
685
686
687
688
689

            // 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));
690
                setString(env, item, "ArtworkURL", libvlc_media_get_meta(p_m, libvlc_meta_ArtworkURL));
691
692
693
                continue;
            }

694
695
696
            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));
697
            setString(env, item, "Language", p_tracks[i].psz_language);
698
699
700
701
702

            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);
703
                SetFloat(env, item, "Framerate", p_tracks[i].u.video.f_frame_rate);
704
705
706
707
708
709
710
711
712
            }
            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);
            }
        }
    }

713
    libvlc_media_tracks_info_release(p_tracks, i_nbTracks);
714
715
716
717
    libvlc_media_release(p_m);
    return array;
}

718
719
jlong Java_org_videolan_vlc_LibVLC_getLengthFromLocation(JNIEnv *env, jobject thiz,
                                                     jint i_instance, jstring fileLocation)
720
{
721
722
723
724
725
    jlong length = 0;
    struct length_change_monitor *monitor;
    monitor = malloc(sizeof(*monitor));
    if (!monitor)
        return 0;
726
727

    /* Initialize pthread variables. */
728
729
730
    pthread_mutex_init(&monitor->doneMutex, NULL);
    pthread_cond_init(&monitor->doneCondVar, NULL);
    monitor->length_changed = false;
731

732
    /* Create a new item and assign it to the media player. */
733
    libvlc_media_t *m = new_media(i_instance, env, thiz, fileLocation, false, false);
734
    if (m == NULL)
735
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
736
        LOGE("Could not create the media to play!");
737
        goto end;
738
739
    }

740
    /* Create a media player playing environment */
741
    libvlc_media_player_t *mp = libvlc_media_player_new_from_media (m);
742
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
743
    libvlc_event_attach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
744
745
    libvlc_media_release (m);
    libvlc_media_player_play( mp );
746
747
748
749
750
751
752

    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 );
753
    libvlc_media_player_stop( mp );
754
    libvlc_media_player_release( mp );
755

756
757
758
759
end:
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);
760
761

    return length;
762
763
}

Sébastien Toque's avatar
Sébastien Toque committed
764
jboolean Java_org_videolan_vlc_LibVLC_hasMediaPlayer(JNIEnv *env, jobject thiz)
765
{
766
    return !!getMediaPlayer(env, thiz);
767
768
}

Sébastien Toque's avatar
Sébastien Toque committed
769
jboolean Java_org_videolan_vlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
770
{
771
772
773
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_playing(mp);
774
    return 0;
775
776
}

Sébastien Toque's avatar
Sébastien Toque committed
777
jboolean Java_org_videolan_vlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
778
{
779
780
781
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_seekable(mp);
782
    return 0;
783
784
}

Sébastien Toque's avatar
Sébastien Toque committed
785
void Java_org_videolan_vlc_LibVLC_play(JNIEnv *env, jobject thiz)
786
{
787
788
789
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_play(mp);
790
791
}

Sébastien Toque's avatar
Sébastien Toque committed
792
void Java_org_videolan_vlc_LibVLC_pause(JNIEnv *env, jobject thiz)
793
{
794
795
796
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_pause(mp);
797
798
}

Sébastien Toque's avatar
Sébastien Toque committed
799
void Java_org_videolan_vlc_LibVLC_stop(JNIEnv *env, jobject thiz)
800
{
801
802
803
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_stop(mp);
804
805
}

Sébastien Toque's avatar
Sébastien Toque committed
806
jint Java_org_videolan_vlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
807
{
808
809
810
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
811
812
813
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
814
jint Java_org_videolan_vlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
815
{
816
817
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
818
        //Returns 0 if the volume was set, -1 if it was out of range or error
819
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
820
821
822
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
823
jlong Java_org_videolan_vlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
824
{
825
826
827
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
828
829
830
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
831
void Java_org_videolan_vlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
832
{
833
834
835
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
836
837
}

Sébastien Toque's avatar
Sébastien Toque committed
838
jfloat Java_org_videolan_vlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
839
{
840
841
842
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
843
844
845
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
846
void Java_org_videolan_vlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
847
{
848
849
850
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
851
852
}

Sébastien Toque's avatar
Sébastien Toque committed
853
jlong Java_org_videolan_vlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
854
{
855
856
857
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
858
859
860
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
861
jstring Java_org_videolan_vlc_LibVLC_version(JNIEnv* env, jobject thiz)
862
{
863
864
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
865

Sébastien Toque's avatar
Sébastien Toque committed
866
jstring Java_org_videolan_vlc_LibVLC_compiler(JNIEnv* env, jobject thiz)
867
868
869
870
{
    return (*env)->NewStringUTF(env, libvlc_get_compiler());
}

Sébastien Toque's avatar
Sébastien Toque committed
871
jstring Java_org_videolan_vlc_LibVLC_changeset(JNIEnv* env, jobject thiz)
872
873
874
{
    return (*env)->NewStringUTF(env, libvlc_get_changeset());
}
875

Sébastien Toque's avatar
Sébastien Toque committed
876
jint Java_org_videolan_vlc_LibVLC_getAudioTracksCount(JNIEnv *env, jobject thiz)
877
878
879
880
881
882
883
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_track_count(mp);
    return -1;
}

884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
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
926
jint Java_org_videolan_vlc_LibVLC_getVideoTracksCount(JNIEnv *env, jobject thiz)
927
928
929
930
931
932
933
{
    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
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
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);
    libvlc_track_description_t *desc = first;
    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;
}

Sébastien Toque's avatar
Sébastien Toque committed
958
jint Java_org_videolan_vlc_LibVLC_getSpuTracksCount(JNIEnv *env, jobject thiz)
959
960
961
962
963
964
965
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_video_get_spu_count(mp);
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
jint Java_org_videolan_vlc_LibVLC_getSpuTrack(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_video_get_spu(mp);
    return -1;
}

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

982
void Java_org_videolan_vlc_LibVLC_nativeReadDirectory(JNIEnv *env, jobject thiz, jstring path, jobject arrayList)
983
984
985
986
987
988
{
    jboolean isCopy;
    /* Get C string */
    const char* psz_path = (*env)->GetStringUTFChars(env, path, &isCopy);

    DIR* p_dir = opendir(psz_path);
989
    (*env)->ReleaseStringUTFChars(env, path, psz_path);
990
    if(!p_dir)
991
992
993
994
        return;

    jclass arrayClass = (*env)->FindClass(env, "java/util/ArrayList");
    jmethodID methodID = (*env)->GetMethodID(env, arrayClass, "add", "(Ljava/lang/Object;)Z");
995
996

    struct dirent* p_dirent;
997
    jstring str;
998
999
1000
    while(1) {
        errno = 0;
        p_dirent = readdir(p_dir);