libvlcjni.c 26.7 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
#include <string.h>
22
#include <pthread.h>
23

24
#include <vlc/vlc.h>
25
26
#include <vlc_common.h>
#include <vlc_url.h>
27

28
#include <jni.h>
29

30
#include <android/api-level.h>
31

32
#include "libvlcjni.h"
ivoire's avatar
ivoire committed
33
#include "aout.h"
34
#include "utils.h"
35
36

#define LOG_TAG "VLC/JNI/main"
37
38
#include "log.h"

39
40
41
42
#define AOUT_AUDIOTRACK      0
#define AOUT_AUDIOTRACK_JAVA 1
#define AOUT_OPENSLES        2

43
libvlc_media_t *new_media(jint instance, JNIEnv *env, jobject thiz, jstring fileLocation, bool noOmx)
44
{
45
46
    libvlc_instance_t *libvlc = (libvlc_instance_t*)instance;
    jboolean isCopy;
47
48
49
    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);
50
51
52
    if (!p_md)
        return NULL;

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    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");
        }
70
    }
71
    return p_md;
72
73
}

Rafaël Carré's avatar
Rafaël Carré committed
74
static libvlc_media_player_t *getMediaPlayer(JNIEnv *env, jobject thiz)
75
76
77
78
{
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
                                          "mMediaPlayerInstance", "I");
79
    return (libvlc_media_player_t*)(*env)->GetIntField(env, thiz, fieldMP);
80
81
}

Rafaël Carré's avatar
Rafaël Carré committed
82
static void unsetMediaPlayer(JNIEnv *env, jobject thiz)
83
84
85
86
{
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
                                          "mMediaPlayerInstance", "I");
Rafaël Carré's avatar
Rafaël Carré committed
87
88
89
90
91
92
    (*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
93
    if (mp)
94
95
96
    {
        libvlc_media_player_stop(mp);
        libvlc_media_player_release(mp);
Rafaël Carré's avatar
Rafaël Carré committed
97
        unsetMediaPlayer(env, thiz);
98
99
100
    }
}

101
102
103
104
105
/* 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;
106

107
static jobject eventManagerInstance = NULL;
108

109
110
static pthread_mutex_t vout_android_lock;
static void *vout_android_surf = NULL;
111
static void *vout_android_gui = NULL;
112

113
void *jni_LockAndGetAndroidSurface() {
114
    pthread_mutex_lock(&vout_android_lock);
Ludovic Fauvet's avatar
Ludovic Fauvet committed
115
    return vout_android_surf;
116
117
}

118
void jni_UnlockAndroidSurface() {
119
120
121
    pthread_mutex_unlock(&vout_android_lock);
}

122
void jni_SetAndroidSurfaceSize(int width, int height)
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
{
    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);
}

139
140
141
static void vlc_event_callback(const libvlc_event_t *ev, void *data)
{
    JNIEnv *env;
142
    JavaVM *myVm = data;
143

144
    bool isAttached = false;
145

146
    if (eventManagerInstance == NULL)
Sébastien Toque's avatar
Sébastien Toque committed
147
        return;
148

149
    int status = (*myVm)->GetEnv(myVm, (void**) &env, JNI_VERSION_1_2);
150
    if (status < 0) {
151
        LOGD("vlc_event_callback: failed to get JNI environment, "
152
153
154
155
             "assuming native thread");
        status = (*myVm)->AttachCurrentThread(myVm, &env, NULL);
        if (status < 0)
            return;
156
        isAttached = true;
157
158
159
160
161
162
    }

    /* Get the object class */
    jclass cls = (*env)->GetObjectClass(env, eventManagerInstance);
    if (!cls) {
        LOGE("EventManager: failed to get class reference");
163
        goto end;
164
165
166
167
    }

    /* Find the callback ID */
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(I)V");
168
169
170
    if (methodID) {
        (*env)->CallVoidMethod(env, eventManagerInstance, methodID, ev->type);
    } else {
171
172
173
        LOGE("EventManager: failed to get the callback method");
    }

174
175
176
end:
    if (isAttached)
        (*myVm)->DetachCurrentThread(myVm);
177
178
}

