libvlcjni.c 18.4 KB
Newer Older
1
#include <stdio.h>
2
#include <string.h>
3
#include <assert.h>
4

5
6
7
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
8

9
#include <pthread.h>
10
#include <vlc/vlc.h>
11

12
#include <jni.h>
13
#include <android/api-level.h>
14

15
#include "libvlcjni.h"
ivoire's avatar
ivoire committed
16
#include "aout.h"
17
18

#define LOG_TAG "VLC/JNI/main"
19
20
#include "log.h"

21
22
23
24
25
#define NAME1(CLZ, FUN) Java_##CLZ##_##FUN
#define NAME2(CLZ, FUN) NAME1(CLZ, FUN)

#define NAME(FUN) NAME2(CLASS, FUN)

26
libvlc_media_player_t *getMediaPlayer(JNIEnv *env, jobject thiz)
27
28
29
30
{
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
                                          "mMediaPlayerInstance", "I");
31
    return (libvlc_media_player_t*)(*env)->GetIntField(env, thiz, fieldMP);
32
33
}

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
jboolean releaseMediaPlayer(JNIEnv *env, jobject thiz)
{
    jclass clazz = (*env)->GetObjectClass(env, thiz);
    jfieldID fieldMP = (*env)->GetFieldID(env, clazz,
                                          "mMediaPlayerInstance", "I");
    jint mediaPlayer = (*env)->GetIntField(env, thiz, fieldMP);
    if (mediaPlayer != 0)
    {
        libvlc_media_player_t *mp = (libvlc_media_player_t*) mediaPlayer;
        libvlc_media_player_stop(mp);
        libvlc_media_player_release(mp);
        (*env)->SetIntField(env, thiz, fieldMP, 0);
    }
    return (mediaPlayer == 0);
}

50
51
52
53
54
/* 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;
55

56
static jobject eventManagerInstance = NULL;
57

58
59
static pthread_mutex_t vout_android_lock;
static void *vout_android_surf = NULL;
60
static void *vout_android_gui = NULL;
61

62
void *jni_LockAndGetAndroidSurface() {
63
    pthread_mutex_lock(&vout_android_lock);
Ludovic Fauvet's avatar
Ludovic Fauvet committed
64
    return vout_android_surf;
65
66
}

67
void jni_UnlockAndroidSurface() {
68
69
70
    pthread_mutex_unlock(&vout_android_lock);
}

71
void jni_SetAndroidSurfaceSize(int width, int height)
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
{
    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);
}

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
static const libvlc_event_type_t mp_events[] = {
    libvlc_MediaPlayerPlaying,
    libvlc_MediaPlayerPaused,
    libvlc_MediaPlayerEndReached,
    libvlc_MediaPlayerStopped,
};

static void vlc_event_callback(const libvlc_event_t *ev, void *data)
{
    int status;
    JNIEnv *env;
    JavaVM *myVm = (JavaVM*)data;
    jint etype = ev->type;

    int isAttached = 0;

104
    if (eventManagerInstance == NULL)
Sébastien Toque's avatar
Sébastien Toque committed
105
        return;
106

107
108
    status = (*myVm)->GetEnv(myVm, (void**) &env, JNI_VERSION_1_2);
    if (status < 0) {
109
        LOGD("vlc_event_callback: failed to get JNI environment, "
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
             "assuming native thread");
        status = (*myVm)->AttachCurrentThread(myVm, &env, NULL);
        if (status < 0)
            return;
        isAttached = 1;
    }

    /* Get the object class */
    jclass cls = (*env)->GetObjectClass(env, eventManagerInstance);
    if (!cls) {
        LOGE("EventManager: failed to get class reference");
        if (isAttached) (*myVm)->DetachCurrentThread(myVm);
        return;
    }

    /* Find the callback ID */
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(I)V");
    if (!methodID) {
        LOGE("EventManager: failed to get the callback method");
        if (isAttached) (*myVm)->DetachCurrentThread(myVm);
        return;
    }

    (*env)->CallVoidMethod(env, eventManagerInstance, methodID, etype);
    if (isAttached) (*myVm)->DetachCurrentThread(myVm);
}

137
jint JNI_OnLoad(JavaVM *vm, void *reserved)
138
{
139
140
141
    // Keep a reference on the Java VM.
    myVm = vm;

142
143
    pthread_mutex_init(&vout_android_lock, NULL);

144
    LOGD("JNI interface loaded.");
145
    return JNI_VERSION_1_2;
146
147
}

