libvlcjni.c 28.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 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
    /* Get the object class */
196
    jclass cls = (*env)->GetObjectClass(env, eventHandlerInstance);
197
    if (!cls) {
198
        LOGE("EventHandler: failed to get class reference");
199
        goto end;
200 201 202
    }

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

210
end:
Edward Wang's avatar
Edward Wang committed
211
    (*env)->DeleteLocalRef(env, bundle);
212
    if (isAttached)
213
        jni_detach_thread();
214 215
}

216
jint JNI_OnLoad(JavaVM *vm, void *reserved)
217
{
Thomas Guillem's avatar
Thomas Guillem committed
218
    JNIEnv* env = NULL;
219 220 221
    // Keep a reference on the Java VM.
    myVm = vm;

Thomas Guillem's avatar
Thomas Guillem committed
222 223
    if ((*vm)->GetEnv(vm, (void**) &env, VLC_JNI_VERSION) != JNI_OK)
        return -1;
224
    pthread_mutex_init(&vout_android_lock, NULL);
225
    pthread_cond_init(&vout_android_surf_attached, NULL);
226

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

273 274 275 276 277
    GET_ID(GetStaticMethodID,
           fields.LibVLC.onNativeCrashID,
           fields.LibVLC.clazz,
           "onNativeCrash", "()V");

Thomas Guillem's avatar
Thomas Guillem committed
278 279 280 281 282 283 284 285 286 287
    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
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
    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;
    }

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
    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
326 327 328
#undef GET_CLASS
#undef GET_ID

329 330
    init_native_crash_handler();

331
    LOGD("JNI interface loaded.");
332
    return VLC_JNI_VERSION;
333 334
}

Thomas Guillem's avatar
Thomas Guillem committed
335 336 337 338
void JNI_OnUnload(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;

339
    pthread_mutex_destroy(&vout_android_lock);
340
    pthread_cond_destroy(&vout_android_surf_attached);
Thomas Guillem's avatar
Thomas Guillem committed
341

342 343
    destroy_native_crash_handler();

Thomas Guillem's avatar
Thomas Guillem committed
344 345 346 347 348 349 350
    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);
351
    (*env)->DeleteGlobalRef(env, fields.Media.clazz);
352 353
}

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

377
void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
378
{
379 380 381
    //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
382
    int aout = (*env)->CallIntMethod(env, thiz, methodId);
383

384
    methodId = (*env)->GetMethodID(env, cls, "getVout", "()I");
385
    int vout = (*env)->CallIntMethod(env, thiz, methodId);
386

387 388 389
    methodId = (*env)->GetMethodID(env, cls, "timeStretchingEnabled", "()Z");
    bool enable_time_stretch = (*env)->CallBooleanMethod(env, thiz, methodId);

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

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

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

407 408 409
    methodId = (*env)->GetMethodID(env, cls, "getHttpReconnect", "()Z");
    bool enable_http_reconnect = (*env)->CallBooleanMethod(env, thiz, methodId);

410 411 412 413
    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
414

415 416 417
    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
418
    LOGD("Subtitle encoding set to \"%s\"", subsencodingstr);
419

420
    methodId = (*env)->GetMethodID(env, cls, "isVerboseMode", "()Z");
421
    bool b_verbose = (*env)->CallBooleanMethod(env, thiz, methodId);
422

423 424
    methodId = (*env)->GetMethodID(env, cls, "isDirectRendering", "()Z");
    bool direct_rendering = (*env)->CallBooleanMethod(env, thiz, methodId);
425
    /* With the MediaCodec opaque mode we cannot use the OpenGL ES vout. */
426
    if (direct_rendering)
427
        vout = VOUT_ANDROID_WINDOW;
428

flx42's avatar
flx42 committed
429 430
    methodId = (*env)->GetMethodID(env, cls, "getCachePath", "()Ljava/lang/String;");
    jstring cachePath = (*env)->CallObjectMethod(env, thiz, methodId);
431 432 433 434 435
    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
436

437
#define MAX_ARGV 20
438 439 440 441 442 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
    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
    }
