libvlcjni.c 32.4 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
}

Rafaël Carré's avatar
Rafaël Carré committed
173
static libvlc_media_player_t *getMediaPlayer(JNIEnv *env, jobject thiz)
174
{
Edward Wang's avatar
Edward Wang committed
175
    return (libvlc_media_player_t*)getLong(env, thiz, "mMediaPlayerInstance");
176
177
}

Rafaël Carré's avatar
Rafaël Carré committed
178
static void unsetMediaPlayer(JNIEnv *env, jobject thiz)
179
{
Edward Wang's avatar
Edward Wang committed
180
    setLong(env, thiz, "mMediaPlayerInstance", (jlong)0);
Rafaël Carré's avatar
Rafaël Carré committed
181
182
183
184
185
}

static void releaseMediaPlayer(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
Rafaël Carré's avatar
Rafaël Carré committed
186
    if (mp)
187
188
189
    {
        libvlc_media_player_stop(mp);
        libvlc_media_player_release(mp);
Rafaël Carré's avatar
Rafaël Carré committed
190
        unsetMediaPlayer(env, thiz);
191
192
193
    }
}

194
195
196
197
198
/* 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;
199

200
static jobject eventManagerInstance = NULL;
201

202
203
static pthread_mutex_t vout_android_lock;
static void *vout_android_surf = NULL;
204
static void *vout_android_gui = NULL;
205

206
void *jni_LockAndGetAndroidSurface() {
207
    pthread_mutex_lock(&vout_android_lock);
Ludovic Fauvet's avatar
Ludovic Fauvet committed
208
    return vout_android_surf;
209
210
}

211
void jni_UnlockAndroidSurface() {
212
213
214
    pthread_mutex_unlock(&vout_android_lock);
}

215
void jni_SetAndroidSurfaceSize(int width, int height)
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
{
    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);
}

232
233
234
static void vlc_event_callback(const libvlc_event_t *ev, void *data)
{
    JNIEnv *env;
235
    JavaVM *myVm = data;
236

237
238
239
240
241
    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;
    }
242
    bool isAttached = false;
243

244
    if (eventManagerInstance == NULL)
Sébastien Toque's avatar
Sébastien Toque committed
245
        return;
246

247
    int status = (*myVm)->GetEnv(myVm, (void**) &env, JNI_VERSION_1_2);
248
    if (status < 0) {
249
        LOGD("vlc_event_callback: failed to get JNI environment, "
250
251
252
253
             "assuming native thread");
        status = (*myVm)->AttachCurrentThread(myVm, &env, NULL);
        if (status < 0)
            return;
254
        isAttached = true;
255
256
257
258
259
260
    }

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

    /* Find the callback ID */
265
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(II)V");
266
    if (methodID) {
267
        (*env)->CallVoidMethod(env, eventManagerInstance, methodID, ev->type, ev_opt_data);
268
    } else {
269
270
271
        LOGE("EventManager: failed to get the callback method");
    }

272
273
274
end:
    if (isAttached)
        (*myVm)->DetachCurrentThread(myVm);
275
276
}

277
jint JNI_OnLoad(JavaVM *vm, void *reserved)
278
{
279
280
281
    // Keep a reference on the Java VM.
    myVm = vm;

282
283
    pthread_mutex_init(&vout_android_lock, NULL);

284
    LOGD("JNI interface loaded.");
285
    return JNI_VERSION_1_2;
286
287
}

288
289
290
291
void JNI_OnUnload(JavaVM* vm, void* reserved) {
    pthread_mutex_destroy(&vout_android_lock);
}

Sébastien Toque's avatar
Sébastien Toque committed
292
void Java_org_videolan_vlc_LibVLC_attachSurface(JNIEnv *env, jobject thiz, jobject surf, jobject gui, jint width, jint height) {
293
294
295
296
297
298
299
    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) {
300
        jthrowable exp = (*env)->ExceptionOccurred(env);
301
302
303
304
305
306
307
308
        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);
309
310

    vout_android_gui = (*env)->NewGlobalRef(env, gui);
