libvlcjni.c 30.9 KB
Newer Older
Rafaël Carré's avatar
Rafaël Carré committed
1 2 3
/*****************************************************************************
 * libvlcjni.c
 *****************************************************************************
Edward Wang's avatar
Edward Wang committed
4
 * Copyright © 2010-2013 VLC authors and VideoLAN
Rafaël Carré's avatar
Rafaël Carré committed
5
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
6 7 8
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
Rafaël Carré's avatar
Rafaël Carré committed
9 10 11 12
 * (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
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
13 14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
Rafaël Carré's avatar
Rafaël Carré committed
15
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
16 17 18
 * You should have received a copy of the GNU Lesser 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.
Rafaël Carré's avatar
Rafaël Carré committed
19 20
 *****************************************************************************/

21
#include <assert.h>
22 23
#include <dirent.h>
#include <errno.h>
24
#include <string.h>
25
#include <pthread.h>
26
#include <sys/stat.h>
27
#include <sys/types.h>
28
#include <unistd.h>
29

30
#include <vlc/vlc.h>
31 32
#include <vlc_common.h>
#include <vlc_url.h>
33

34
#include <jni.h>
35

36
#include <android/api-level.h>
37

38
#include "libvlcjni.h"
39
#include "vout.h"
40
#include "utils.h"
41
#include "native_crash_handler.h"
42

43 44
#define VOUT_ANDROID_SURFACE 0
#define VOUT_OPENGLES2       1
45
#define VOUT_ANDROID_WINDOW  2
46

47 48 49
#define AOUT_AUDIOTRACK      0
#define AOUT_OPENSLES        1

50
#define LOG_TAG "VLC/JNI/main"
51 52
#include "log.h"

Thomas Guillem's avatar
Thomas Guillem committed
53 54
struct fields fields;

55 56
#define VLC_JNI_VERSION JNI_VERSION_1_2

57 58 59 60 61
#define THREAD_NAME "libvlcjni"
int jni_attach_thread(JNIEnv **env, const char *thread_name);
void jni_detach_thread();
int jni_get_env(JNIEnv **env);

62 63 64 65 66 67 68 69 70 71 72 73
static void add_media_options(libvlc_media_t *p_md, JNIEnv *env, jobjectArray mediaOptions)
{
    int stringCount = (*env)->GetArrayLength(env, mediaOptions);
    for(int i = 0; i < stringCount; i++)
    {
        jstring option = (jstring)(*env)->GetObjectArrayElement(env, mediaOptions, i);
        const char* p_st = (*env)->GetStringUTFChars(env, option, 0);
        libvlc_media_add_option(p_md, p_st); // option
        (*env)->ReleaseStringUTFChars(env, option, p_st);
    }
}

74
libvlc_media_t *new_media(JNIEnv *env, jobject thiz, jstring fileLocation, bool noOmx, bool noVideo)
75
{
76
    libvlc_instance_t *libvlc = getLibVlcInstance(env, thiz);
77
    jboolean isCopy;
78 79 80
    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);
81 82 83
    if (!p_md)
        return NULL;

84 85 86 87 88 89 90 91 92
    jclass cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodId = (*env)->GetMethodID(env, cls, "getMediaOptions", "(ZZ)[Ljava/lang/String;");
    if (methodId != NULL)
    {
        jobjectArray mediaOptions = (*env)->CallObjectMethod(env, thiz, methodId, noOmx, noVideo);
        if (mediaOptions != NULL)
        {
            add_media_options(p_md, env, mediaOptions);
            (*env)->DeleteLocalRef(env, mediaOptions);
93
        }
94
    }
95
    return p_md;
96 97
}

98 99 100 101 102
libvlc_instance_t *getLibVlcInstance(JNIEnv *env, jobject thiz)
{
    return (libvlc_instance_t*)(intptr_t)getLong(env, thiz, "mLibVlcInstance");
}

103
libvlc_media_player_t *getMediaPlayer(JNIEnv *env, jobject thiz)
104
{
Ludovic Fauvet's avatar
Ludovic Fauvet committed
105
    return (libvlc_media_player_t*)(intptr_t)getLong(env, thiz, "mInternalMediaPlayerInstance");
106 107
}

108