179
jint JNI_OnLoad(JavaVM *vm, void *reserved)
180
{
181
182
183
    // Keep a reference on the Java VM.
    myVm = vm;

184
185
    pthread_mutex_init(&vout_android_lock, NULL);

186
    LOGD("JNI interface loaded.");
187
    return JNI_VERSION_1_2;
188
189
}

190
191
192
193
void JNI_OnUnload(JavaVM* vm, void* reserved) {
    pthread_mutex_destroy(&vout_android_lock);
}

Sébastien Toque's avatar
Sébastien Toque committed
194
void Java_org_videolan_vlc_LibVLC_attachSurface(JNIEnv *env, jobject thiz, jobject surf, jobject gui, jint width, jint height) {
195
196
197
198
199
200
201
    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) {
202
        jthrowable exp = (*env)->ExceptionOccurred(env);
203
204
205
206
207
208
209
210
        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);
211
212

    vout_android_gui = (*env)->NewGlobalRef(env, gui);
213
214
215
    pthread_mutex_unlock(&vout_android_lock);
}

Sébastien Toque's avatar
Sébastien Toque committed
216
void Java_org_videolan_vlc_LibVLC_detachSurface(JNIEnv *env, jobject thiz) {
217
218
    pthread_mutex_lock(&vout_android_lock);
    vout_android_surf = NULL;
219
220
221
    if (vout_android_gui != NULL)
        (*env)->DeleteGlobalRef(env, vout_android_gui);
    vout_android_gui = NULL;
222
223
    pthread_mutex_unlock(&vout_android_lock);
}
224

Sébastien Toque's avatar
Sébastien Toque committed
225
void Java_org_videolan_vlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
226
{
227
228
229
230
231
    //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;

232
    /* Don't add any invalid options, otherwise it causes LibVLC to crash */
233
    const char *argv[] = {
234
235
        "-I", "dummy",
        "-vv",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
236
237
238
        "--no-osd",
        "--no-video-title-show",
        "--no-stats",
239
240
        "--no-plugins-cache",
        "--no-drop-late-frames",
241
242
        "--control", "logger",
        "--logmode", "android",
243
        "--ffmpeg-fast",
244
        use_opensles ? "--aout=opensles" : "--aout=android_audiotrack"
245
    };
246
    libvlc_instance_t *instance = libvlc_new(sizeof(argv) / sizeof(*argv), argv);
247
248

    jclass clazz = (*env)->GetObjectClass(env, thiz);
249
    jfieldID field = (*env)->GetFieldID(env, clazz, "mLibVlcInstance", "I");
250
    (*env)->SetIntField(env, thiz, field, (jint) instance);
251

252
253
    if (!instance)
    {
Sébastien Toque's avatar
Sébastien Toque committed
254
        jclass exc = (*env)->FindClass(env, "org/videolan/vlc/LibVlcException");
255
256
257
258
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
259
260
}

261
262
263
264
265
266
267
268
269
270
271
272
273
274
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 */
    char* psz_location = make_URI(psz_path, "file");
    /* Box into jstring */
    jstring t = (*env)->NewStringUTF(env, psz_location);
    /* Clean up */
    (*env)->ReleaseStringUTFChars(env, path, psz_path);
    free(psz_location);
    return t;
}
275

Sébastien Toque's avatar
Sébastien Toque committed
276
void Java_org_videolan_vlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
277
{
278
    releaseMediaPlayer(env, thiz);
279
280
281
282
283
284
285
286
287
288
    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);

    (*env)->SetIntField(env, thiz, field, 0);
289
290
}

291
292
int currentSdk( JNIEnv *p_env, jobject thiz )
{
Sébastien Toque's avatar
Sébastien Toque committed
293
    jclass  cls = (*p_env)->FindClass( p_env, "org/videolan/vlc/Util" );
294
295
    if ( !cls )
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
296
        LOGE("Failed to load util class (org/videolan/vlc/Util)" );
297
298
299
300
301
302
303
304
305
306
        return 0;
    }
    jmethodID methodSdkVersion = (*p_env)->GetStaticMethodID( p_env, cls,
                                                       "getApiLevel", "()I" );
    if ( !methodSdkVersion )
    {
        LOGE("Failed to load method getApiLevel()" );
        return 0;
    }
    int version = (*p_env)->CallStaticIntMethod( p_env, cls, methodSdkVersion );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
