libvlcjni.c 44.5 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
#define AOUT_AUDIOTRACK_JAVA 0
#define AOUT_AUDIOTRACK      1
47
48
#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
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
142
    libvlc_instance_t *libvlc = (libvlc_instance_t*)(intptr_t)instance;
143
    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
    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");
165
            libvlc_media_add_option(p_md, ":codec=mediacodec,iomx,all");
166
        }
167
168
        if (noVideo)
            libvlc_media_add_option(p_md, ":no-video");
169
    }
170
    return p_md;
171
172
}

173
174
static libvlc_media_list_t *getMediaList(JNIEnv *env, jobject thiz)
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
175
    return (libvlc_media_list_t*)(intptr_t)getLong(env, thiz, "mMediaListInstance");
176
177
}

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

static libvlc_media_list_player_t *getMediaListPlayer(JNIEnv *env, jobject thiz)
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
185
    return (libvlc_media_list_player_t*)(intptr_t)getLong(env, thiz, "mMediaListPlayerInstance");
186
187
}

Rafaël Carré's avatar
Rafaël Carré committed
188
189
static void releaseMediaPlayer(JNIEnv *env, jobject thiz)
{
190
191
    libvlc_media_list_player_t* p_mlp = getMediaListPlayer(env, thiz);
    if (p_mlp)
192
    {
193
194
195
196
197
        libvlc_media_list_player_stop(p_mlp);
        libvlc_media_list_player_release(p_mlp);
        /* libvlc_media_list_player_release frees the media player, so
         * we don't free it ourselves. */
        setLong(env, thiz, "mInternalMediaPlayerInstance", 0);
198
        setLong(env, thiz, "mMediaListPlayerInstance", 0);
199
200
201
    }
}

202
203
204
205
206
/* 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;
207

208
static jobject eventHandlerInstance = NULL;
209
static jobject debugBufferInstance = NULL;
210

211
212
static pthread_mutex_t vout_android_lock;
static void *vout_android_surf = NULL;
213
static void *vout_android_gui = NULL;
214

215
void *jni_LockAndGetAndroidSurface() {
216
    pthread_mutex_lock(&vout_android_lock);
Ludovic Fauvet's avatar
Ludovic Fauvet committed
217
    return vout_android_surf;
218
219
}

220
void jni_UnlockAndroidSurface() {
221
222
223
    pthread_mutex_unlock(&vout_android_lock);
}

224
void jni_SetAndroidSurfaceSize(int width, int height, int sar_num, int sar_den)
225
226
227
228
229
230
231
232
{
    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);
233
    jmethodID methodId = (*p_env)->GetMethodID (p_env, cls, "setSurfaceSize", "(IIII)V");
234

235
    (*p_env)->CallVoidMethod (p_env, vout_android_gui, methodId, width, height, sar_num, sar_den);
236
237
238
239
240

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

241
242
243
244
static void vlc_event_callback(const libvlc_event_t *ev, void *data)
{
    JNIEnv *env;

245
    bool isAttached = false;
246

247
    if (eventHandlerInstance == NULL)
Sébastien Toque's avatar
Sébastien Toque committed
248
        return;
249

Rafaël Carré's avatar
Rafaël Carré committed
250
251
    if ((*myVm)->GetEnv(myVm, (void**) &env, JNI_VERSION_1_2) < 0) {
        if ((*myVm)->AttachCurrentThread(myVm, &env, NULL) < 0)
252
            return;
253
        isAttached = true;
254
255
    }

256
257
258
259
260
261
262
263
264
265
    /* Creating the bundle in C allows us to subscribe to more events
     * and get better flexibility for each event. For example, we can
     * have totally different types of data for each event, instead of,
     * for example, only an integer and/or string.
     */
    jclass clsBundle = (*env)->FindClass(env, "android/os/Bundle");
    jmethodID clsCtor = (*env)->GetMethodID(env, clsBundle, "<init>", "()V" );
    jobject bundle = (*env)->NewObject(env, clsBundle, clsCtor);

    jmethodID putInt = (*env)->GetMethodID(env, clsBundle, "putInt", "(Ljava/lang/String;I)V" );
266
    jmethodID putFloat = (*env)->GetMethodID(env, clsBundle, "putFloat", "(Ljava/lang/String;F)V" );