Rafaël Carré's avatar
Rafaël Carré committed
109 110
static void releaseMediaPlayer(JNIEnv *env, jobject thiz)
{
111 112
    libvlc_media_player_t* p_mp = getMediaPlayer(env, thiz);
    if (p_mp)
113
    {
114 115
        libvlc_media_player_stop(p_mp);
        libvlc_media_player_release(p_mp);
116
        setLong(env, thiz, "mInternalMediaPlayerInstance", 0);
117 118 119
    }
}

120 121 122 123
/* 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
 */
Thomas Guillem's avatar
Thomas Guillem committed
124
static JavaVM *myVm;
125

126
static jobject eventHandlerInstance = NULL;
127 128 129 130 131

static void vlc_event_callback(const libvlc_event_t *ev, void *data)
{
    JNIEnv *env;

132
    bool isAttached = false;
133

134
    if (eventHandlerInstance == NULL)
Sébastien Toque's avatar
Sébastien Toque committed
135
        return;
136

137 138
    if (jni_get_env(&env) < 0) {
        if (jni_attach_thread(&env, THREAD_NAME) < 0)
139
            return;
140
        isAttached = true;
141 142
    }

143 144 145 146 147 148 149 150 151 152
    /* 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" );
153
    jmethodID putLong = (*env)->GetMethodID(env, clsBundle, "putLong", "(Ljava/lang/String;J)V" );
154
    jmethodID putFloat = (*env)->GetMethodID(env, clsBundle, "putFloat", "(Ljava/lang/String;F)V" );
155
    jmethodID putString = (*env)->GetMethodID(env, clsBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V" );
Thomas Guillem's avatar
Thomas Guillem committed
156
    (*env)->DeleteLocalRef(env, clsBundle);
157

158
    if (ev->type == libvlc_MediaPlayerPositionChanged) {
159 160 161
        jstring sData = (*env)->NewStringUTF(env, "data");
        (*env)->CallVoidMethod(env, bundle, putFloat, sData, ev->u.media_player_position_changed.new_position);
        (*env)->DeleteLocalRef(env, sData);
162 163
    } else if (ev->type == libvlc_MediaPlayerTimeChanged) {
        jstring sData = (*env)->NewStringUTF(env, "data");
164
        (*env)->CallVoidMethod(env, bundle, putLong, sData, ev->u.media_player_time_changed.new_time);
165
        (*env)->DeleteLocalRef(env, sData);
166
    } else if(ev->type == libvlc_MediaPlayerVout) {
167 168 169 170
        /* 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);
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
    } 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);
194 195 196 197 198
    } else if(ev->type == libvlc_MediaPlayerESAdded ||
              ev->type == libvlc_MediaPlayerESDeleted ) {
        jstring sData = (*env)->NewStringUTF(env, "data");
        (*env)->CallVoidMethod(env, bundle, putInt, sData, ev->u.media_player_es_changed.i_type);
        (*env)->DeleteLocalRef(env, sData);
199 200
    }

201
    /* Get the object class */
202
    jclass cls = (*env)->GetObjectClass(env, eventHandlerInstance);
203
    if (!cls) {
204
        LOGE("EventHandler: failed to get class reference");
205
        goto end;
206 207 208
    }

    /* Find the callback ID */
209
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callback", "(ILandroid/os/Bundle;)V");
210
    if (methodID) {
211
        (*env)->CallVoidMethod(env, eventHandlerInstance, methodID, ev->type, bundle);
212
    } else {
213
        LOGE("EventHandler: failed to get the callback method");
214
    }
Thomas Guillem's avatar
Thomas Guillem committed
215
    (*env)->DeleteLocalRef(env, cls);
216

217
end:
Edward Wang's avatar
Edward Wang committed
218
    (*env)->DeleteLocalRef(env, bundle);
219
    if (isAttached)
220
        jni_detach_thread();
221 222
}

223
jint JNI_OnLoad(JavaVM *vm, void *reserved)
224
{
Thomas Guillem's avatar
Thomas Guillem committed
225
    JNIEnv* env = NULL;
226 227 228
    // Keep a reference on the Java VM.
    myVm = vm;

Thomas Guillem's avatar
Thomas Guillem committed
229 230
    if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
        return -1;
231
    pthread_mutex_init(&vout_android_lock, NULL);
232
    pthread_cond_init(&vout_android_surf_attached, NULL);
233

Thomas Guillem's avatar
Thomas Guillem committed
234
#define GET_CLASS(clazz, str, b_globlal) do { \
Thomas Guillem's avatar
Thomas Guillem committed
235 236 237 238 239
    (clazz) = (*env)->FindClass(env, (str)); \
    if (!(clazz)) { \
        LOGE("FindClass(%s) failed", (str)); \
        return -1; \
    } \
Thomas Guillem's avatar
Thomas Guillem committed
240 241 242 243 244 245
    if (b_globlal) { \
        (clazz) = (jclass) (*env)->NewGlobalRef(env, (clazz)); \
        if (!(clazz)) { \
            LOGE("NewGlobalRef(%s) failed", (str)); \
            return -1; \
        } \
Thomas Guillem's avatar
Thomas Guillem committed
246 247 248 249 250 251 252 253 254 255 256
    } \
} while (0)