307
    LOGI("Phone running on API level %d\n", version );
308
309
    return version;
}
310

Sébastien Toque's avatar
Sébastien Toque committed
311
void Java_org_videolan_vlc_LibVLC_detachEventManager(JNIEnv *env, jobject thiz)
312
313
314
315
316
317
318
{
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }
}

Sébastien Toque's avatar
Sébastien Toque committed
319
void Java_org_videolan_vlc_LibVLC_setEventManager(JNIEnv *env, jobject thiz, jobject eventManager)
320
{
321
322
323
324
325
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }

326
327
328
329
330
331
332
333
334
335
336
    jclass cls = (*env)->GetObjectClass(env, eventManager);
    if (!cls) {
        LOGE("setEventManager: failed to get class reference");
        return;
    }

    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(I)V");
    if (!methodID) {
        LOGE("setEventManager: failed to get the callback method");
        return;
    }
337

338
339
340
    eventManagerInstance = (*env)->NewGlobalRef(env, eventManager);
}

341
342
343
344
345
346
347
348
349
350
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
351
352
    if (fieldId == NULL)
        return;
353
354
355
356

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

357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
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);
}

373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
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);
}

389
390
391
392
393
394
395
396
397
398
399
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
400
401
    if (fieldId == NULL)
        return;
402
403
404
405
406
407
408
409

    /* 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
410
411
jobjectArray Java_org_videolan_vlc_LibVLC_readMediaMeta(JNIEnv *env,
                                                        jobject thiz, jint instance, jstring mrl)
Rafaël Carré's avatar
Rafaël Carré committed
412
413
414
415
416
{
    jobjectArray array = (*env)->NewObjectArray(env, 8,
            (*env)->FindClass(env, "java/lang/String"),
            (*env)->NewStringUTF(env, ""));

417
    libvlc_media_t *m = new_media(instance, env, thiz, mrl, false);
418
419
    if (!m)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
420
        LOGE("readMediaMeta: Could not create the media!");
421
        return array;
422
423
424
    }

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

426
    static const char str[][7] = {
Rafaël Carré's avatar
Rafaël Carré committed
427
428
        "artist", "album", "title", "genre",
    };
429
    static const libvlc_meta_t meta_id[] = {
Rafaël Carré's avatar
Rafaël Carré committed
430
431
432
433
434
        libvlc_meta_Artist,
        libvlc_meta_Album,
        libvlc_meta_Title,
        libvlc_meta_Genre,
    };
435
    for (int i=0; i < sizeof(str) / sizeof(*str); i++) {
Rafaël Carré's avatar
Rafaël Carré committed
436
437
        char *meta = libvlc_media_get_meta(m, meta_id[i]);
        if (!meta)
438
            meta = strdup("");
Rafaël Carré's avatar
Rafaël Carré committed
439
440
441
442
443
444
445
446
447
448
449
450
451

        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
452
453
void Java_org_videolan_vlc_LibVLC_readMedia(JNIEnv *env, jobject thiz,
                                            jint instance, jstring mrl)
454
{
455
456
457
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

458
    /* Create a new item */
459
    libvlc_media_t *m = new_media(instance, env, thiz, mrl, false);
460
461
    if (!m)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
462
        LOGE("readMedia: Could not create the media!");
463
464
        return;
    }
465

466
    /* Create a media player playing environment */
467
468
    libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)instance);

469
470
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz);

471
    libvlc_media_player_set_media(mp, m);
ivoire's avatar
ivoire committed
472

473
474
475
476
    //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 )
477
    {
Sébastien Toque's avatar
Sébastien Toque committed
478
479
480
        libvlc_audio_set_callbacks(mp, aout_play, NULL, NULL, NULL, NULL,
                                   (void*) myJavaLibVLC);
        libvlc_audio_set_format_callbacks(mp, aout_open, aout_close);
481
482
    }

483
484
485
    /* No need to keep the media now */
    libvlc_media_release(m);

486
487
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
488
489
490
491
492
493
    static const libvlc_event_type_t mp_events[] = {
        libvlc_MediaPlayerPlaying,
        libvlc_MediaPlayerPaused,
        libvlc_MediaPlayerEndReached,
        libvlc_MediaPlayerStopped,
    };