311
312
313
    pthread_mutex_unlock(&vout_android_lock);
}

Sébastien Toque's avatar
Sébastien Toque committed
314
void Java_org_videolan_vlc_LibVLC_detachSurface(JNIEnv *env, jobject thiz) {
315
316
    pthread_mutex_lock(&vout_android_lock);
    vout_android_surf = NULL;
317
318
319
    if (vout_android_gui != NULL)
        (*env)->DeleteGlobalRef(env, vout_android_gui);
    vout_android_gui = NULL;
320
321
    pthread_mutex_unlock(&vout_android_lock);
}
322

323
324
static void debug_log(void *data, int level, const char *fmt, va_list ap)
{
Rafaël Carré's avatar
Rafaël Carré committed
325
    bool *verbose = data;
326
327
328
329
330
331
332
333
334
335
336
337
338

    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
339
    if (!*verbose && prio < ANDROID_LOG_ERROR)
340
341
        return;

342
343
344
345
    __android_log_vprint(prio, "VLC", fmt, ap);
}

static libvlc_log_subscriber_t debug_subscriber;
Rafaël Carré's avatar
Rafaël Carré committed
346
static bool verbosity;
347

348
349
void Java_org_videolan_vlc_LibVLC_changeVerbosity(JNIEnv *env, jobject thiz, jboolean verbose)
{
Rafaël Carré's avatar
Rafaël Carré committed
350
    verbosity = verbose;
351
    libvlc_log_unsubscribe(&debug_subscriber);
Rafaël Carré's avatar
Rafaël Carré committed
352
    libvlc_log_subscribe(&debug_subscriber, debug_log, &verbosity);
353
354
355
}

void Java_org_videolan_vlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz, jboolean verbose)
356
{
357
358
359
360
361
    //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;

362
363
364
    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
365
366
    verbosity = verbose;
    libvlc_log_subscribe(&debug_subscriber, debug_log, &verbosity);
367

368
    /* Don't add any invalid options, otherwise it causes LibVLC to crash */
369
    const char *argv[] = {
370
        "-I", "dummy",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
371
372
373
        "--no-osd",
        "--no-video-title-show",
        "--no-stats",
374
375
        "--no-plugins-cache",
        "--no-drop-late-frames",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
376
        "--avcodec-fast",
377
        enable_time_stretch ? "--audio-time-stretch" : "--no-audio-time-stretch",
378
        use_opensles ? "--aout=opensles" : "--aout=android_audiotrack",
379
    };
380
    libvlc_instance_t *instance = libvlc_new(sizeof(argv) / sizeof(*argv), argv);
381

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

384
385
    if (!instance)
    {
Sébastien Toque's avatar
Sébastien Toque committed
386
        jclass exc = (*env)->FindClass(env, "org/videolan/vlc/LibVlcException");
387
388
389
390
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
391
392
}

393
394
395
396
397
398
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 */
399
400
401
402
403
    char* psz_location;
    if(unlikely( strstr( psz_path, "://" ) ))
        psz_location = strdup(psz_path);
    else
        psz_location = vlc_path2uri(psz_path, "file");
404
405
406
407
408
409
410
    /* Box into jstring */
    jstring t = (*env)->NewStringUTF(env, psz_location);
    /* Clean up */
    (*env)->ReleaseStringUTFChars(env, path, psz_path);
    free(psz_location);
    return t;
}
411

Sébastien Toque's avatar
Sébastien Toque committed
412
void Java_org_videolan_vlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
413
{
414
    releaseMediaPlayer(env, thiz);
Edward Wang's avatar
Edward Wang committed
415
    jlong libVlcInstance = getLong(env, thiz, "mLibVlcInstance");
416
417
418
419
420
    if (!libVlcInstance)
        return; // Already destroyed

    libvlc_instance_t *instance = (libvlc_instance_t*) libVlcInstance;
    libvlc_release(instance);
421
    libvlc_log_unsubscribe(&debug_subscriber);
422

Edward Wang's avatar
Edward Wang committed
423
    setLong(env, thiz, "mLibVlcInstance", 0);
424
425
}