#define GET_ID(get, id, clazz, str, args) do { \
    (id) = (*env)->get(env, (clazz), (str), (args)); \
    if (!(id)) { \
        LOGE(#get"(%s) failed", (str)); \
        return -1; \
    } \
} while (0)

Thomas Guillem's avatar
Thomas Guillem committed
257 258 259 260 261 262 263 264
    jclass Version_clazz;
    jfieldID SDK_INT_fieldID;

    GET_CLASS(Version_clazz, "android/os/Build$VERSION", false);
    GET_ID(GetStaticFieldID, SDK_INT_fieldID, Version_clazz, "SDK_INT", "I");
    fields.SDK_INT = (*env)->GetStaticIntField(env, Version_clazz,
                                               SDK_INT_fieldID);

Thomas Guillem's avatar
Thomas Guillem committed
265
    GET_CLASS(fields.IllegalStateException.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
266
              "java/lang/IllegalStateException", true);
Thomas Guillem's avatar
Thomas Guillem committed
267
    GET_CLASS(fields.IllegalArgumentException.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
268
              "java/lang/IllegalArgumentException", true);
Thomas Guillem's avatar
Thomas Guillem committed
269
    GET_CLASS(fields.String.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
270
              "java/lang/String", true);
271
    GET_CLASS(fields.LibVLC.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
272
              "org/videolan/libvlc/LibVLC", true);
Thomas Guillem's avatar
Thomas Guillem committed
273
    GET_CLASS(fields.VLCObject.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
274
              "org/videolan/libvlc/VLCObject", true);
275
    GET_CLASS(fields.Media.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
276
              "org/videolan/libvlc/Media", true);
277
    GET_CLASS(fields.Media.Track.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
278
              "org/videolan/libvlc/Media$Track", true);
Thomas Guillem's avatar
Thomas Guillem committed
279

280 281 282 283 284
    GET_ID(GetStaticMethodID,
           fields.LibVLC.onNativeCrashID,
           fields.LibVLC.clazz,
           "onNativeCrash", "()V");

Thomas Guillem's avatar
Thomas Guillem committed
285 286 287 288 289 290 291 292 293 294
    GET_ID(GetFieldID,
           fields.VLCObject.mInstanceID,
           fields.VLCObject.clazz,
           "mInstance", "J");

    GET_ID(GetMethodID,
           fields.VLCObject.dispatchEventFromNativeID,
           fields.VLCObject.clazz,
           "dispatchEventFromNative", "(IJJ)V");

Thomas Guillem's avatar
Thomas Guillem committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
    if (fields.SDK_INT <= 7)
    {
        LOGE("fields.SDK_INT is less than 7: using compat WeakReference");
        GET_ID(GetMethodID,
               fields.VLCObject.getWeakReferenceID,
               fields.VLCObject.clazz,
               "getWeakReference", "()Ljava/lang/Object;");
        GET_ID(GetStaticMethodID,
               fields.VLCObject.dispatchEventFromWeakNativeID,
               fields.VLCObject.clazz,
               "dispatchEventFromWeakNative", "(Ljava/lang/Object;IJJ)V");
    } else
    {
        fields.VLCObject.getWeakReferenceID = NULL;
        fields.VLCObject.dispatchEventFromWeakNativeID = NULL;
    }

312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    GET_ID(GetStaticMethodID,
           fields.Media.createAudioTrackFromNativeID,
           fields.Media.clazz,
           "createAudioTrackFromNative",
           "(Ljava/lang/String;Ljava/lang/String;IIIILjava/lang/String;Ljava/lang/String;II)"
           "Lorg/videolan/libvlc/Media$Track;");

    GET_ID(GetStaticMethodID,
           fields.Media.createVideoTrackFromNativeID,
           fields.Media.clazz,
           "createVideoTrackFromNative",
           "(Ljava/lang/String;Ljava/lang/String;IIIILjava/lang/String;Ljava/lang/String;IIIIII)"
           "Lorg/videolan/libvlc/Media$Track;");

    GET_ID(GetStaticMethodID,
           fields.Media.createSubtitleTrackFromNativeID,
           fields.Media.clazz,
           "createSubtitleTrackFromNative",
           "(Ljava/lang/String;Ljava/lang/String;IIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)"
           "Lorg/videolan/libvlc/Media$Track;");