267
268
    jmethodID putString = (*env)->GetMethodID(env, clsBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V" );

269
270
271
272
273
    if (ev->type == libvlc_MediaPlayerPositionChanged) {
            jstring sData = (*env)->NewStringUTF(env, "data");
            (*env)->CallVoidMethod(env, bundle, putFloat, sData, ev->u.media_player_position_changed.new_position);
            (*env)->DeleteLocalRef(env, sData);
    } else if(ev->type == libvlc_MediaPlayerVout) {
274
275
276
277
        /* For determining the vout/ES track change */
        jstring sData = (*env)->NewStringUTF(env, "data");
        (*env)->CallVoidMethod(env, bundle, putInt, sData, ev->u.media_player_vout.new_count);
        (*env)->DeleteLocalRef(env, sData);
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
    } else if(ev->type == libvlc_MediaListItemAdded ||
              ev->type == libvlc_MediaListItemDeleted ) {
        jstring item_uri = (*env)->NewStringUTF(env, "item_uri");
        jstring item_index = (*env)->NewStringUTF(env, "item_index");
        char* mrl = libvlc_media_get_mrl(
            ev->type == libvlc_MediaListItemAdded ?
            ev->u.media_list_item_added.item :
            ev->u.media_list_item_deleted.item
            );
        jstring item_uri_value = (*env)->NewStringUTF(env, mrl);
        jint item_index_value;
        if(ev->type == libvlc_MediaListItemAdded)
            item_index_value = ev->u.media_list_item_added.index;
        else
            item_index_value = ev->u.media_list_item_deleted.index;

        (*env)->CallVoidMethod(env, bundle, putString, item_uri, item_uri_value);
        (*env)->CallVoidMethod(env, bundle, putInt, item_index, item_index_value);

        (*env)->DeleteLocalRef(env, item_uri);
        (*env)->DeleteLocalRef(env, item_uri_value);
        (*env)->DeleteLocalRef(env, item_index);
        free(mrl);
301
302
    }

303
    /* Get the object class */
304
    jclass cls = (*env)->GetObjectClass(env, eventHandlerInstance);
305
    if (!cls) {
306
        LOGE("EventHandler: failed to get class reference");
307
        goto end;
308
309
310
    }

    /* Find the callback ID */
311
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(ILandroid/os/Bundle;)V");
312
    if (methodID) {
313
        (*env)->CallVoidMethod(env, eventHandlerInstance, methodID, ev->type, bundle);
314
    } else {
315
        LOGE("EventHandler: failed to get the callback method");
316
317
    }

318
319
320
end:
    if (isAttached)
        (*myVm)->DetachCurrentThread(myVm);
321
322
}

323
jint JNI_OnLoad(JavaVM *vm, void *reserved)
324
{
325
326
327
    // Keep a reference on the Java VM.
    myVm = vm;

328
329
    pthread_mutex_init(&vout_android_lock, NULL);

330
    LOGD("JNI interface loaded.");
331
    return JNI_VERSION_1_2;
332
333
}

334
335
336
337
void JNI_OnUnload(JavaVM* vm, void* reserved) {
    pthread_mutex_destroy(&vout_android_lock);
}

338
void Java_org_videolan_libvlc_LibVLC_attachSurface(JNIEnv *env, jobject thiz, jobject surf, jobject gui, jint width, jint height) {
339
340
341
342
343
344
345
    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) {
346
        jthrowable exp = (*env)->ExceptionOccurred(env);
347
348
349
350
351
352
353
354
        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);
355
356

    vout_android_gui = (*env)->NewGlobalRef(env, gui);
357
358
359
    pthread_mutex_unlock(&vout_android_lock);
}

360
void Java_org_videolan_libvlc_LibVLC_detachSurface(JNIEnv *env, jobject thiz) {
361
362
    pthread_mutex_lock(&vout_android_lock);
    vout_android_surf = NULL;
363
364
365
    if (vout_android_gui != NULL)
        (*env)->DeleteGlobalRef(env, vout_android_gui);
    vout_android_gui = NULL;
366
367
    pthread_mutex_unlock(&vout_android_lock);
}
368

Rafaël Carré's avatar
Rafaël Carré committed
369
370
371
// FIXME: use atomics
static bool verbosity;
static bool buffer_logging;
372