148
149
150
151
void JNI_OnUnload(JavaVM* vm, void* reserved) {
    pthread_mutex_destroy(&vout_android_lock);
}

152
void Java_org_videolan_vlc_android_LibVLC_attachSurface(JNIEnv *env, jobject thiz, jobject surf, jobject gui, jint width, jint height) {
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
    jclass clz;
    jfieldID fid;
    jthrowable exp;

    pthread_mutex_lock(&vout_android_lock);
    //vout_android_ref = (*env)->NewGlobalRef(env, surf);
    clz = (*env)->GetObjectClass(env, surf);
    fid = (*env)->GetFieldID(env, clz, "mSurface", "I");
    if (fid == NULL) {
        exp = (*env)->ExceptionOccurred(env);
        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);
171
172

    vout_android_gui = (*env)->NewGlobalRef(env, gui);
173
174
175
    pthread_mutex_unlock(&vout_android_lock);
}

176
void Java_org_videolan_vlc_android_LibVLC_detachSurface(JNIEnv *env, jobject thiz) {
177
178
179
    pthread_mutex_lock(&vout_android_lock);
    //(*env)->DeleteGlobalRef(env, vout_android_ref);
    vout_android_surf = NULL;
180
181
182
    if (vout_android_gui != NULL)
        (*env)->DeleteGlobalRef(env, vout_android_gui);
    vout_android_gui = NULL;
183
184
    pthread_mutex_unlock(&vout_android_lock);
}
185

186
void Java_org_videolan_vlc_android_LibVLC_nativeInit(JNIEnv *env, jobject thiz, jboolean enable_iomx)
187
{
188
189
190
191
192
193
194
195
196
    /*
     * 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.
     */
197
198
199
200
201
202
203
204
205
206
    const char *argv[] = {
        "-I", "dummy",
        "-vv",
        "--no-plugins-cache",
        "--no-drop-late-frames",
        /* Leave those 2 options and 2 values at the end of the array.
         * They are modified by the code below for iomx. */
        "--file-caching", "1500",
        "--network-caching", "1500",
    };
207
    size_t argc = sizeof(argv) / sizeof(*argv);
208
209
210
211
212
    if (!enable_iomx) {
        argc -= 4; // Drop the iomx specific caching values
        argv[argc++] = "--codec";
        argv[argc++] = "avcodec,all";
    }
213

214
    libvlc_instance_t *instance = libvlc_new(argc, argv);
215
216

    jclass clazz = (*env)->GetObjectClass(env, thiz);
217
    jfieldID field = (*env)->GetFieldID(env, clazz, "mLibVlcInstance", "I");
218
    (*env)->SetIntField(env, thiz, field, (jint) instance);
219

220
221
222
223
224
225
226
227
    if (!instance)
    {
        jclass exc = (*env)->FindClass(env, "vlc/android/LibVlcException");
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
    return;
228
229
}

230

231
void Java_org_videolan_vlc_android_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
232
{
233
    releaseMediaPlayer(env, thiz);
234
235
236
237
238
239
240
241
242
243
    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);
244
245
}

246
247
int currentSdk( JNIEnv *p_env, jobject thiz )
{
248
    jclass  cls = (*p_env)->FindClass( p_env, "org/videolan/vlc/android/Util" );
249
250
    if ( !cls )
    {
251
        LOGE( "Failed to load util class (org/videolan/vlc/android/Util)" );
252
253
254
255
256
257
258
259
260
261
        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 );
262
    LOGI("Got version: %d\n", version );
263
264
    return version;
}
265

266
267
268
269
270
271
272
273
void Java_org_videolan_vlc_android_LibVLC_detachEventManager(JNIEnv *env, jobject thiz)
{
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }
}

274
275
void Java_org_videolan_vlc_android_LibVLC_setEventManager(JNIEnv *env, jobject thiz, jobject eventManager)
{
276
277
278
279
280
    if (eventManagerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventManagerInstance);
        eventManagerInstance = NULL;
    }

281
282
283
284
285
286
287
288
289
290
291
    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;
    }
292

293
294
295
    eventManagerInstance = (*env)->NewGlobalRef(env, eventManager);
}