Thomas Guillem's avatar
Thomas Guillem committed
333 334 335
#undef GET_CLASS
#undef GET_ID

336 337
    init_native_crash_handler();

338
    LOGD("JNI interface loaded.");
339
    return VLC_JNI_VERSION;
340 341
}

Thomas Guillem's avatar
Thomas Guillem committed
342 343 344 345
void JNI_OnUnload(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;

346
    pthread_mutex_destroy(&vout_android_lock);
347
    pthread_cond_destroy(&vout_android_surf_attached);
Thomas Guillem's avatar
Thomas Guillem committed
348

349 350
    destroy_native_crash_handler();

Thomas Guillem's avatar
Thomas Guillem committed
351 352 353 354 355 356 357
    if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
        return;

    (*env)->DeleteGlobalRef(env, fields.IllegalStateException.clazz);
    (*env)->DeleteGlobalRef(env, fields.IllegalArgumentException.clazz);
    (*env)->DeleteGlobalRef(env, fields.String.clazz);
    (*env)->DeleteGlobalRef(env, fields.VLCObject.clazz);
358
    (*env)->DeleteGlobalRef(env, fields.Media.clazz);
359 360
}

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
int jni_attach_thread(JNIEnv **env, const char *thread_name)
{
    JavaVMAttachArgs args;
    jint result;

    args.version = VLC_JNI_VERSION;
    args.name = thread_name;
    args.group = NULL;

    result = (*myVm)->AttachCurrentThread(myVm, env, &args);
    return result == JNI_OK ? 0 : -1;
}

void jni_detach_thread()
{
    (*myVm)->DetachCurrentThread(myVm);
}

int jni_get_env(JNIEnv **env)
{
381
    return (*myVm)->GetEnv(myVm, (void **)env, VLC_JNI_VERSION) == JNI_OK ? 0 : -1;
382 383
}