476
    argv[argc++] = "--spdif";
477 478
    argv[argc++] = b_verbose ? "-vvv" : "-vv";

479 480 481 482 483 484
    /* 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);
485

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

488
    (*env)->ReleaseStringUTFChars(env, chroma, chromastr);
489 490
    (*env)->ReleaseStringUTFChars(env, subsencoding, subsencodingstr);

491 492
    if (!instance)
    {
493
        jclass exc = (*env)->FindClass(env, "org/videolan/libvlc/LibVlcException");
494 495 496 497
        (*env)->ThrowNew(env, exc, "Unable to instantiate LibVLC");
    }

    LOGI("LibVLC initialized: %p", instance);
498 499
}

500
void Java_org_videolan_libvlc_LibVLC_nativeDestroy(JNIEnv *env, jobject thiz)
501
{
502
    releaseMediaPlayer(env, thiz);
Edward Wang's avatar
Edward Wang committed
503
    jlong libVlcInstance = getLong(env, thiz, "mLibVlcInstance");
504 505 506
    if (!libVlcInstance)
        return; // Already destroyed

Ludovic Fauvet's avatar
Ludovic Fauvet committed
507
    libvlc_instance_t *instance = (libvlc_instance_t*)(intptr_t) libVlcInstance;
508 509
    libvlc_release(instance);

Edward Wang's avatar
Edward Wang committed
510
    setLong(env, thiz, "mLibVlcInstance", 0);
511 512
}

513
void Java_org_videolan_libvlc_LibVLC_detachEventHandler(JNIEnv *env, jobject thiz)
514
{
515 516 517
    if (eventHandlerInstance != NULL) {
        (*env)->DeleteGlobalRef(env, eventHandlerInstance);
        eventHandlerInstance = NULL;
518 519 520
    }
}

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

528
    eventHandlerInstance = getEventHandlerReference(env, thiz, eventHandler);
529 530
}

531
void Java_org_videolan_libvlc_LibVLC_playMRL(JNIEnv *env, jobject thiz,
532 533
                                             jstring mrl, jobjectArray mediaOptions)
{
534
    jclass cls;
535 536 537
    /* Release previous media player, if any */
    releaseMediaPlayer(env, thiz);

538 539
    libvlc_instance_t *p_instance = getLibVlcInstance(env, thiz);

540
    /* Create a media player playing environment */
541
    libvlc_media_player_t *mp = libvlc_media_player_new(p_instance);
Rafaël Carré's avatar
Rafaël Carré committed
542
    libvlc_media_player_set_video_title_display(mp, libvlc_position_disable, 0);
543
    jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz); // freed in aout_close
544

545 546
    /* Connect the event manager */
    libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
547 548 549 550 551
    static const libvlc_event_type_t mp_events[] = {
        libvlc_MediaPlayerPlaying,
        libvlc_MediaPlayerPaused,
        libvlc_MediaPlayerEndReached,
        libvlc_MediaPlayerStopped,
552
        libvlc_MediaPlayerVout,
553
        libvlc_MediaPlayerPositionChanged,
554
        libvlc_MediaPlayerTimeChanged,
555
        libvlc_MediaPlayerEncounteredError
556
    };
557
    for(int i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); i++)
558 559
        libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

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

563 564 565 566
    cls = (*env)->GetObjectClass(env, thiz);
    jmethodID methodID = (*env)->GetMethodID(env, cls, "applyEqualizer", "()V");
    (*env)->CallVoidMethod(env, thiz, methodID);

567 568
    const char* p_mrl = (*env)->GetStringUTFChars(env, mrl, 0);

569
    libvlc_media_t* p_md = libvlc_media_new_location(p_instance, p_mrl);
570
    /* media options */
571
    if (mediaOptions != NULL)
572
        add_media_options(p_md, env, mediaOptions);
573

574 575
    (*env)->ReleaseStringUTFChars(env, mrl, p_mrl);