Rafaël Carré's avatar
Rafaël Carré committed
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
jobjectArray Java_org_videolan_vlc_android_LibVLC_readMediaMeta(JNIEnv *env,
                                    jobject thiz, jint instance, jstring mrl)
{
    jobjectArray array = (*env)->NewObjectArray(env, 8,
            (*env)->FindClass(env, "java/lang/String"),
            (*env)->NewStringUTF(env, ""));


    static const char str[4][7] = {
        "artist", "album", "title", "genre",
    };
    static const libvlc_meta_t meta_id[4] = {
        libvlc_meta_Artist,
        libvlc_meta_Album,
        libvlc_meta_Title,
        libvlc_meta_Genre,
    };

Rafaël Carré's avatar
Rafaël Carré committed
314
    jboolean isCopy;
Rafaël Carré's avatar
Rafaël Carré committed
315
316
317
    const char *psz_mrl = (*env)->GetStringUTFChars(env, mrl, &isCopy);
    libvlc_media_t *m = libvlc_media_new_path((libvlc_instance_t*)instance,
                                              psz_mrl);
Rafaël Carré's avatar
Rafaël Carré committed
318
    (*env)->ReleaseStringUTFChars(env, mrl, psz_mrl);
Rafaël Carré's avatar
Rafaël Carré committed
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    libvlc_media_parse(m);

    int i;
    for (i=0; i < 4 ; i++) {
        char *meta = libvlc_media_get_meta(m, meta_id[i]);
        if (!meta)
            meta = strdup("unknown");

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

339
void Java_org_videolan_vlc_android_LibVLC_readMedia(JNIEnv *env, jobject thiz,
340
                                       jint instance, jstring mrl)
341
{
342
343
344
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

345
    /* Create a new item */
346
347
    jboolean isCopy;
    const char *psz_mrl = (*env)->GetStringUTFChars(env, mrl, &isCopy);
348
349
    libvlc_media_t *m = libvlc_media_new_path((libvlc_instance_t*)instance,
                                              psz_mrl);
350
    (*env)->ReleaseStringUTFChars(env, mrl, psz_mrl);
351

352
    /* Create a media player playing environment */
353
354
    libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)instance);

355
356
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz);

357
    libvlc_media_player_set_media(mp, m);
ivoire's avatar
ivoire committed
358

359
    if ( currentSdk( env, thiz )  < 9 ) //On newer version, we can use SLES
360
    {
Sébastien Toque's avatar
Sébastien Toque committed
361
362
363
        libvlc_audio_set_callbacks(mp, aout_play, NULL, NULL, NULL, NULL,
                                   (void*) myJavaLibVLC);
        libvlc_audio_set_format_callbacks(mp, aout_open, aout_close);
364
365
    }

366
367
368
    /* No need to keep the media now */
    libvlc_media_release(m);

369
370
371
372
373
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);

    /* Subscribe to the events */

374
    int i;
375
376
377
    for (i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); ++i)
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

378
379
380
381
382
383
    /* 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);

384
    /* Play the media. */
385
386
    libvlc_media_player_play(mp);

387
    //libvlc_media_player_release(mp);
388
389
}

390
391
392
393
jboolean Java_org_videolan_vlc_android_LibVLC_hasVideoTrack(JNIEnv *env, jobject thiz,
                                                            jint i_instance, jstring filePath)
{
    libvlc_instance_t *p_instance = (libvlc_instance_t *)i_instance;
394
395
396
397
    const char *psz_filePath = (*env)->GetStringUTFChars(env, filePath, 0);

    /* Create a new item and assign it to the media player. */
    libvlc_media_t *p_m = libvlc_media_new_path(p_instance, psz_filePath);
Rafaël Carré's avatar
Rafaël Carré committed
398
399
    (*env)->ReleaseStringUTFChars(env, filePath, psz_filePath);

400
401
402
    if (p_m == NULL)
    {
        LOGE("Couldn't create the media!");
Rafaël Carré's avatar
Rafaël Carré committed
403
        return JNI_FALSE;
404
405
406
407
408
    }

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

Rafaël Carré's avatar
Rafaël Carré committed
410
411
    jboolean hasVideo = JNI_FALSE;
    int i_nbTracks = libvlc_media_get_tracks_info(p_m, &p_tracks);
412
413
414
    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
415
416
417
418
419
420
421
422
423
        {
            hasVideo = JNI_TRUE;
            break;
        }

    free(p_tracks);
    libvlc_media_release(p_m);

    return hasVideo;
424
425
}

426
427
428
429
430
struct length_change_monitor {
    pthread_mutex_t doneMutex;
    pthread_cond_t doneCondVar;
    bool length_changed;
};
431
432