384
void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
385
{
386 387 388
    //only use OpenSLES if java side says we can
    jclass cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodId = (*env)->GetMethodID(env, cls, "getAout", "()I");
Thomas Guillem's avatar
Thomas Guillem committed
389
    int aout = (*env)->CallIntMethod(env, thiz, methodId);
390

391
    methodId = (*env)->GetMethodID(env, cls, "getVout", "()I");
392
    int vout = (*env)->CallIntMethod(env, thiz, methodId);
393

394 395 396
    methodId = (*env)->GetMethodID(env, cls, "timeStretchingEnabled", "()Z");
    bool enable_time_stretch = (*env)->CallBooleanMethod(env, thiz, methodId);

Edward Wang's avatar
Edward Wang committed
397 398 399
    methodId = (*env)->GetMethodID(env, cls, "frameSkipEnabled", "()Z");
    bool enable_frame_skip = (*env)->CallBooleanMethod(env, thiz, methodId);

Edward Wang's avatar
Edward Wang committed
400 401
    methodId = (*env)->GetMethodID(env, cls, "getDeblocking", "()I");
    int deblocking = (*env)->CallIntMethod(env, thiz, methodId);
Rafaël Carré's avatar
Rafaël Carré committed
402 403
    char deblockstr[2];
    snprintf(deblockstr, sizeof(deblockstr), "%d", deblocking);
Edward Wang's avatar
Edward Wang committed
404
    LOGD("Using deblocking level %d", deblocking);
405

Edward Wang's avatar
Edward Wang committed
406 407
    methodId = (*env)->GetMethodID(env, cls, "getNetworkCaching", "()I");
    int networkCaching = (*env)->CallIntMethod(env, thiz, methodId);
Rafaël Carré's avatar
Rafaël Carré committed
408
    char networkCachingstr[25];
Edward Wang's avatar
Edward Wang committed
409
    if(networkCaching > 0) {
Rafaël Carré's avatar
Rafaël Carré committed
410
        snprintf(networkCachingstr, sizeof(networkCachingstr), "--network-caching=%d", networkCaching);
Edward Wang's avatar
Edward Wang committed
411 412 413
        LOGD("Using network caching of %d ms", networkCaching);
    }

414 415 416
    methodId = (*env)->GetMethodID(env, cls, "getHttpReconnect", "()Z");
    bool enable_http_reconnect = (*env)->CallBooleanMethod(env, thiz, methodId);

417 418 419 420
    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
421

422 423 424
    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
425
    LOGD("Subtitle encoding set to \"%s\"", subsencodingstr);
426

427
    methodId = (*env)->GetMethodID(env, cls, "isVerboseMode", "()Z");
428
    bool b_verbose = (*env)->CallBooleanMethod(env, thiz, methodId);
429

430 431
    methodId = (*env)->GetMethodID(env, cls, "isDirectRendering", "()Z");
    bool direct_rendering = (*env)->CallBooleanMethod(env, thiz, methodId);
432
    /* With the MediaCodec opaque mode we cannot use the OpenGL ES vout. */
433
    if (direct_rendering)
434
        vout = VOUT_ANDROID_WINDOW;
435

flx42's avatar
flx42 committed
436 437
    methodId = (*env)->GetMethodID(env, cls, "getCachePath", "()Ljava/lang/String;");
    jstring cachePath = (*env)->CallObjectMethod(env, thiz, methodId);
438 439 440 441 442
    if (cachePath) {
        const char *cache_path = (*env)->GetStringUTFChars(env, cachePath, 0);
        setenv("DVDCSS_CACHE", cache_path, 1);
        (*env)->ReleaseStringUTFChars(env, cachePath, cache_path);
    }
flx42's avatar
flx42 committed
443

444
#define MAX_ARGV 20
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
    const char *argv[MAX_ARGV];
    int argc = 0;

    /* CPU intensive plugin, setting for slow devices */
    argv[argc++] = enable_time_stretch ? "--audio-time-stretch" : "--no-audio-time-stretch";
    /* avcodec-skiploopfilter */
    argv[argc++] = "--avcodec-skiploopfilter";
    argv[argc++] = deblockstr;
    /* avcodec-skip-frame */
    argv[argc++] = "--avcodec-skip-frame";
    argv[argc++] = enable_frame_skip ? "2" : "0";
    /* avcodec-skip-idct */
    argv[argc++] = "--avcodec-skip-idct";
    argv[argc++] = enable_frame_skip ? "2" : "0";
    /* Remove me when UTF-8 is enforced by law */
    argv[argc++] = "--subsdec-encoding";
    argv[argc++] = subsencodingstr;
    /* Enable statistics */
    argv[argc++] = "--stats";
    /* XXX: why can't the default be fine ? #7792 */
    if (networkCaching > 0)
        argv[argc++] = networkCachingstr;
    /* Android audio API */
    argv[argc++] = aout == AOUT_OPENSLES ? "--aout=opensles" :
        (aout == AOUT_AUDIOTRACK ? "--aout=android_audiotrack" : "--aout=dummy");
    /* Android video API  */
    argv[argc++] = vout == VOUT_ANDROID_WINDOW ? "--vout=androidwindow" :
        (vout == VOUT_OPENGLES2 ? "--vout=gles2" : "--vout=androidsurface");
    /* chroma */
    argv[argc++] = "--androidsurface-chroma";
    argv[argc++] = chromastr != NULL && chromastr[0] != 0 ? chromastr : "RV32";
    /* direct rendering */
    if (!direct_rendering) {
        argv[argc++] = "--no-mediacodec-dr";
#ifdef HAVE_IOMX_DR
        argv[argc++] = "--no-omxil-dr";
#endif
    }
483
    argv[argc++] = "--spdif";
484 485
    argv[argc++] = b_verbose ? "-vvv" : "-vv";

486 487 488 489 490 491
    /* Reconnect on lost HTTP streams, e.g. network change */
    if (enable_http_reconnect)
        argv[argc++] = "--http-reconnect";

    assert(MAX_ARGV >= argc);
    libvlc_instance_t *instance = libvlc_new(argc, argv);
492

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

495
    (*env)->ReleaseStringUTFChars(env, chroma, chromastr);
496 497
    (*env)->ReleaseStringUTFChars(env, subsencoding, subsencodingstr);

498 499
    if (!instance)
    {
500
        jclass exc = (*env)->FindClass(env, "org/videolan/libvlc/LibVlcException");
501 502 503 504
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
505 506
}

507
void Java_org_videolan_libvlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
508
{
509
    releaseMediaPlayer(env, thiz);
Edward Wang's avatar
Edward Wang committed
510
    jlong libVlcInstance = getLong(env, thiz, "mLibVlcInstance");
511 512 513
    if (!libVlcInstance)
        return; // Already destroyed

Ludovic Fauvet's avatar
Ludovic Fauvet committed
514
    libvlc_instance_t *instance = (libvlc_instance_t*)(intptr_t) libVlcInstance;
515 516
    libvlc_release(instance);

Edward Wang's avatar
Edward Wang committed
517
    setLong(env, thiz, "mLibVlcInstance", 0);
518 519
}

520
void Java_org_videolan_libvlc_LibVLC_detachEventHandler(JNIEnv *env, jobject thiz)
521
{
522 523 524
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
525 526 527
    }
}

