libvlcjni.c 30.8 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 156
    jmethodID putString = (*env)->GetMethodID(env, clsBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V" );

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

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

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

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

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

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

Thomas Guillem's avatar
Thomas Guillem committed
232
#define GET_CLASS(clazz, str, b_globlal) do { \
Thomas Guillem's avatar
Thomas Guillem committed
233 234 235 236 237
    (clazz) = (*env)->FindClass(env, (str)); \
    if (!(clazz)) { \
        LOGE("FindClass(%s) failed", (str)); \
        return -1; \
    } \
Thomas Guillem's avatar
Thomas Guillem committed
238 239 240 241 242 243
    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
244 245 246 247 248 249 250 251 252 253 254
    } \
} 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
255 256 257 258 259 260 261 262
    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
263
    GET_CLASS(fields.IllegalStateException.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
264
              "java/lang/IllegalStateException", true);
Thomas Guillem's avatar
Thomas Guillem committed
265
    GET_CLASS(fields.IllegalArgumentException.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
266
              "java/lang/IllegalArgumentException", true);
Thomas Guillem's avatar
Thomas Guillem committed
267
    GET_CLASS(fields.String.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
268
              "java/lang/String", true);
269
    GET_CLASS(fields.LibVLC.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
270
              "org/videolan/libvlc/LibVLC", true);
Thomas Guillem's avatar
Thomas Guillem committed
271
    GET_CLASS(fields.VLCObject.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
272
              "org/videolan/libvlc/VLCObject", true);
273
    GET_CLASS(fields.Media.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
274
              "org/videolan/libvlc/Media", true);
275
    GET_CLASS(fields.Media.Track.clazz,
Thomas Guillem's avatar
Thomas Guillem committed
276
              "org/videolan/libvlc/Media$Track", true);
Thomas Guillem's avatar
Thomas Guillem committed
277

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

Thomas Guillem's avatar
Thomas Guillem committed
283 284 285 286 287 288 289 290 291 292
    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
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
    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;
    }

310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
    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
331 332 333
#undef GET_CLASS
#undef GET_ID

334 335
    init_native_crash_handler();

336
    LOGD("JNI interface loaded.");
337
    return VLC_JNI_VERSION;
338 339
}

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

344
    pthread_mutex_destroy(&vout_android_lock);
345
    pthread_cond_destroy(&vout_android_surf_attached);
Thomas Guillem's avatar
Thomas Guillem committed
346

347 348
    destroy_native_crash_handler();

Thomas Guillem's avatar
Thomas Guillem committed
349 350 351 352 353 354 355
    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);
356
    (*env)->DeleteGlobalRef(env, fields.Media.clazz);
357 358
}

359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
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)
{
379
    return (*myVm)->GetEnv(myVm, (void **)env, VLC_JNI_VERSION) == JNI_OK ? 0 : -1;
380 381
}

382
void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
383
{
384 385 386
    //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
387
    int aout = (*env)->CallIntMethod(env, thiz, methodId);
388

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

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

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

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

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

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

415 416 417 418
    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
419

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

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

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

flx42's avatar
flx42 committed
434 435
    methodId = (*env)->GetMethodID(env, cls, "getCachePath", "()Ljava/lang/String;");
    jstring cachePath = (*env)->CallObjectMethod(env, thiz, methodId);
436 437 438 439 440
    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
441

442
#define MAX_ARGV 20
443 444 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
    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
    }
481
    argv[argc++] = "--spdif";
482 483
    argv[argc++] = b_verbose ? "-vvv" : "-vv";

484 485 486 487 488 489
    /* 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);
490

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

493
    (*env)->ReleaseStringUTFChars(env, chroma, chromastr);
494 495
    (*env)->ReleaseStringUTFChars(env, subsencoding, subsencodingstr);

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

    LOGI("LibVLC initialized: %p", instance);
503 504
}

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

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

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

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

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

533
    eventHandlerInstance = getEventHandlerReference(env, thiz, eventHandler);
534 535
}

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

543 544
    libvlc_instance_t *p_instance = getLibVlcInstance(env, thiz);

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

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

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

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

574 575
    const char* p_mrl = (*env)->GetStringUTFChars(env, mrl, 0);

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

581 582
    (*env)->ReleaseStringUTFChars(env, mrl, p_mrl);

583 584 585
    /* 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
586 587
        libvlc_MediaParsedChanged,
        libvlc_MediaMetaChanged,
588 589 590 591
    };
    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);

592 593
    libvlc_media_player_set_media(mp, p_md);
    libvlc_media_player_play(mp);
594 595
}

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

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

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

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

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

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

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

648 649 650 651 652 653 654 655
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;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
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);
}

762 763 764 765 766 767 768 769
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;
}

770 771 772 773 774 775 776
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
777

778 779 780 781 782 783 784 785
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;
}

786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
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;
}

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
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
822 823 824 825 826 827 828
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
829 830 831 832 833 834 835 836 837

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

838 839 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
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) {
871 872 873 874 875 876
    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,
877 878
        getLibVlcInstance(env, thiz),
        children,
879 880 881
        p_md);
    libvlc_media_release(p_md);
    return ret;
882 883
}

Thomas Guillem's avatar
Thomas Guillem committed
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
// 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;
}
904 905 906 907 908 909 910 911 912 913 914 915

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