static void length_changed_callback(const libvlc_event_t *ev, void *data)
433
{
434
435
436
437
438
    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);
439
440
}

441
442
443
jlong Java_org_videolan_vlc_android_LibVLC_getLengthFromFile(JNIEnv *env, jobject thiz,
                                                        jint i_instance, jstring filePath)
{
444
445
446
447
448
    jlong length = 0;
    struct length_change_monitor *monitor;
    monitor = malloc(sizeof(*monitor));
    if (!monitor)
        return 0;
449
450

    /* Initialize pthread variables. */
451
452
453
    pthread_mutex_init(&monitor->doneMutex, NULL);
    pthread_cond_init(&monitor->doneCondVar, NULL);
    monitor->length_changed = false;
454

455
    libvlc_instance_t *p_instance = (libvlc_instance_t *)i_instance;
456
457
458
    const char *psz_filePath = (*env)->GetStringUTFChars(env, filePath, 0);

    /* Create a new item and assign it to the media player. */
459
    libvlc_media_t *m = libvlc_media_new_path(p_instance, psz_filePath);
460
    if (m == NULL)
461
    {
462
        LOGE("Couldn't create the media to play!");
463
        goto end;
464
465
    }

466
    /* Create a media player playing environment */
467
    libvlc_media_player_t *mp = libvlc_media_player_new_from_media (m);
468
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
469
    libvlc_event_attach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
470
471
    libvlc_media_release (m);
    libvlc_media_player_play( mp );
472
473
474
475
476
477
478

    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 );
479
    libvlc_media_player_stop( mp );
480
    libvlc_media_player_release( mp );
481

482
483
484
485
end:
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);
486
487

    return length;
488
489
}

490
jboolean Java_org_videolan_vlc_android_LibVLC_hasMediaPlayer(JNIEnv *env, jobject thiz)
491
{
492
    return !!getMediaPlayer(env, thiz);
493
494
}

495
jboolean Java_org_videolan_vlc_android_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
496
{
497
498
499
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_playing(mp);
500
    return 0;
501
502
}

503
jboolean Java_org_videolan_vlc_android_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
504
{
505
506
507
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_seekable(mp);
508
    return 0;
509
510
}

511
void Java_org_videolan_vlc_android_LibVLC_play(JNIEnv *env, jobject thiz)
512
{
513
514
515
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_play(mp);
516
517
}

518
void Java_org_videolan_vlc_android_LibVLC_pause(JNIEnv *env, jobject thiz)
519
{
520
521
522
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_pause(mp);
523
524
}

525
void Java_org_videolan_vlc_android_LibVLC_stop(JNIEnv *env, jobject thiz)
526
{
527
528
529
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_stop(mp);
530
531
}

532
jint Java_org_videolan_vlc_android_LibVLC_getVolume(JNIEnv *env, jobject thiz)
533
{
534
535
536
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
537
538
539
    return -1;
}

540
jint Java_org_videolan_vlc_android_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
541
{
542
543
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
544
        //Returns 0 if the volume was set, -1 if it was out of range or error
545
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
546
547
548
    return -1;
}

549
jlong Java_org_videolan_vlc_android_LibVLC_getTime(JNIEnv *env, jobject thiz)
550
{
551
552
553
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
554
555
556
    return -1;
}

557
void Java_org_videolan_vlc_android_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
558
{
559
560
561
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
562
563
}

564
jfloat Java_org_videolan_vlc_android_LibVLC_getPosition(JNIEnv *env, jobject thiz)
565
{
566
567
568
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
569
570
571
    return -1;
}

572
void Java_org_videolan_vlc_android_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
573
{
574
575
576
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
577
578
}

579
jlong Java_org_videolan_vlc_android_LibVLC_getLength(JNIEnv *env, jobject thiz)
580
{
581
582
583
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
584
585
586
    return -1;
}

587
jstring Java_org_videolan_vlc_android_LibVLC_version(JNIEnv* env, jobject thiz)
588
{
589
590
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
591

592
jstring Java_org_videolan_vlc_android_LibVLC_compiler(JNIEnv* env, jobject thiz)
593
594
595
596
{
    return (*env)->NewStringUTF(env, libvlc_get_compiler());
}

597
jstring Java_org_videolan_vlc_android_LibVLC_changeset(JNIEnv* env, jobject thiz)
598
599
600
{
    return (*env)->NewStringUTF(env, libvlc_get_changeset());
}