528
void Java_org_videolan_libvlc_LibVLC_setEventHandler(JNIEnv *env, jobject thiz, jobject eventHandler)
529
{
530 531 532
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
533 534
    }

535
    eventHandlerInstance = getEventHandlerReference(env, thiz, eventHandler);
536 537
}

538
void Java_org_videolan_libvlc_LibVLC_playMRL(JNIEnv *env, jobject thiz,
539 540
                                             jstring mrl, jobjectArray mediaOptions)
{
541
    jclass cls;
542 543 544
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

545 546
    libvlc_instance_t *p_instance = getLibVlcInstance(env, thiz);

547
    /* Create a media player playing environment */
548
    libvlc_media_player_t *mp = libvlc_media_player_new(p_instance);
Rafaël Carré's avatar
Rafaël Carré committed
549
    libvlc_media_player_set_video_title_display(mp, libvlc_position_disable, 0);
550
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz); // freed in aout_close
551

552 553
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
554 555 556 557 558
    static const libvlc_event_type_t mp_events[] = {
        libvlc_MediaPlayerPlaying,
        libvlc_MediaPlayerPaused,
        libvlc_MediaPlayerEndReached,
        libvlc_MediaPlayerStopped,
559
        libvlc_MediaPlayerVout,
560
        libvlc_MediaPlayerPositionChanged,
561
        libvlc_MediaPlayerTimeChanged,
562 563 564
        libvlc_MediaPlayerEncounteredError,
        libvlc_MediaPlayerESAdded,
        libvlc_MediaPlayerESDeleted,
565
    };
566
    for(int i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); i++)
567 568
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

569
    /* Keep a pointer to this media player */
Ludovic Fauvet's avatar
Ludovic Fauvet committed
570
    setLong(env, thiz, "mInternalMediaPlayerInstance", (jlong)(intptr_t)mp);
571

572 573 574 575
    cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodID = (*env)->GetMethodID(env, cls, "applyEqualizer", "()V");
    (*env)->CallVoidMethod(env, thiz, methodID);

576 577
    const char* p_mrl = (*env)->GetStringUTFChars(env, mrl, 0);

578
    libvlc_media_t* p_md = libvlc_media_new_location(p_instance, p_mrl);
579
    /* media options */
580
    if (mediaOptions != NULL)
581
        add_media_options(p_md, env, mediaOptions);
582

583 584
    (*env)->ReleaseStringUTFChars(env, mrl, p_mrl);

585 586 587
    /* Connect the media event manager. */
    libvlc_event_manager_t *ev_media = libvlc_media_event_manager(p_md);
    static const libvlc_event_type_t mp_media_events[] = {
Alexandre Perraud's avatar
Alexandre Perraud committed
588 589
        libvlc_MediaParsedChanged,
        libvlc_MediaMetaChanged,
590 591 592 593
    };
    for(int i = 0; i < (sizeof(mp_media_events) / sizeof(*mp_media_events)); i++)
        libvlc_event_attach(ev_media, mp_media_events[i], vlc_event_callback, myVm);

594 595
    libvlc_media_player_set_media(mp, p_md);
    libvlc_media_player_play(mp);
596 597
}

598
jfloat Java_org_videolan_libvlc_LibVLC_getRate(JNIEnv *env, jobject thiz) {
Edward Wang's avatar
Edward Wang committed
599 600 601 602 603 604 605
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        return libvlc_media_player_get_rate(mp);
    else
        return 1.00;
}