494
    int i;
495
496
497
    for (i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); ++i)
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

498
499
500
501
502
503
    /* 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);

504
505
506
    libvlc_media_player_play(mp);
}

Sébastien Toque's avatar
Sébastien Toque committed
507
jboolean Java_org_videolan_vlc_LibVLC_hasVideoTrack(JNIEnv *env, jobject thiz,
508
                                                    jint i_instance, jstring fileLocation)
509
{
510
    /* Create a new item and assign it to the media player. */
511
    libvlc_media_t *p_m = new_media(i_instance, env, thiz, fileLocation, false);
512
513
    if (p_m == NULL)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
514
        LOGE("Could not create the media!");
Rafaël Carré's avatar
Rafaël Carré committed
515
        return JNI_FALSE;
516
517
518
519
520
    }

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

Rafaël Carré's avatar
Rafaël Carré committed
522
523
    jboolean hasVideo = JNI_FALSE;
    int i_nbTracks = libvlc_media_get_tracks_info(p_m, &p_tracks);
524
525
526
    unsigned i;
    for (i = 0; i < i_nbTracks; ++i)
        if (p_tracks[i].i_type == libvlc_track_video)
Rafaël Carré's avatar
Rafaël Carré committed
527
528
529
530
531
        {
            hasVideo = JNI_TRUE;
            break;
        }

532
    libvlc_media_tracks_info_release(p_tracks, i_nbTracks);
Rafaël Carré's avatar
Rafaël Carré committed
533
534
535
    libvlc_media_release(p_m);

    return hasVideo;
536
537
}

538
539
540
541
542
543
544
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
545
        LOGE("Failed to load class (org/videolan/vlc/TrackInfo)" );
546
547
548
549
550
551
552
553
554
555
556
557
        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. */
558
    libvlc_media_t *p_m = new_media(instance, env, thiz, mrl, false);
559
560
    if (p_m == NULL)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
561
        LOGE("Could not create the media!");
562
563
564
565
566
567
568
569
        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);
570
    jobjectArray array = (*env)->NewObjectArray(env, i_nbTracks + 1, cls, NULL);
571
572
573
574

    unsigned i;
    if (array != NULL)
    {
575
        for (i = 0; i <= i_nbTracks; ++i)
576
577
578
579
580
        {
            jobject item = (*env)->NewObject(env, cls, clsCtor);
            if (item == NULL)
                continue;
            (*env)->SetObjectArrayElement(env, array, i, item);
581
582
583
584
585
586
587
588
589
590
591
592
593

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

594
595
596
            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));
597
            setString(env, item, "Language", p_tracks[i].psz_language);
598
599
600
601
602

            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);
603
                SetFloat(env, item, "Framerate", p_tracks[i].u.video.f_frame_rate);
604
605
606
607
608
609
610
611
612
            }
            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);
            }
        }
    }

613
    libvlc_media_tracks_info_release(p_tracks, i_nbTracks);
614
615
616
617
    libvlc_media_release(p_m);
    return array;
}

618
619
620
621
622
struct length_change_monitor {
    pthread_mutex_t doneMutex;
    pthread_cond_t doneCondVar;
    bool length_changed;
};
623
624

static void length_changed_callback(const libvlc_event_t *ev, void *data)
625
{
626
627
628
629
630
    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);
631
632
}

633
634
jlong Java_org_videolan_vlc_LibVLC_getLengthFromLocation(JNIEnv *env, jobject thiz,
                                                     jint i_instance, jstring fileLocation)
635
{
636
637
638
639
640
    jlong length = 0;
    struct length_change_monitor *monitor;
    monitor = malloc(sizeof(*monitor));
    if (!monitor)
        return 0;
641
642

    /* Initialize pthread variables. */
643
644
645
    pthread_mutex_init(&monitor->doneMutex, NULL);
    pthread_cond_init(&monitor->doneCondVar, NULL);
    monitor->length_changed = false;
646

647
    /* Create a new item and assign it to the media player. */
648
    libvlc_media_t *m = new_media(i_instance, env, thiz, fileLocation, false);
649
    if (m == NULL)
650
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
651
        LOGE("Could not create the media to play!");
652
        goto end;
653
654
    }