373
374
375
376
377
static void debug_buffer_log(void *data, int level, const char *fmt, va_list ap)
{
    bool isAttached = false;
    JNIEnv *env = NULL;

Rafaël Carré's avatar
Rafaël Carré committed
378
379
    if ((*myVm)->GetEnv(myVm, (void**) &env, JNI_VERSION_1_2) < 0) {
        if ((*myVm)->AttachCurrentThread(myVm, &env, NULL) < 0)
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
            return;
        isAttached = true;
    }

    /* Prepare message string */
    char* psz_fmt_newline = malloc(strlen(fmt) + 2);
    if(!psz_fmt_newline)
        return;
    strcpy(psz_fmt_newline, fmt);
    strcat(psz_fmt_newline, "\n");
    char* psz_msg = NULL;
    int res = vasprintf(&psz_msg, psz_fmt_newline, ap);
    free(psz_fmt_newline);
    if(res < 0)
        return;

    jobject buffer = debugBufferInstance;
    jclass buffer_class = (*env)->FindClass(env, "java/lang/StringBuffer");
    jmethodID bufferAppendID = (*env)->GetMethodID(env, buffer_class, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");

    jstring message = (*env)->NewStringUTF(env, psz_msg);
    (*env)->CallObjectMethod(env, buffer, bufferAppendID, message);
    (*env)->DeleteLocalRef(env, message);
    free(psz_msg);

    if (isAttached)
        (*myVm)->DetachCurrentThread(myVm);
}

Rafaël Carré's avatar
Rafaël Carré committed
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
static void debug_log(void *data, int level, const libvlc_log_t *ctx, const char *fmt, va_list ap)
{
    bool *verbose = data;

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

    if (buffer_logging) {
        va_list aq;
        va_copy(aq, ap);
        debug_buffer_log(data, level, fmt, aq);
        va_end(aq);
    }

    if (!*verbose && prio < ANDROID_LOG_ERROR)
        return;

    __android_log_vprint(prio, "VLC", fmt, ap);
}
437

438
void Java_org_videolan_libvlc_LibVLC_startDebugBuffer(JNIEnv *env, jobject thiz)
439
{
440
441
    jclass libVLC_class = (*env)->FindClass(env, "org/videolan/libvlc/LibVLC");
    jmethodID getInstance = (*env)->GetStaticMethodID(env, libVLC_class, "getInstance", "()Lorg/videolan/libvlc/LibVLC;");
442
443
444
445
446
447
448
449
450
451
452
453
454
    jobject libvlcj = (*env)->CallStaticObjectMethod(env, libVLC_class, getInstance);

    jfieldID bufferID = (*env)->GetFieldID(env, libVLC_class, "mDebugLogBuffer", "Ljava/lang/StringBuffer;");
    jobject buffer = (*env)->GetObjectField(env, libvlcj, bufferID);

    debugBufferInstance = (*env)->NewGlobalRef(env, buffer);
    (*env)->DeleteLocalRef(env, buffer);

    jfieldID buffer_flag = (*env)->GetFieldID(env, libVLC_class, "mIsBufferingLog", "Z");
    (*env)->SetBooleanField(env, libvlcj, buffer_flag, JNI_TRUE);

    (*env)->DeleteLocalRef(env, libVLC_class);
    (*env)->DeleteLocalRef(env, libvlcj);
Rafaël Carré's avatar
Rafaël Carré committed
455
    buffer_logging = true;
456
457
}

458
void Java_org_videolan_libvlc_LibVLC_stopDebugBuffer(JNIEnv *env, jobject thiz)
459
{
Rafaël Carré's avatar
Rafaël Carré committed
460
    buffer_logging = false;
461
462
    jclass libVLC_class = (*env)->FindClass(env, "org/videolan/libvlc/LibVLC");
    jmethodID getInstance = (*env)->GetStaticMethodID(env, libVLC_class, "getInstance", "()Lorg/videolan/libvlc/LibVLC;");
463
464
465
466
467
468
469
470
471
472
473
    jobject libvlcj = (*env)->CallStaticObjectMethod(env, libVLC_class, getInstance);

    (*env)->DeleteGlobalRef(env, debugBufferInstance);

    jfieldID buffer_flag = (*env)->GetFieldID(env, libVLC_class, "mIsBufferingLog", "Z");
    (*env)->SetBooleanField(env, libvlcj, buffer_flag, JNI_FALSE);

    (*env)->DeleteLocalRef(env, libVLC_class);
    (*env)->DeleteLocalRef(env, libvlcj);
}

474
void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
475
{
476
477
478
479
480
    //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;

481
482
483
    methodId = (*env)->GetMethodID(env, cls, "timeStretchingEnabled", "()Z");
    bool enable_time_stretch = (*env)->CallBooleanMethod(env, thiz, methodId);

484
485
486
487
    methodId = (*env)->GetMethodID(env, cls, "getChroma", "()Ljava/lang/String;");
    jstring chroma = (*env)->CallObjectMethod(env, thiz, methodId);
    const char *chromastr = (*env)->GetStringUTFChars(env, chroma, 0);
    LOGD("Chroma set to \"%s\"", chromastr);
Edward Wang's avatar
Edward Wang committed
488

489
490
491
    methodId = (*env)->GetMethodID(env, cls, "getSubtitlesEncoding", "()Ljava/lang/String;");
    jstring subsencoding = (*env)->CallObjectMethod(env, thiz, methodId);
    const char *subsencodingstr = (*env)->GetStringUTFChars(env, subsencoding, 0);
Edward Wang's avatar
Edward Wang committed
492
    LOGD("Subtitle encoding set to \"%s\"", subsencodingstr);
493

494
495
496
    methodId = (*env)->GetMethodID(env, cls, "isVerboseMode", "()Z");
    verbosity = (*env)->CallBooleanMethod(env, thiz, methodId);

497
    /* Don't add any invalid options, otherwise it causes LibVLC to crash */
498
    const char *argv[] = {
499
        "-I", "dummy",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
500
501
502
        "--no-osd",
        "--no-video-title-show",
        "--no-stats",
503
504
        "--no-plugins-cache",
        "--no-drop-late-frames",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
505
        "--avcodec-fast",
506
        "--avcodec-threads=0",
507
        "--subsdec-encoding", subsencodingstr,
508
        enable_time_stretch ? "--audio-time-stretch" : "--no-audio-time-stretch",
509
        use_opensles ? "--aout=opensles" : "--aout=android_audiotrack",
510
        "--androidsurface-chroma", chromastr != NULL && chromastr[0] != 0 ? chromastr : "RV32",
511
    };
512
    libvlc_instance_t *instance = libvlc_new(sizeof(argv) / sizeof(*argv), argv);
513

Ludovic Fauvet's avatar
Ludovic Fauvet committed
514
    setLong(env, thiz, "mLibVlcInstance", (jlong)(intptr_t) instance);
515

516
    (*env)->ReleaseStringUTFChars(env, chroma, chromastr);
517
518
    (*env)->ReleaseStringUTFChars(env, subsencoding, subsencodingstr);

519
520
    if (!instance)
    {
521
        jclass exc = (*env)->FindClass(env, "org/videolan/libvlc/LibVlcException");
522
523
524
525
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
526

Rafaël Carré's avatar
Rafaël Carré committed
527
528
    libvlc_log_set(instance, debug_log, &verbosity);

529
530
531
    /* Initialize media list (a.k.a. playlist/history) */
    libvlc_media_list_t* pointer = libvlc_media_list_new( instance );
    if(!pointer) {
532
        jclass exc = (*env)->FindClass(env, "org/videolan/libvlc/LibVlcException");
533
534
535
536
        (*env)->ThrowNew(env, exc, "Unable to create LibVLC media list");
        return;
    }

537
538
539
540
541
542
543
544
545
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_list_event_manager(pointer);
    static const libvlc_event_type_t mp_events[] = {
        libvlc_MediaListItemAdded,
        libvlc_MediaListItemDeleted,
    };
    for(int i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); i++)
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

Ludovic Fauvet's avatar
Ludovic Fauvet committed
546
    setLong(env, thiz, "mMediaListInstance", (jlong)(intptr_t)pointer);
547
548
}

549
jstring Java_org_videolan_libvlc_LibVLC_nativeToURI(JNIEnv *env, jobject thiz, jstring path)
550
551
552
553
554
{
    jboolean isCopy;
    /* Get C string */
    const char* psz_path = (*env)->GetStringUTFChars(env, path, &isCopy);
    /* Convert the path to URI */
555
556
557
558
559
    char* psz_location;
    if(unlikely( strstr( psz_path, "://" ) ))
        psz_location = strdup(psz_path);
    else
        psz_location = vlc_path2uri(psz_path, "file");
560
561
562
563
564
565
566
    /* Box into jstring */
    jstring t = (*env)->NewStringUTF(env, psz_location);
    /* Clean up */
    (*env)->ReleaseStringUTFChars(env, path, psz_path);
    free(psz_location);
    return t;
}
567

568
void Java_org_videolan_libvlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
569
{
570
    releaseMediaPlayer(env, thiz);
Edward Wang's avatar
Edward Wang committed
571
    jlong libVlcInstance = getLong(env, thiz, "mLibVlcInstance");
572
573
574
    if (!libVlcInstance)
        return; // Already destroyed

Ludovic Fauvet's avatar
Ludovic Fauvet committed
575
    libvlc_instance_t *instance = (libvlc_instance_t*)(intptr_t) libVlcInstance;
Rafaël Carré's avatar
Rafaël Carré committed
576
    libvlc_log_unset(instance);
577
578
    libvlc_release(instance);

Edward Wang's avatar
Edward Wang committed
579
    setLong(env, thiz, "mLibVlcInstance", 0);
580
581
}

582
void Java_org_videolan_libvlc_LibVLC_detachEventHandler(JNIEnv *env, jobject thiz)
583
{
584
585
586
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
587
588
589
    }
}