606
void Java_org_videolan_libvlc_LibVLC_setRate(JNIEnv *env, jobject thiz, jfloat rate) {
Edward Wang's avatar
Edward Wang committed
607 608 609 610 611
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        libvlc_media_player_set_rate(mp, rate);
}

612
jboolean Java_org_videolan_libvlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
613
{
614
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
615
    if (mp)
616
        return !!libvlc_media_player_is_playing(mp);
617 618
    else
        return 0;
619 620
}

621
jboolean Java_org_videolan_libvlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
622
{
623 624 625
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return !!libvlc_media_player_is_seekable(mp);
626
    return 0;
627 628
}

629
void Java_org_videolan_libvlc_LibVLC_play(JNIEnv *env, jobject thiz)
630
{
631
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
632
    if (mp)
633
        libvlc_media_player_play(mp);
634 635
}

636
void Java_org_videolan_libvlc_LibVLC_pause(JNIEnv *env, jobject thiz)
637
{
638
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
639
    if (mp)
640
        libvlc_media_player_pause(mp);
641 642
}

643
void Java_org_videolan_libvlc_LibVLC_stop(JNIEnv *env, jobject thiz)
644
{
645
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
646
    if (mp)
647
        libvlc_media_player_stop(mp);
648 649
}

650 651 652 653 654 655 656 657
jint Java_org_videolan_libvlc_LibVLC_getPlayerState(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_media_player_get_state(mp);
    return -1;
}

658
jint Java_org_videolan_libvlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
659
{
660 661 662
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jint) libvlc_audio_get_volume(mp);
663 664 665
    return -1;
}

666
jint Java_org_videolan_libvlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
667
{
668 669
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
670
        //Returns 0 if the volume was set, -1 if it was out of range or error
671
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
672 673 674
    return -1;
}

675
jlong Java_org_videolan_libvlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
676
{
677 678 679
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
680 681 682
    return -1;
}

683
void Java_org_videolan_libvlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
684
{
685 686 687
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
688 689
}

690
jfloat Java_org_videolan_libvlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
691
{
692 693 694
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
695 696 697
    return -1;
}

698
void Java_org_videolan_libvlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
699
{
700 701 702
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
703 704
}

705
jlong Java_org_videolan_libvlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
706
{
707 708 709
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
710 711 712
    return -1;
}

713
jstring Java_org_videolan_libvlc_LibVLC_version(JNIEnv* env, jobject thiz)
714
{
715 716
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
717

718
jstring Java_org_videolan_libvlc_LibVLC_compiler(JNIEnv* env, jobject thiz)
719 720 721 722
{
    return (*env)->NewStringUTF(env, libvlc_get_compiler());
}

723
jstring Java_org_videolan_libvlc_LibVLC_changeset(JNIEnv* env, jobject thiz)
724 725 726
{
    return (*env)->NewStringUTF(env, libvlc_get_changeset());
}
727

728 729 730
jstring Java_org_videolan_libvlc_LibVLC_getMeta(JNIEnv *env, jobject thiz, int meta)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
731 732
    char *psz_meta;
    jstring string = NULL;
733 734 735 736 737 738 739
    if (!mp)
        return NULL;

    libvlc_media_t *p_mp = libvlc_media_player_get_media(mp);
    if (!p_mp)
        return NULL;

740 741 742 743 744
    psz_meta = libvlc_media_get_meta(p_mp, meta);
    if (psz_meta) {
        string = (*env)->NewStringUTF(env, psz_meta);
        free(psz_meta);
    }
745
    libvlc_media_release(p_mp);
746
    return string;
747 748
}

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
jint Java_org_videolan_libvlc_LibVLC_getTitle(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_title(mp);
    return -1;
}

void Java_org_videolan_libvlc_LibVLC_setTitle(JNIEnv *env, jobject thiz, jint title)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_title(mp, title);
}

764 765 766 767 768 769 770 771
jint Java_org_videolan_libvlc_LibVLC_getChapterCount(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_chapter_count(mp);
    return -1;
}

772 773 774 775 776 777 778
jint Java_org_videolan_libvlc_LibVLC_getChapterCountForTitle(JNIEnv *env, jobject thiz, jint title)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_chapter_count_for_title(mp, title);
    return -1;
}
Edward Wang's avatar
Edward Wang committed
779

780 781 782 783 784 785 786 787
jint Java_org_videolan_libvlc_LibVLC_getChapter(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_chapter(mp);
    return -1;
}

