1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "JET_JNI"
19
20
21#include <stdio.h>
22#include <unistd.h>
23#include <fcntl.h>
24
25#include <jni.h>
26#include <JNIHelp.h>
27#include "core_jni_helpers.h"
28
29#include <utils/Log.h>
30#include <media/JetPlayer.h>
31
32
33using namespace android;
34
35// ----------------------------------------------------------------------------
36static const char* const kClassPathName = "android/media/JetPlayer";
37
38// ----------------------------------------------------------------------------
39struct fields_t {
40    // these fields provide access from C++ to the...
41    jclass    jetClass;              // JetPlayer java class global ref
42    jmethodID postNativeEventInJava; // java method to post events to the Java thread from native
43    jfieldID  nativePlayerInJavaObj; // stores in Java the native JetPlayer object
44};
45
46static fields_t javaJetPlayerFields;
47
48
49// ----------------------------------------------------------------------------
50// ----------------------------------------------------------------------------
51
52/*
53 * This function is called from JetPlayer instance's render thread
54 */
55static void
56jetPlayerEventCallback(int what, int arg1=0, int arg2=0, void* javaTarget = NULL)
57{
58    JNIEnv *env = AndroidRuntime::getJNIEnv();
59    if (env) {
60        env->CallStaticVoidMethod(
61            javaJetPlayerFields.jetClass, javaJetPlayerFields.postNativeEventInJava,
62            javaTarget,
63            what, arg1, arg2);
64        if (env->ExceptionCheck()) {
65            env->ExceptionDescribe();
66            env->ExceptionClear();
67        }
68    } else {
69        ALOGE("JET jetPlayerEventCallback(): No JNI env for JET event callback, can't post event.");
70        return;
71    }
72}
73
74
75// ----------------------------------------------------------------------------
76// ----------------------------------------------------------------------------
77
78static jboolean
79android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this,
80    jint maxTracks, jint trackBufferSize)
81{
82    //ALOGV("android_media_JetPlayer_setup(): entering.");
83    JetPlayer* lpJet = new JetPlayer(env->NewGlobalRef(weak_this), maxTracks, trackBufferSize);
84
85    EAS_RESULT result = lpJet->init();
86
87    if (result==EAS_SUCCESS) {
88        // save our newly created C++ JetPlayer in the "nativePlayerInJavaObj" field
89        // of the Java object (in mNativePlayerInJavaObj)
90        env->SetLongField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, (jlong)lpJet);
91        return JNI_TRUE;
92    } else {
93        ALOGE("android_media_JetPlayer_setup(): initialization failed with EAS error code %d", (int)result);
94        delete lpJet;
95        env->SetLongField(weak_this, javaJetPlayerFields.nativePlayerInJavaObj, 0);
96        return JNI_FALSE;
97    }
98}
99
100
101// ----------------------------------------------------------------------------
102static void
103android_media_JetPlayer_finalize(JNIEnv *env, jobject thiz)
104{
105    ALOGV("android_media_JetPlayer_finalize(): entering.");
106    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
107        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
108    if (lpJet != NULL) {
109        lpJet->release();
110        delete lpJet;
111    }
112
113    ALOGV("android_media_JetPlayer_finalize(): exiting.");
114}
115
116
117// ----------------------------------------------------------------------------
118static void
119android_media_JetPlayer_release(JNIEnv *env, jobject thiz)
120{
121    android_media_JetPlayer_finalize(env, thiz);
122    env->SetLongField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, 0);
123    ALOGV("android_media_JetPlayer_release() done");
124}
125
126
127// ----------------------------------------------------------------------------
128static jboolean
129android_media_JetPlayer_loadFromFile(JNIEnv *env, jobject thiz, jstring path)
130{
131    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
132        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
133    if (lpJet == NULL) {
134        jniThrowException(env, "java/lang/IllegalStateException",
135            "Unable to retrieve JetPlayer pointer for openFile()");
136        return JNI_FALSE;
137    }
138
139    // set up event callback function
140    lpJet->setEventCallback(jetPlayerEventCallback);
141
142    const char *pathStr = env->GetStringUTFChars(path, NULL);
143    if (pathStr == NULL) {  // Out of memory
144        ALOGE("android_media_JetPlayer_openFile(): aborting, out of memory");
145        return JNI_FALSE;
146    }
147
148    ALOGV("android_media_JetPlayer_openFile(): trying to open %s", pathStr );
149    EAS_RESULT result = lpJet->loadFromFile(pathStr);
150    env->ReleaseStringUTFChars(path, pathStr);
151
152    if (result==EAS_SUCCESS) {
153        //ALOGV("android_media_JetPlayer_openFile(): file successfully opened");
154        return JNI_TRUE;
155    } else {
156        ALOGE("android_media_JetPlayer_openFile(): failed to open file with EAS error %d",
157            (int)result);
158        return JNI_FALSE;
159    }
160}
161
162
163// ----------------------------------------------------------------------------
164static jboolean
165android_media_JetPlayer_loadFromFileD(JNIEnv *env, jobject thiz,
166    jobject fileDescriptor, jlong offset, jlong length)
167{
168    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
169        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
170    if (lpJet == NULL) {
171        jniThrowException(env, "java/lang/IllegalStateException",
172            "Unable to retrieve JetPlayer pointer for openFile()");
173        return JNI_FALSE;
174    }
175
176    // set up event callback function
177    lpJet->setEventCallback(jetPlayerEventCallback);
178
179    ALOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" );
180    EAS_RESULT result = lpJet->loadFromFD(jniGetFDFromFileDescriptor(env, fileDescriptor),
181        (long long)offset, (long long)length); // cast params to types used by EAS_FILE
182
183    if (result==EAS_SUCCESS) {
184        ALOGV("android_media_JetPlayer_openFileDescr(): file successfully opened");
185        return JNI_TRUE;
186    } else {
187        ALOGE("android_media_JetPlayer_openFileDescr(): failed to open file with EAS error %d",
188            (int)result);
189        return JNI_FALSE;
190    }
191}
192
193
194// ----------------------------------------------------------------------------
195static jboolean
196android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz)
197{
198    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
199        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
200    if (lpJet == NULL) {
201        jniThrowException(env, "java/lang/IllegalStateException",
202            "Unable to retrieve JetPlayer pointer for closeFile()");
203        return JNI_FALSE;
204    }
205
206    if (lpJet->closeFile()==EAS_SUCCESS) {
207        //ALOGV("android_media_JetPlayer_closeFile(): file successfully closed");
208        return JNI_TRUE;
209    } else {
210        ALOGE("android_media_JetPlayer_closeFile(): failed to close file");
211        return JNI_FALSE;
212    }
213}
214
215
216// ----------------------------------------------------------------------------
217static jboolean
218android_media_JetPlayer_play(JNIEnv *env, jobject thiz)
219{
220    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
221        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
222    if (lpJet == NULL) {
223        jniThrowException(env, "java/lang/IllegalStateException",
224            "Unable to retrieve JetPlayer pointer for play()");
225        return JNI_FALSE;
226    }
227
228    EAS_RESULT result = lpJet->play();
229    if (result==EAS_SUCCESS) {
230        //ALOGV("android_media_JetPlayer_play(): play successful");
231        return JNI_TRUE;
232    } else {
233        ALOGE("android_media_JetPlayer_play(): failed to play with EAS error code %ld",
234            result);
235        return JNI_FALSE;
236    }
237}
238
239
240// ----------------------------------------------------------------------------
241static jboolean
242android_media_JetPlayer_pause(JNIEnv *env, jobject thiz)
243{
244    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
245        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
246    if (lpJet == NULL) {
247        jniThrowException(env, "java/lang/IllegalStateException",
248            "Unable to retrieve JetPlayer pointer for pause()");
249        return JNI_FALSE;
250    }
251
252    EAS_RESULT result = lpJet->pause();
253    if (result==EAS_SUCCESS) {
254        //ALOGV("android_media_JetPlayer_pause(): pause successful");
255        return JNI_TRUE;
256    } else {
257        if (result==EAS_ERROR_QUEUE_IS_EMPTY) {
258            ALOGV("android_media_JetPlayer_pause(): paused with an empty queue");
259            return JNI_TRUE;
260        } else
261            ALOGE("android_media_JetPlayer_pause(): failed to pause with EAS error code %ld",
262                result);
263        return JNI_FALSE;
264    }
265}
266
267
268// ----------------------------------------------------------------------------
269static jboolean
270android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz,
271        jint segmentNum, jint libNum, jint repeatCount, jint transpose, jint muteFlags,
272        jbyte userID)
273{
274    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
275        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
276    if (lpJet == NULL) {
277        jniThrowException(env, "java/lang/IllegalStateException",
278            "Unable to retrieve JetPlayer pointer for queueSegment()");
279        return JNI_FALSE;
280    }
281
282    EAS_RESULT result
283        = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
284    if (result==EAS_SUCCESS) {
285        //ALOGV("android_media_JetPlayer_queueSegment(): segment successfully queued");
286        return JNI_TRUE;
287    } else {
288        ALOGE("android_media_JetPlayer_queueSegment(): failed with EAS error code %ld",
289            result);
290        return JNI_FALSE;
291    }
292}
293
294
295// ----------------------------------------------------------------------------
296static jboolean
297android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz,
298        jint segmentNum, jint libNum, jint repeatCount, jint transpose, jbooleanArray muteArray,
299        jbyte userID)
300{
301    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
302        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
303    if (lpJet == NULL) {
304        jniThrowException(env, "java/lang/IllegalStateException",
305            "Unable to retrieve JetPlayer pointer for queueSegmentMuteArray()");
306        return JNI_FALSE;
307    }
308
309    EAS_RESULT result=EAS_FAILURE;
310
311    jboolean *muteTracks = NULL;
312    muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
313    if (muteTracks == NULL) {
314        ALOGE("android_media_JetPlayer_queueSegment(): failed to read track mute mask.");
315        return JNI_FALSE;
316    }
317
318    EAS_U32 muteMask=0;
319    int maxTracks = lpJet->getMaxTracks();
320    for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
321        if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
322            muteMask = (muteMask << 1) | 0x00000001;
323        else
324            muteMask = muteMask << 1;
325    }
326    //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): FINAL mute mask =0x%08lX", mask);
327
328    result = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteMask, userID);
329
330    env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
331    if (result==EAS_SUCCESS) {
332        //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): segment successfully queued");
333        return JNI_TRUE;
334    } else {
335        ALOGE("android_media_JetPlayer_queueSegmentMuteArray(): failed with EAS error code %ld",
336            result);
337        return JNI_FALSE;
338    }
339}
340
341
342// ----------------------------------------------------------------------------
343static jboolean
344android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz,
345         jint muteFlags /*unsigned?*/, jboolean bSync)
346{
347    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
348        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
349    if (lpJet == NULL) {
350        jniThrowException(env, "java/lang/IllegalStateException",
351            "Unable to retrieve JetPlayer pointer for setMuteFlags()");
352        return JNI_FALSE;
353    }
354
355    EAS_RESULT result;
356    result = lpJet->setMuteFlags(muteFlags, bSync==JNI_TRUE ? true : false);
357    if (result==EAS_SUCCESS) {
358        //ALOGV("android_media_JetPlayer_setMuteFlags(): mute flags successfully updated");
359        return JNI_TRUE;
360    } else {
361        ALOGE("android_media_JetPlayer_setMuteFlags(): failed with EAS error code %ld", result);
362        return JNI_FALSE;
363    }
364}
365
366
367// ----------------------------------------------------------------------------
368static jboolean
369android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz,
370        jbooleanArray muteArray, jboolean bSync)
371{
372    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
373        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
374    if (lpJet == NULL) {
375        jniThrowException(env, "java/lang/IllegalStateException",
376            "Unable to retrieve JetPlayer pointer for setMuteArray()");
377        return JNI_FALSE;
378    }
379
380    EAS_RESULT result=EAS_FAILURE;
381
382    jboolean *muteTracks = NULL;
383    muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
384    if (muteTracks == NULL) {
385        ALOGE("android_media_JetPlayer_setMuteArray(): failed to read track mute mask.");
386        return JNI_FALSE;
387    }
388
389    EAS_U32 muteMask=0;
390    int maxTracks = lpJet->getMaxTracks();
391    for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
392        if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
393            muteMask = (muteMask << 1) | 0x00000001;
394        else
395            muteMask = muteMask << 1;
396    }
397    //ALOGV("android_media_JetPlayer_setMuteArray(): FINAL mute mask =0x%08lX", muteMask);
398
399    result = lpJet->setMuteFlags(muteMask, bSync==JNI_TRUE ? true : false);
400
401    env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
402    if (result==EAS_SUCCESS) {
403        //ALOGV("android_media_JetPlayer_setMuteArray(): mute flags successfully updated");
404        return JNI_TRUE;
405    } else {
406        ALOGE("android_media_JetPlayer_setMuteArray(): \
407            failed to update mute flags with EAS error code %ld", result);
408        return JNI_FALSE;
409    }
410}
411
412
413// ----------------------------------------------------------------------------
414static jboolean
415android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz,
416         jint trackId, jboolean muteFlag, jboolean bSync)
417{
418    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
419        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
420    if (lpJet == NULL) {
421        jniThrowException(env, "java/lang/IllegalStateException",
422            "Unable to retrieve JetPlayer pointer for setMuteFlag()");
423        return JNI_FALSE;
424    }
425
426    EAS_RESULT result;
427    result = lpJet->setMuteFlag(trackId,
428        muteFlag==JNI_TRUE ? true : false, bSync==JNI_TRUE ? true : false);
429    if (result==EAS_SUCCESS) {
430        //ALOGV("android_media_JetPlayer_setMuteFlag(): mute flag successfully updated for track %d", trackId);
431        return JNI_TRUE;
432    } else {
433        ALOGE("android_media_JetPlayer_setMuteFlag(): failed to update mute flag for track %d with EAS error code %ld",
434                trackId, result);
435        return JNI_FALSE;
436    }
437}
438
439
440// ----------------------------------------------------------------------------
441static jboolean
442android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId)
443{
444    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
445        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
446    if (lpJet == NULL) {
447        jniThrowException(env, "java/lang/IllegalStateException",
448            "Unable to retrieve JetPlayer pointer for triggerClip()");
449        return JNI_FALSE;
450    }
451
452    EAS_RESULT result;
453    result = lpJet->triggerClip(clipId);
454    if (result==EAS_SUCCESS) {
455        //ALOGV("android_media_JetPlayer_triggerClip(): triggerClip successful for clip %d", clipId);
456        return JNI_TRUE;
457    } else {
458        ALOGE("android_media_JetPlayer_triggerClip(): triggerClip for clip %d failed with EAS error code %ld",
459                clipId, result);
460        return JNI_FALSE;
461    }
462}
463
464
465// ----------------------------------------------------------------------------
466static jboolean
467android_media_JetPlayer_clearQueue(JNIEnv *env, jobject thiz)
468{
469    JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
470        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
471    if (lpJet == NULL) {
472        jniThrowException(env, "java/lang/IllegalStateException",
473            "Unable to retrieve JetPlayer pointer for clearQueue()");
474        return JNI_FALSE;
475    }
476
477    EAS_RESULT result = lpJet->clearQueue();
478    if (result==EAS_SUCCESS) {
479        //ALOGV("android_media_JetPlayer_clearQueue(): clearQueue successful");
480        return JNI_TRUE;
481    } else {
482        ALOGE("android_media_JetPlayer_clearQueue(): clearQueue failed with EAS error code %ld",
483                result);
484        return JNI_FALSE;
485    }
486}
487
488
489// ----------------------------------------------------------------------------
490// ----------------------------------------------------------------------------
491static const JNINativeMethod gMethods[] = {
492    // name,               signature,               funcPtr
493    {"native_setup",       "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
494    {"native_finalize",    "()V",                   (void *)android_media_JetPlayer_finalize},
495    {"native_release",     "()V",                   (void *)android_media_JetPlayer_release},
496    {"native_loadJetFromFile",
497                           "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_loadFromFile},
498    {"native_loadJetFromFileD", "(Ljava/io/FileDescriptor;JJ)Z",
499                                                    (void *)android_media_JetPlayer_loadFromFileD},
500    {"native_closeJetFile","()Z",                   (void *)android_media_JetPlayer_closeFile},
501    {"native_playJet",     "()Z",                   (void *)android_media_JetPlayer_play},
502    {"native_pauseJet",    "()Z",                   (void *)android_media_JetPlayer_pause},
503    {"native_queueJetSegment",
504                           "(IIIIIB)Z",             (void *)android_media_JetPlayer_queueSegment},
505    {"native_queueJetSegmentMuteArray",
506                           "(IIII[ZB)Z",     (void *)android_media_JetPlayer_queueSegmentMuteArray},
507    {"native_setMuteFlags","(IZ)Z",                 (void *)android_media_JetPlayer_setMuteFlags},
508    {"native_setMuteArray","([ZZ)Z",                (void *)android_media_JetPlayer_setMuteArray},
509    {"native_setMuteFlag", "(IZZ)Z",                (void *)android_media_JetPlayer_setMuteFlag},
510    {"native_triggerClip", "(I)Z",                  (void *)android_media_JetPlayer_triggerClip},
511    {"native_clearQueue",  "()Z",                   (void *)android_media_JetPlayer_clearQueue},
512};
513
514#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
515#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
516
517
518int register_android_media_JetPlayer(JNIEnv *env)
519{
520    javaJetPlayerFields.jetClass = NULL;
521    javaJetPlayerFields.postNativeEventInJava = NULL;
522    javaJetPlayerFields.nativePlayerInJavaObj = NULL;
523
524    // Get the JetPlayer java class
525    jclass jetPlayerClass = FindClassOrDie(env, kClassPathName);
526    javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass);
527
528    // Get the mNativePlayerInJavaObj variable field
529    javaJetPlayerFields.nativePlayerInJavaObj = GetFieldIDOrDie(env,
530            jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J");
531
532    // Get the callback to post events from this native code to Java
533    javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
534            javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME,
535            "(Ljava/lang/Object;III)V");
536
537    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
538}
539