590
void Java_org_videolan_libvlc_LibVLC_setEventHandler(JNIEnv *env, jobject thiz, jobject eventHandler)
591
{
592
593
594
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
595
596
    }

597
    jclass cls = (*env)->GetObjectClass(env, eventHandler);
598
    if (!cls) {
599
        LOGE("setEventHandler: failed to get class reference");
600
601
602
        return;
    }

603
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(ILandroid/os/Bundle;)V");
604
    if (!methodID) {
605
        LOGE("setEventHandler: failed to get the callback method");
606
607
        return;
    }
608

609
    eventHandlerInstance = (*env)->NewGlobalRef(env, eventHandler);
610
611
}

612
jobjectArray Java_org_videolan_libvlc_LibVLC_readMediaMeta(JNIEnv *env,
613
                                                        jobject thiz, jlong instance, jstring mrl)
Rafaël Carré's avatar
Rafaël Carré committed
614
615
616
617
618
{
    jobjectArray array = (*env)->NewObjectArray(env, 8,
            (*env)->FindClass(env, "java/lang/String"),
            (*env)->NewStringUTF(env, ""));

619
    libvlc_media_t *m = new_media(instance, env, thiz, mrl, false, false);
620
621
    if (!m)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
622
        LOGE("readMediaMeta: Could not create the media!");
623
        return array;
624
625
626
    }

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