576 577 578
    /* 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
579 580
        libvlc_MediaParsedChanged,
        libvlc_MediaMetaChanged,
581 582 583 584
    };
    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);

585 586
    libvlc_media_player_set_media(mp, p_md);
    libvlc_media_player_play(mp);
587 588
}

589
jfloat Java_org_videolan_libvlc_LibVLC_getRate(JNIEnv *env, jobject thiz) {
Edward Wang's avatar
Edward Wang committed
590 591 592 593 594 595 596
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        return libvlc_media_player_get_rate(mp);
    else
        return 1.00;
}

597
void Java_org_videolan_libvlc_LibVLC_setRate(JNIEnv *env, jobject thiz, jfloat rate) {
Edward Wang's avatar
Edward Wang committed
598 599 600 601 602
    libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    if(mp)
        libvlc_media_player_set_rate(mp, rate);
}

603
jboolean Java_org_videolan_libvlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
604
{
605
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
606
    if (mp)
607
        return !!libvlc_media_player_is_playing(mp);
608 609
    else
        return 0;
610 611
}

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

620
void Java_org_videolan_libvlc_LibVLC_play(JNIEnv *env, jobject thiz)
621
{
622
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
623
    if (mp)
624
        libvlc_media_player_play(mp);
625 626
}

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

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

641 642 643 644 645 646 647 648
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;
}

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

657
jint Java_org_videolan_libvlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
658
{
659 660
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
661
        //Returns 0 if the volume was set, -1 if it was out of range or error
662
        return (jint) libvlc_audio_set_volume(mp, (int) volume);
663 664 665
    return -1;
}

666
jlong Java_org_videolan_libvlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
667
{
668 669 670
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return libvlc_media_player_get_time(mp);
671 672 673
    return -1;
}

674
void Java_org_videolan_libvlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
675
{
676 677 678
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_time(mp, time);
679 680
}

681
jfloat Java_org_videolan_libvlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
682
{
683 684 685
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jfloat) libvlc_media_player_get_position(mp);
686 687 688
    return -1;
}

689
void Java_org_videolan_libvlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
690
{
691 692 693
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        libvlc_media_player_set_position(mp, pos);
694 695
}

696
jlong Java_org_videolan_libvlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
697
{
698 699 700
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    if (mp)
        return (jlong) libvlc_media_player_get_length(mp);
701 702 703
    return -1;
}

704
jstring Java_org_videolan_libvlc_LibVLC_version(JNIEnv* env, jobject thiz)
705
{
706 707
    return (*env)->NewStringUTF(env, libvlc_get_version());
}
708

709
jstring Java_org_videolan_libvlc_LibVLC_compiler(JNIEnv* env, jobject thiz)
710 711 712 713
{
    return (*env)->NewStringUTF(env, libvlc_get_compiler());
}

714
jstring Java_org_videolan_libvlc_LibVLC_changeset(JNIEnv* env, jobject thiz)
715 716 717
{
    return (*env)->NewStringUTF(env, libvlc_get_changeset());
}
718

719 720 721
jstring Java_org_videolan_libvlc_LibVLC_getMeta(JNIEnv *env, jobject thiz, int meta)
{
    libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
722 723
    char *psz_meta;
    jstring string = NULL;
724 725 726 727 728 729 730
    if (!mp)
        return NULL;

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

731 732 733 734 735
    psz_meta = libvlc_media_get_meta(p_mp, meta);
    if (psz_meta) {
        string = (*env)->NewStringUTF(env, psz_meta);
        free(psz_meta);
    }
736
    libvlc_media_release(p_mp);
737
    return string;
738 739
}

740 741 742 743 744 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);
}

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

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

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

779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
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) {
812 813 814 815 816 817
    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,
818 819
        getLibVlcInstance(env, thiz),
        children,
820 821 822
        p_md);
    libvlc_media_release(p_md);
    return ret;
823 824
}

Thomas Guillem's avatar
Thomas Guillem committed
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
// 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;
}
845 846 847 848 849 850 851 852 853 854 855 856

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