655
    /* Create a media player playing environment */
656
    libvlc_media_player_t *mp = libvlc_media_player_new_from_media (m);
657
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
658
    libvlc_event_attach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
659
660
    libvlc_media_release (m);
    libvlc_media_player_play( mp );
661
662
663
664
665
666
667

    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 );
668
    libvlc_media_player_stop( mp );
669
    libvlc_media_player_release( mp );
670

671
672
673
674
end:
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);
675
676

    return length;
677
678
}

Sébastien Toque's avatar
Sébastien Toque committed
679
jboolean Java_org_videolan_vlc_LibVLC_hasMediaPlayer(JNIEnv *env, jobject thiz)
680
{
681
    return !!getMediaPlayer(env, thiz);
682
683
}

Sébastien Toque's avatar
Sébastien Toque committed
684
jboolean Java_org_videolan_vlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
685
{
686
687
688
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_playing(mp);
689
    return 0;
690
691
}

Sébastien Toque's avatar
Sébastien Toque committed
692
jboolean Java_org_videolan_vlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
693
{
694
695
696
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_seekable(mp);
697
    return 0;
698
699
}

Sébastien Toque's avatar
Sébastien Toque committed
700
void Java_org_videolan_vlc_LibVLC_play(JNIEnv *env, jobject thiz)
701
{
702
703
704
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_play(mp);
705
706
}

Sébastien Toque's avatar
Sébastien Toque committed
707
void Java_org_videolan_vlc_LibVLC_pause(JNIEnv *env, jobject thiz)
708
{
709
710
711
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_pause(mp);
712
713
}

Sébastien Toque's avatar
Sébastien Toque committed
714
void Java_org_videolan_vlc_LibVLC_stop(JNIEnv *env, jobject thiz)
715
{
716
717
718
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_stop(mp);
719
720
}

Sébastien Toque's avatar
Sébastien Toque committed
721
jint Java_org_videolan_vlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
722
{
723
724
725
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
726
727
728
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
729
jint Java_org_videolan_vlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
730
{
731
732
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
733
        //Returns 0 if the volume was set, -1 if it was out of range or error
734
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
735
736
737
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
738
jlong Java_org_videolan_vlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
739
{
740
741
742
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
743
744
745
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
746
void Java_org_videolan_vlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
747
{
748
749
750
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
751
752
}

Sébastien Toque's avatar
Sébastien Toque committed
753
jfloat Java_org_videolan_vlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
754
{
755
756
757
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
758
759
760
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
761
void Java_org_videolan_vlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
762
{
763
764
765
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
766
767
}

Sébastien Toque's avatar
Sébastien Toque committed
768
jlong Java_org_videolan_vlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
769
{
770
771
772
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
773
774
775
    return -1;
}

Sébastien Toque's avatar
Sébastien Toque committed
776
jstring Java_org_videolan_vlc_LibVLC_version(JNIEnv* env, jobject thiz)
777
{
778
779
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
780

Sébastien Toque's avatar
Sébastien Toque committed
781
jstring Java_org_videolan_vlc_LibVLC_compiler(JNIEnv* env, jobject thiz)
782
783
784
785
{
    return (*env)->NewStringUTF(env, libvlc_get_compiler());
}

Sébastien Toque's avatar
Sébastien Toque committed
786
jstring Java_org_videolan_vlc_LibVLC_changeset(JNIEnv* env, jobject thiz)
787
788
789
{
    return (*env)->NewStringUTF(env, libvlc_get_changeset());
}
790

Sébastien Toque's avatar
Sébastien Toque committed
791
jint Java_org_videolan_vlc_LibVLC_getAudioTracksCount(JNIEnv *env, jobject thiz)
792
793
794
795
796
797
798
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_track_count(mp);
    return -1;
}

799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
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
841
jint Java_org_videolan_vlc_LibVLC_getVideoTracksCount(JNIEnv *env, jobject thiz)
842
843
844
845
846
847
848
{
    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
849
jint Java_org_videolan_vlc_LibVLC_getSpuTracksCount(JNIEnv *env, jobject thiz)
850
851
852
853
854
855
856
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_video_get_spu_count(mp);
    return -1;
}