628
    static const char str[][7] = {
Rafaël Carré's avatar
Rafaël Carré committed
629
630
        "artist", "album", "title", "genre",
    };
631
    static const libvlc_meta_t meta_id[] = {
Rafaël Carré's avatar
Rafaël Carré committed
632
633
634
635
636
        libvlc_meta_Artist,
        libvlc_meta_Album,
        libvlc_meta_Title,
        libvlc_meta_Genre,
    };
637
    for (int i=0; i < sizeof(str) / sizeof(*str); i++) {
Rafaël Carré's avatar
Rafaël Carré committed
638
639
        char *meta = libvlc_media_get_meta(m, meta_id[i]);
        if (!meta)
640
            meta = strdup("");
Rafaël Carré's avatar
Rafaël Carré committed
641
642
643
644
645
646
647
648
649
650
651
652
653

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

654
655
static void create_player_and_play(JNIEnv* env, jobject thiz,
                                   jlong instance, int position) {
656
657
658
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

659
660
    libvlc_media_list_t* p_mlist = getMediaList(env, thiz);

661
    /* Create a media player playing environment */
Ludovic Fauvet's avatar
Ludovic Fauvet committed
662
663
    libvlc_media_list_player_t* p_mlp = libvlc_media_list_player_new((libvlc_instance_t*)(intptr_t)instance);
    libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)(intptr_t)instance);
664

665
666
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz);

667
668
669
670
    //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 )
671
    {
672
        libvlc_audio_set_callbacks(mp, aout_play, aout_pause, NULL, NULL, NULL,
Sébastien Toque's avatar
Sébastien Toque committed
673
674
                                   (void*) myJavaLibVLC);
        libvlc_audio_set_format_callbacks(mp, aout_open, aout_close);
675
676
    }