788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
jstring Java_org_videolan_libvlc_LibVLC_getChapterDescription(JNIEnv *env, jobject thiz, jint title)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    libvlc_track_description_t *description;
    jstring string = NULL;
    if (!mp)
        return NULL;
    description = libvlc_video_get_chapter_description(mp, title);
    if (description) {
        string = (*env)->NewStringUTF(env, description->psz_name);
        free(description);
    }
    return string;
}

803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
void Java_org_videolan_libvlc_LibVLC_setChapter(JNIEnv *env, jobject thiz, jint chapter)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_chapter(mp, chapter);
}

void Java_org_videolan_libvlc_LibVLC_previousChapter(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_previous_chapter(mp);
}

void Java_org_videolan_libvlc_LibVLC_nextChapter(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_next_chapter(mp);
}

Edward Wang's avatar
Edward Wang committed
824 825 826 827 828 829 830
jint Java_org_videolan_libvlc_LibVLC_getTitleCount(JNIEnv *env, jobject thiz)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_title_count(mp);
    return -1;
}
Geoffrey Métais's avatar
Geoffrey Métais committed
831 832 833 834 835 836 837 838 839

void Java_org_videolan_libvlc_LibVLC_playerNavigate(JNIEnv *env, jobject thiz, jint navigate)
{
    unsigned nav = navigate;
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_navigate(mp, (unsigned) nav);
}

840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
static int expand_media_internal(JNIEnv *env, libvlc_instance_t* p_instance, jobject arrayList, libvlc_media_t* p_md) {
    if(!p_md) {
        return -1;
    }
    libvlc_media_list_t* p_subitems = libvlc_media_subitems(p_md);
    libvlc_media_release(p_md);
    if(p_subitems) {
        // Expand any subitems if needed
        int subitem_count = libvlc_media_list_count(p_subitems);
        if(subitem_count > 0) {
            LOGD("Found %d subitems, expanding", subitem_count);
            jclass arrayListClass; jmethodID methodAdd;
            arrayListGetIDs(env, &arrayListClass, &methodAdd, NULL);

            for(int i = subitem_count - 1; i >= 0; i--) {
                libvlc_media_t* p_subitem = libvlc_media_list_item_at_index(p_subitems, i);
                char* p_subitem_uri = libvlc_media_get_mrl(p_subitem);
                arrayListStringAdd(env, arrayListClass, methodAdd, arrayList, p_subitem_uri);
                free(p_subitem_uri);
            }
        }
        libvlc_media_list_release(p_subitems);
        if(subitem_count > 0) {
            return 0;
        } else {
            return -1;
        }
    } else {
        return -1;
    }
}

jint Java_org_videolan_libvlc_LibVLC_expandMedia(JNIEnv *env, jobject thiz, jobject children) {
873 874 875 876 877 878
    jint ret;
    libvlc_media_t *p_md = libvlc_media_player_get_media(getMediaPlayer(env, thiz));

    if (!p_md)
        return -1;
    ret = (jint)expand_media_internal(env,
879 880
        getLibVlcInstance(env, thiz),
        children,
881 882 883
        p_md);
    libvlc_media_release(p_md);
    return ret;
884 885
}

Thomas Guillem's avatar
Thomas Guillem committed
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
// TODO: remove static variables
static int i_window_width = 0;
static int i_window_height = 0;

void Java_org_videolan_libvlc_LibVLC_setWindowSize(JNIEnv *env, jobject thiz, jint width, jint height)
{
    pthread_mutex_lock(&vout_android_lock);
    i_window_width = width;
    i_window_height = height;
    pthread_mutex_unlock(&vout_android_lock);
}

int jni_GetWindowSize(int *width, int *height)
{
    pthread_mutex_lock(&vout_android_lock);
    *width = i_window_width;
    *height = i_window_height;
    pthread_mutex_unlock(&vout_android_lock);
    return 0;
}
906 907 908 909 910 911 912 913 914 915 916 917

/* used by opensles module */
int aout_get_native_sample_rate(void)
{
    JNIEnv *p_env;
    jni_attach_thread (&p_env, THREAD_NAME);
    jclass cls = (*p_env)->FindClass (p_env, "android/media/AudioTrack");
    jmethodID method = (*p_env)->GetStaticMethodID (p_env, cls, "getNativeOutputSampleRate", "(I)I");
    int sample_rate = (*p_env)->CallStaticIntMethod (p_env, cls, method, 3); // AudioManager.STREAM_MUSIC
    jni_detach_thread ();
    return sample_rate;
}