Sébastien Toque's avatar
Sébastien Toque committed
426
void Java_org_videolan_vlc_LibVLC_detachEventManager(JNIEnv *env, jobject thiz)
427
428
429
430
431
432
433
{
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }
}

Sébastien Toque's avatar
Sébastien Toque committed
434
void Java_org_videolan_vlc_LibVLC_setEventManager(JNIEnv *env, jobject thiz, jobject eventManager)
435
{
436
437
438
439
440
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }

441
442
443
444
445
446
    jclass cls = (*env)->GetObjectClass(env, eventManager);
    if (!cls) {
        LOGE("setEventManager: failed to get class reference");
        return;
    }

447
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(II)V");
448
449
450
451
    if (!methodID) {
        LOGE("setEventManager: failed to get the callback method");
        return;
    }
452

453
454
455
    eventManagerInstance = (*env)->NewGlobalRef(env, eventManager);
}

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

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

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

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

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

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

512
    /* Create a media player playing environment */
513
514
    libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)instance);

515
516
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz);

517
    libvlc_media_player_set_media(mp, m);
ivoire's avatar
ivoire committed
518

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

529
530
531
    /* No need to keep the media now */
    libvlc_media_release(m);

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

545
    /* Keep a pointer to this media player */
Edward Wang's avatar
Edward Wang committed
546
    setLong(env, thiz, "mMediaPlayerInstance", (jlong) mp);
547

548
549
550
    libvlc_media_player_play(mp);
}

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

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

580
581
582
583
584
585
586
587
588
589
    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
590

591
592
593
594
595
    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);
596
597
598
599
600
601
602
603
604

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

607
608
609
610
611
    int i_nbTracks;
    if( mp_alive )
        i_nbTracks = libvlc_video_get_track_count(p_mp);
    else
        i_nbTracks = -1;
612
613
    LOGI("Number of video tracks: %d",i_nbTracks);

Edward Wang's avatar
Edward Wang committed
614
    libvlc_event_detach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
615
616
    libvlc_media_player_stop(p_mp);
    libvlc_media_player_release(p_mp);
Rafaël Carré's avatar
Rafaël Carré committed
617
618
    libvlc_media_release(p_m);

Edward Wang's avatar
Edward Wang committed
619
620
621
622
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);

623
624
    if(i_nbTracks > 0)
        return JNI_TRUE;
625
626
    else if(i_nbTracks < 0)
        (*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/IOException"), "VLC can't open the file");
627
628
    else
        return JNI_FALSE;
629
630
}

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

    unsigned i;
    if (array != NULL)
    {
668
        for (i = 0; i <= i_nbTracks; ++i)
669
670
671
672
673
        {
            jobject item = (*env)->NewObject(env, cls, clsCtor);
            if (item == NULL)
                continue;
            (*env)->SetObjectArrayElement(env, array, i, item);
674
675
676
677
678
679
680
681
682
683

            // 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));
684
                setString(env, item, "ArtworkURL", libvlc_media_get_meta(p_m, libvlc_meta_ArtworkURL));
685
686
687
                continue;
            }

688
689
690
            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));
691
            setString(env, item, "Language", p_tracks[i].psz_language);
692
693
694
695
696

            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);
697
                setFloat(env, item, "Framerate", p_tracks[i].u.video.f_frame_rate);
698
699
700
701
702
703
704
705
706
            }
            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);
            }
        }
    }

707
    libvlc_media_tracks_info_release(p_tracks, i_nbTracks);
708
709
710
711
    libvlc_media_release(p_m);
    return array;
}

712
713
jlong Java_org_videolan_vlc_LibVLC_getLengthFromLocation(JNIEnv *env, jobject thiz,
                                                     jint i_instance, jstring fileLocation)