677
678
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
679
680
681
682
683
    static const libvlc_event_type_t mp_events[] = {
        libvlc_MediaPlayerPlaying,
        libvlc_MediaPlayerPaused,
        libvlc_MediaPlayerEndReached,
        libvlc_MediaPlayerStopped,
684
        libvlc_MediaPlayerVout,
685
686
        libvlc_MediaPlayerPositionChanged,
        libvlc_MediaPlayerEncounteredError
687
    };
688
    for(int i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); i++)
689
690
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

691
692
693
    libvlc_media_list_player_set_media_list(p_mlp, p_mlist);
    libvlc_media_list_player_set_media_player(p_mlp, mp);

694
    /* Keep a pointer to this media player */
Ludovic Fauvet's avatar
Ludovic Fauvet committed
695
696
    setLong(env, thiz, "mMediaListPlayerInstance", (jlong)(intptr_t)p_mlp);
    setLong(env, thiz, "mInternalMediaPlayerInstance", (jlong)(intptr_t)mp);
697

698
    libvlc_media_list_player_play_item_at_index(p_mlp, position);
699
700
}

701
jint Java_org_videolan_libvlc_LibVLC_readMedia(JNIEnv *env, jobject thiz,
702
703
704
705
706
707
708
                                            jlong instance, jstring mrl, jboolean novideo)
{
    /* Create a new item */
    libvlc_media_t *m = new_media(instance, env, thiz, mrl, false, novideo);
    if (!m)
    {
        LOGE("readMedia: Could not create the media!");
709
        return -1;
710
711
712
713
714
    }

    libvlc_media_list_t* p_mlist = getMediaList(env, thiz);

    libvlc_media_list_lock(p_mlist);
715
716
717
718
    if(libvlc_media_list_add_media(p_mlist, m) != 0) {
        LOGE("readMedia: Could not add to the media list!");
        libvlc_media_list_unlock(p_mlist);
        libvlc_media_release(m);
719
        return -1;
720
    }
721
722
723
724
725
726
727
    int position = libvlc_media_list_index_of_item(p_mlist, m);
    libvlc_media_list_unlock(p_mlist);

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

    create_player_and_play(env, thiz, instance, position);
728
729

    return position;
730
731
}

732
void Java_org_videolan_libvlc_LibVLC_playIndex(JNIEnv *env, jobject thiz,
733
734
735
736
                                            jlong instance, int position) {
    create_player_and_play(env, thiz, instance, position);
}

737
void Java_org_videolan_libvlc_LibVLC_getMediaListItems(
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
                JNIEnv *env, jobject thiz, jobject arrayList) {
    jclass arrayClass = (*env)->FindClass(env, "java/util/ArrayList");
    jmethodID methodID = (*env)->GetMethodID(env, arrayClass, "add", "(Ljava/lang/Object;)Z");
    jstring str;

    libvlc_media_list_t* p_mlist = getMediaList(env, thiz);
    libvlc_media_list_lock( p_mlist );
    for(int i = 0; i < libvlc_media_list_count( p_mlist ); i++) {
        char* mrl = libvlc_media_get_mrl( libvlc_media_list_item_at_index( p_mlist, i ) );
        str = (*env)->NewStringUTF(env, mrl);
        (*env)->CallBooleanMethod(env, arrayList, methodID, str);
        (*env)->DeleteLocalRef(env, str);
        free(mrl);
    }
    libvlc_media_list_unlock( p_mlist );
}

755
jfloat Java_org_videolan_libvlc_LibVLC_getRate(JNIEnv *env, jobject thiz) {
Edward Wang's avatar
Edward Wang committed
756
757
758
759
760
761
762
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        return libvlc_media_player_get_rate(mp);
    else
        return 1.00;
}

763
void Java_org_videolan_libvlc_LibVLC_setRate(JNIEnv *env, jobject thiz, jfloat rate) {
Edward Wang's avatar
Edward Wang committed
764
765
766
767
768
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        libvlc_media_player_set_rate(mp, rate);
}

769
jboolean Java_org_videolan_libvlc_LibVLC_hasVideoTrack(JNIEnv *env, jobject thiz,
Edward Wang's avatar
Edward Wang committed
770
                                                    jlong i_instance, jstring fileLocation)
771
{
772
    /* Create a new item and assign it to the media player. */
773
    libvlc_media_t *p_m = new_media(i_instance, env, thiz, fileLocation, false, false);
774
775
    if (p_m == NULL)
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
776
        LOGE("Could not create the media!");
Rafaël Carré's avatar
Rafaël Carré committed
777
        return JNI_FALSE;
778
779
780
781
    }

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

783
784
785
786
787
788
789
790
791
792
    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
793

794
795
796
797
798
    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);
799
800
801
802
803
804
805
806
807

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

810
811
812
813
814
    int i_nbTracks;
    if( mp_alive )
        i_nbTracks = libvlc_video_get_track_count(p_mp);
    else
        i_nbTracks = -1;
815
816
    LOGI("Number of video tracks: %d",i_nbTracks);

Edward Wang's avatar
Edward Wang committed
817
    libvlc_event_detach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
818
819
    libvlc_media_player_stop(p_mp);
    libvlc_media_player_release(p_mp);
Rafaël Carré's avatar
Rafaël Carré committed
820
821
    libvlc_media_release(p_m);

Edward Wang's avatar
Edward Wang committed
822
823
824
825
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);

826
827
    if(i_nbTracks > 0)
        return JNI_TRUE;
828
829
    else if(i_nbTracks < 0)
        (*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/IOException"), "VLC can't open the file");
830
831
    else
        return JNI_FALSE;
832
833
}

834
jobjectArray read_track_info_internal(JNIEnv *env, jobject thiz, libvlc_media_t* p_m)
835
836
{
    /* get java class */
837
    jclass cls = (*env)->FindClass( env, "org/videolan/libvlc/TrackInfo" );
838
839
    if ( !cls )
    {
840
        LOGE("Failed to load class (org/videolan/libvlc/TrackInfo)" );
841
842
843
844
845
846
847
        return NULL;
    }

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

    /* Get the tracks information of the media. */
853
    libvlc_media_track_t **p_tracks;
854

855
    int i_nbTracks = libvlc_media_tracks_get(p_m, &p_tracks);
856
    jobjectArray array = (*env)->NewObjectArray(env, i_nbTracks + 1, cls, NULL);
857
858
859
860

    unsigned i;
    if (array != NULL)
    {
861
        for (i = 0; i <= i_nbTracks; ++i)
862
863
864
865
866
        {
            jobject item = (*env)->NewObject(env, cls, clsCtor);
            if (item == NULL)
                continue;
            (*env)->SetObjectArrayElement(env, array, i, item);
867
868
869
870
871
872
873
874
875
876

            // 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));
877
                setString(env, item, "ArtworkURL", libvlc_media_get_meta(p_m, libvlc_meta_ArtworkURL));
878
879
880
                continue;
            }

881
882
883
884
            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));
            setString(env, item, "Language", p_tracks[i]->psz_language);
885

886
            if (p_tracks[i]->i_type == libvlc_track_video)
887
            {
888
889
890
                setInt(env, item, "Height", p_tracks[i]->video->i_height);
                setInt(env, item, "Width", p_tracks[i]->video->i_width);
                setFloat(env, item, "Framerate", (float)p_tracks[i]->video->i_frame_rate_num / p_tracks[i]->video->i_frame_rate_den);
891
            }
892
            if (p_tracks[i]->i_type == libvlc_track_audio)
893
            {
894
895
                setInt(env, item, "Channels", p_tracks[i]->audio->i_channels);
                setInt(env, item, "Samplerate", p_tracks[i]->audio->i_rate);
896
897
898
899
            }
        }
    }

900
    libvlc_media_tracks_release(p_tracks, i_nbTracks);
901
902
903
    return array;
}

904

905
jobjectArray Java_org_videolan_libvlc_LibVLC_readTracksInfo(JNIEnv *env, jobject thiz,
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
                                                         jlong instance, jstring mrl)
{
    /* Create a new item and assign it to the media player. */
    libvlc_media_t *p_m = new_media(instance, env, thiz, mrl, false, false);
    if (p_m == NULL)
    {
        LOGE("Could not create the media!");
        return NULL;
    }

    libvlc_media_parse(p_m);
    jobjectArray jar = read_track_info_internal(env, thiz, p_m);
    libvlc_media_release(p_m);
    return jar;
}


923
jobjectArray Java_org_videolan_libvlc_LibVLC_readTracksInfoPosition(JNIEnv *env, jobject thiz,
924
925
926
927
928
929
930
931
932
933
934
                                                         jint position)
{
    libvlc_media_list_t* p_mlist = getMediaList(env, thiz);
    libvlc_media_t *p_m = libvlc_media_list_item_at_index( p_mlist, position );
    if (p_m == NULL) {
        LOGE("Could not load get media @ position %d!", position);
        return NULL;
    } else
        return read_track_info_internal(env, thiz, p_m);
}