714
{
715
716
717
718
719
    jlong length = 0;
    struct length_change_monitor *monitor;
    monitor = malloc(sizeof(*monitor));
    if (!monitor)
        return 0;
720
721

    /* Initialize pthread variables. */
722
723
724
    pthread_mutex_init(&monitor->doneMutex, NULL);
    pthread_cond_init(&monitor->doneCondVar, NULL);
    monitor->length_changed = false;
725

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

734
    /* Create a media player playing environment */
735
    libvlc_media_player_t *mp = libvlc_media_player_new_from_media (m);
736
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
737
    libvlc_event_attach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
738
739
    libvlc_media_release (m);
    libvlc_media_player_play( mp );
740
741
742
743
744
745
746

    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 );
747
    libvlc_media_player_stop( mp );
748
    libvlc_media_player_release( mp );
749

750
751
752
753
end:
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);
754
755

    return length;
756
757
}

Sébastien Toque's avatar
Sébastien Toque committed
758
jboolean Java_org_videolan_vlc_LibVLC_hasMediaPlayer(JNIEnv *env, jobject thiz)
759
{
760
    return !!getMediaPlayer(env, thiz);
761
762
}

Sébastien Toque's avatar
Sébastien Toque committed
763
jboolean Java_org_videolan_vlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
764
{
765
766
767
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_playing(mp);
768
    return 0;
769
770
}

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

Sébastien Toque's avatar
Sébastien Toque committed
779
void Java_org_videolan_vlc_LibVLC_play(JNIEnv *env, jobject thiz)
780
{
781
782
783
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_play(mp);
784
785
}

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

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

Sébastien Toque's avatar
Sébastien Toque committed
800
jint Java_org_videolan_vlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
801
{
802
803
804
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
805
806
807
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
808
jint Java_org_videolan_vlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
809
{
810
811
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
812
        //Returns 0 if the volume was set, -1 if it was out of range or error
813
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
814
815
816
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
817
jlong Java_org_videolan_vlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
818
{
819
820
821
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
822
823
824
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
825
void Java_org_videolan_vlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
826
{
827
828
829
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
830
831
}

Sébastien Toque's avatar
Sébastien Toque committed
832
jfloat Java_org_videolan_vlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
833
{
834
835
836
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
837
838
839
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
840
void Java_org_videolan_vlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
841
{
842
843
844
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
845
846
}

Sébastien Toque's avatar
Sébastien Toque committed
847
jlong Java_org_videolan_vlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
848
{
849
850
851
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
852
853
854
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
855
jstring Java_org_videolan_vlc_LibVLC_version(JNIEnv* env, jobject thiz)
856
{
857
858
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
859

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

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

Sébastien Toque's avatar
Sébastien Toque committed
870
jint Java_org_videolan_vlc_LibVLC_getAudioTracksCount(JNIEnv *env, jobject thiz)
871
872
873
874
875
876
877
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_track_count(mp);
    return -1;
}

878
879
880
881
882
883
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
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
920
jint Java_org_videolan_vlc_LibVLC_getVideoTracksCount(JNIEnv *env, jobject thiz)
921
922
923
924
925
926
927
{
    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
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
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
952
jint Java_org_videolan_vlc_LibVLC_getSpuTracksCount(JNIEnv *env, jobject thiz)
953
954
955
956
957
958
959
{
    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
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
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;
}
975

976
void Java_org_videolan_vlc_LibVLC_nativeReadDirectory(JNIEnv *env, jobject thiz, jstring path, jobject arrayList)
977
978
979
980
981
982
{
    jboolean isCopy;
    /* Get C string */
    const char* psz_path = (*env)->GetStringUTFChars(env, path, &isCopy);

    DIR* p_dir = opendir(psz_path);
983
    (*env)->ReleaseStringUTFChars(env, path, psz_path);
984
    if(!p_dir)
985
986
987
988
        return;

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

    struct dirent* p_dirent;
991
    jstring str;
992
993
994
995
996
997
998
999
1000
    while(1) {
        errno = 0;
        p_dirent = readdir(p_dir);
        if(p_dirent == NULL) {
            if(errno > 0) /* error reading this entry */
                continue;
            else if(errno == 0) /* end of stream */
                break;
        }