935
jlong Java_org_videolan_libvlc_LibVLC_getLengthFromLocation(JNIEnv *env, jobject thiz,
Edward Wang's avatar
Edward Wang committed
936
                                                     jlong i_instance, jstring fileLocation)
937
{
938
939
940
941
942
    jlong length = 0;
    struct length_change_monitor *monitor;
    monitor = malloc(sizeof(*monitor));
    if (!monitor)
        return 0;
943
944

    /* Initialize pthread variables. */
945
946
947
    pthread_mutex_init(&monitor->doneMutex, NULL);
    pthread_cond_init(&monitor->doneCondVar, NULL);
    monitor->length_changed = false;
948

949
    /* Create a new item and assign it to the media player. */
950
    libvlc_media_t *m = new_media(i_instance, env, thiz, fileLocation, false, false);
951
    if (m == NULL)
952
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
953
        LOGE("Could not create the media to play!");
954
        goto end;
955
956
    }

957
    /* Create a media player playing environment */
958
    libvlc_media_player_t *mp = libvlc_media_player_new_from_media (m);
959
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
960
    libvlc_event_attach(ev, libvlc_MediaPlayerLengthChanged, length_changed_callback, monitor);
961
962
    libvlc_media_release (m);
    libvlc_media_player_play( mp );
963
964
965
966
967
968
969

    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 );
970
    libvlc_media_player_stop( mp );
971
    libvlc_media_player_release( mp );
972

973
974
975
976
end:
    pthread_mutex_destroy(&monitor->doneMutex);
    pthread_cond_destroy(&monitor->doneCondVar);
    free(monitor);
977
978

    return length;
979
980
}

981
jboolean Java_org_videolan_libvlc_LibVLC_hasMediaPlayer(JNIEnv *env, jobject thiz)
982
{
983
    return !!getMediaListPlayer(env, thiz);
984
985
}

986
jboolean Java_org_videolan_libvlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
987
{
988
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
989
    if (mp)
990
991
992
        return !!libvlc_media_list_player_is_playing(mp);
    else
        return 0;
993
994
}

995
jboolean Java_org_videolan_libvlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
996
{
997
998
999
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_seekable(mp);
1000
    return 0;
1001
1002
}

1003
void Java_org_videolan_libvlc_LibVLC_play(JNIEnv *env, jobject thiz)
1004
{
1005
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
1006
    if (mp)
1007
        libvlc_media_list_player_play(mp);
1008
1009
}

1010
void Java_org_videolan_libvlc_LibVLC_pause(JNIEnv *env, jobject thiz)
1011
{
1012
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
1013
    if (mp)
1014
        libvlc_media_list_player_pause(mp);
1015
1016
}

1017
void Java_org_videolan_libvlc_LibVLC_stop(JNIEnv *env, jobject thiz)
1018
{
1019
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
1020
    if (mp)
1021
        libvlc_media_list_player_stop(mp);
1022
1023
}

1024
void Java_org_videolan_libvlc_LibVLC_previous(JNIEnv *env, jobject thiz)
Edward Wang's avatar
Edward Wang committed
1025
1026
1027
1028
1029
1030
{
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
    if (mp)
        libvlc_media_list_player_previous(mp);
}

1031
void Java_org_videolan_libvlc_LibVLC_next(JNIEnv *env, jobject thiz)
Edward Wang's avatar
Edward Wang committed
1032
1033
1034
1035
1036
1037
{
    libvlc_media_list_player_t *mp = getMediaListPlayer(env, thiz);
    if (mp)
        libvlc_media_list_player_next(mp);
}

1038
jint Java_org_videolan_libvlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
1039
{
1040
1041
1042
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
1043
1044
1045
    return -1;
}

1046
jint Java_org_videolan_libvlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
1047
{
1048
1049
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
1050
        //Returns 0 if the volume was set, -1 if it was out of range or error
1051
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
1052
1053
1054
    return -1;
}

1055
jlong Java_org_videolan_libvlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
1056
{
1057
1058
1059
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
1060
1061
1062
    return -1;
}

1063
void Java_org_videolan_libvlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
1064
{
1065
1066