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 <android_runtime/AndroidRuntime.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->SetIntField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, (int)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->SetIntField(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->GetIntField(
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->SetIntField(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->GetIntField(
132        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
133    if (lpJet == NULL ) {
134        jniThrowException(env, "java/lang/IllegalStateException",
135            "Unable to retrieve JetPlayer pointer for openFile()");
136    }
137
138    // set up event callback function
139    lpJet->setEventCallback(jetPlayerEventCallback);
140
141    const char *pathStr = env->GetStringUTFChars(path, NULL);
142    if (pathStr == NULL) {  // Out of memory
143        ALOGE("android_media_JetPlayer_openFile(): aborting, out of memory");
144        return JNI_FALSE;
145    }
146
147    ALOGV("android_media_JetPlayer_openFile(): trying to open %s", pathStr );
148    EAS_RESULT result = lpJet->loadFromFile(pathStr);
149    env->ReleaseStringUTFChars(path, pathStr);
150
151    if (result==EAS_SUCCESS) {
152        //ALOGV("android_media_JetPlayer_openFile(): file successfully opened");
153        return JNI_TRUE;
154    } else {
155        ALOGE("android_media_JetPlayer_openFile(): failed to open file with EAS error %d",
156            (int)result);
157        return JNI_FALSE;
158    }
159}
160
161
162// ----------------------------------------------------------------------------
163static jboolean
164android_media_JetPlayer_loadFromFileD(JNIEnv *env, jobject thiz,
165    jobject fileDescriptor, jlong offset, jlong length)
166{
167    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
168        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
169    if (lpJet == NULL ) {
170        jniThrowException(env, "java/lang/IllegalStateException",
171            "Unable to retrieve JetPlayer pointer for openFile()");
172    }
173
174    // set up event callback function
175    lpJet->setEventCallback(jetPlayerEventCallback);
176
177    ALOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" );
178    EAS_RESULT result = lpJet->loadFromFD(jniGetFDFromFileDescriptor(env, fileDescriptor),
179        (long long)offset, (long long)length); // cast params to types used by EAS_FILE
180
181    if (result==EAS_SUCCESS) {
182        ALOGV("android_media_JetPlayer_openFileDescr(): file successfully opened");
183        return JNI_TRUE;
184    } else {
185        ALOGE("android_media_JetPlayer_openFileDescr(): failed to open file with EAS error %d",
186            (int)result);
187        return JNI_FALSE;
188    }
189}
190
191
192// ----------------------------------------------------------------------------
193static jboolean
194android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz)
195{
196    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
197        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
198    if (lpJet == NULL ) {
199        jniThrowException(env, "java/lang/IllegalStateException",
200            "Unable to retrieve JetPlayer pointer for closeFile()");
201    }
202
203    if (lpJet->closeFile()==EAS_SUCCESS) {
204        //ALOGV("android_media_JetPlayer_closeFile(): file successfully closed");
205        return JNI_TRUE;
206    } else {
207        ALOGE("android_media_JetPlayer_closeFile(): failed to close file");
208        return JNI_FALSE;
209    }
210}
211
212
213// ----------------------------------------------------------------------------
214static jboolean
215android_media_JetPlayer_play(JNIEnv *env, jobject thiz)
216{
217    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
218        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
219    if (lpJet == NULL ) {
220        jniThrowException(env, "java/lang/IllegalStateException",
221            "Unable to retrieve JetPlayer pointer for play()");
222    }
223
224    EAS_RESULT result = lpJet->play();
225    if (result==EAS_SUCCESS) {
226        //ALOGV("android_media_JetPlayer_play(): play successful");
227        return JNI_TRUE;
228    } else {
229        ALOGE("android_media_JetPlayer_play(): failed to play with EAS error code %ld",
230            result);
231        return JNI_FALSE;
232    }
233}
234
235
236// ----------------------------------------------------------------------------
237static jboolean
238android_media_JetPlayer_pause(JNIEnv *env, jobject thiz)
239{
240    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
241        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
242    if (lpJet == NULL ) {
243        jniThrowException(env, "java/lang/IllegalStateException",
244            "Unable to retrieve JetPlayer pointer for pause()");
245    }
246
247    EAS_RESULT result = lpJet->pause();
248    if (result==EAS_SUCCESS) {
249        //ALOGV("android_media_JetPlayer_pause(): pause successful");
250        return JNI_TRUE;
251    } else {
252        if (result==EAS_ERROR_QUEUE_IS_EMPTY) {
253            ALOGV("android_media_JetPlayer_pause(): paused with an empty queue");
254            return JNI_TRUE;
255        } else
256            ALOGE("android_media_JetPlayer_pause(): failed to pause with EAS error code %ld",
257                result);
258        return JNI_FALSE;
259    }
260}
261
262
263// ----------------------------------------------------------------------------
264static jboolean
265android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz,
266        jint segmentNum, jint libNum, jint repeatCount, jint transpose, jint muteFlags,
267        jbyte userID)
268{
269    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
270        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
271    if (lpJet == NULL ) {
272        jniThrowException(env, "java/lang/IllegalStateException",
273            "Unable to retrieve JetPlayer pointer for queueSegment()");
274    }
275
276    EAS_RESULT result
277        = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
278    if (result==EAS_SUCCESS) {
279        //ALOGV("android_media_JetPlayer_queueSegment(): segment successfully queued");
280        return JNI_TRUE;
281    } else {
282        ALOGE("android_media_JetPlayer_queueSegment(): failed with EAS error code %ld",
283            result);
284        return JNI_FALSE;
285    }
286}
287
288
289// ----------------------------------------------------------------------------
290static jboolean
291android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz,
292        jint segmentNum, jint libNum, jint repeatCount, jint transpose, jbooleanArray muteArray,
293        jbyte userID)
294{
295    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
296        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
297    if (lpJet == NULL ) {
298        jniThrowException(env, "java/lang/IllegalStateException",
299            "Unable to retrieve JetPlayer pointer for queueSegmentMuteArray()");
300    }
301
302    EAS_RESULT result=EAS_FAILURE;
303
304    jboolean *muteTracks = NULL;
305    muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
306    if (muteTracks == NULL) {
307        ALOGE("android_media_JetPlayer_queueSegment(): failed to read track mute mask.");
308        return JNI_FALSE;
309    }
310
311    EAS_U32 muteMask=0;
312    int maxTracks = lpJet->getMaxTracks();
313    for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
314        if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
315            muteMask = (muteMask << 1) | 0x00000001;
316        else
317            muteMask = muteMask << 1;
318    }
319    //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): FINAL mute mask =0x%08lX", mask);
320
321    result = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteMask, userID);
322
323    env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
324    if (result==EAS_SUCCESS) {
325        //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): segment successfully queued");
326        return JNI_TRUE;
327    } else {
328        ALOGE("android_media_JetPlayer_queueSegmentMuteArray(): failed with EAS error code %ld",
329            result);
330        return JNI_FALSE;
331    }
332}
333
334
335// ----------------------------------------------------------------------------
336static jboolean
337android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz,
338         jint muteFlags /*unsigned?*/, jboolean bSync)
339{
340    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
341        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
342    if (lpJet == NULL ) {
343        jniThrowException(env, "java/lang/IllegalStateException",
344            "Unable to retrieve JetPlayer pointer for setMuteFlags()");
345    }
346
347    EAS_RESULT result;
348    result = lpJet->setMuteFlags(muteFlags, bSync==JNI_TRUE ? true : false);
349    if (result==EAS_SUCCESS) {
350        //ALOGV("android_media_JetPlayer_setMuteFlags(): mute flags successfully updated");
351        return JNI_TRUE;
352    } else {
353        ALOGE("android_media_JetPlayer_setMuteFlags(): failed with EAS error code %ld", result);
354        return JNI_FALSE;
355    }
356}
357
358
359// ----------------------------------------------------------------------------
360static jboolean
361android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz,
362        jbooleanArray muteArray, jboolean bSync)
363{
364    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
365        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
366    if (lpJet == NULL ) {
367        jniThrowException(env, "java/lang/IllegalStateException",
368            "Unable to retrieve JetPlayer pointer for setMuteArray()");
369    }
370
371    EAS_RESULT result=EAS_FAILURE;
372
373    jboolean *muteTracks = NULL;
374    muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
375    if (muteTracks == NULL) {
376        ALOGE("android_media_JetPlayer_setMuteArray(): failed to read track mute mask.");
377        return JNI_FALSE;
378    }
379
380    EAS_U32 muteMask=0;
381    int maxTracks = lpJet->getMaxTracks();
382    for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
383        if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
384            muteMask = (muteMask << 1) | 0x00000001;
385        else
386            muteMask = muteMask << 1;
387    }
388    //ALOGV("android_media_JetPlayer_setMuteArray(): FINAL mute mask =0x%08lX", muteMask);
389
390    result = lpJet->setMuteFlags(muteMask, bSync==JNI_TRUE ? true : false);
391
392    env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
393    if (result==EAS_SUCCESS) {
394        //ALOGV("android_media_JetPlayer_setMuteArray(): mute flags successfully updated");
395        return JNI_TRUE;
396    } else {
397        ALOGE("android_media_JetPlayer_setMuteArray(): \
398            failed to update mute flags with EAS error code %ld", result);
399        return JNI_FALSE;
400    }
401}
402
403
404// ----------------------------------------------------------------------------
405static jboolean
406android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz,
407         jint trackId, jboolean muteFlag, jboolean bSync)
408{
409    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
410        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
411    if (lpJet == NULL ) {
412        jniThrowException(env, "java/lang/IllegalStateException",
413            "Unable to retrieve JetPlayer pointer for setMuteFlag()");
414    }
415
416    EAS_RESULT result;
417    result = lpJet->setMuteFlag(trackId,
418        muteFlag==JNI_TRUE ? true : false, bSync==JNI_TRUE ? true : false);
419    if (result==EAS_SUCCESS) {
420        //ALOGV("android_media_JetPlayer_setMuteFlag(): mute flag successfully updated for track %d", trackId);
421        return JNI_TRUE;
422    } else {
423        ALOGE("android_media_JetPlayer_setMuteFlag(): failed to update mute flag for track %d with EAS error code %ld",
424                trackId, result);
425        return JNI_FALSE;
426    }
427}
428
429
430// ----------------------------------------------------------------------------
431static jboolean
432android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId)
433{
434    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
435        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
436    if (lpJet == NULL ) {
437        jniThrowException(env, "java/lang/IllegalStateException",
438            "Unable to retrieve JetPlayer pointer for triggerClip()");
439    }
440
441    EAS_RESULT result;
442    result = lpJet->triggerClip(clipId);
443    if (result==EAS_SUCCESS) {
444        //ALOGV("android_media_JetPlayer_triggerClip(): triggerClip successful for clip %d", clipId);
445        return JNI_TRUE;
446    } else {
447        ALOGE("android_media_JetPlayer_triggerClip(): triggerClip for clip %d failed with EAS error code %ld",
448                clipId, result);
449        return JNI_FALSE;
450    }
451}
452
453
454// ----------------------------------------------------------------------------
455static jboolean
456android_media_JetPlayer_clearQueue(JNIEnv *env, jobject thiz)
457{
458    JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
459        thiz, javaJetPlayerFields.nativePlayerInJavaObj);
460    if (lpJet == NULL ) {
461        jniThrowException(env, "java/lang/IllegalStateException",
462            "Unable to retrieve JetPlayer pointer for clearQueue()");
463    }
464
465    EAS_RESULT result = lpJet->clearQueue();
466    if (result==EAS_SUCCESS) {
467        //ALOGV("android_media_JetPlayer_clearQueue(): clearQueue successful");
468        return JNI_TRUE;
469    } else {
470        ALOGE("android_media_JetPlayer_clearQueue(): clearQueue failed with EAS error code %ld",
471                result);
472        return JNI_FALSE;
473    }
474}
475
476
477// ----------------------------------------------------------------------------
478// ----------------------------------------------------------------------------
479static JNINativeMethod gMethods[] = {
480    // name,               signature,               funcPtr
481    {"native_setup",       "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
482    {"native_finalize",    "()V",                   (void *)android_media_JetPlayer_finalize},
483    {"native_release",     "()V",                   (void *)android_media_JetPlayer_release},
484    {"native_loadJetFromFile",
485                           "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_loadFromFile},
486    {"native_loadJetFromFileD", "(Ljava/io/FileDescriptor;JJ)Z",
487                                                    (void *)android_media_JetPlayer_loadFromFileD},
488    {"native_closeJetFile","()Z",                   (void *)android_media_JetPlayer_closeFile},
489    {"native_playJet",     "()Z",                   (void *)android_media_JetPlayer_play},
490    {"native_pauseJet",    "()Z",                   (void *)android_media_JetPlayer_pause},
491    {"native_queueJetSegment",
492                           "(IIIIIB)Z",             (void *)android_media_JetPlayer_queueSegment},
493    {"native_queueJetSegmentMuteArray",
494                           "(IIII[ZB)Z",     (void *)android_media_JetPlayer_queueSegmentMuteArray},
495    {"native_setMuteFlags","(IZ)Z",                 (void *)android_media_JetPlayer_setMuteFlags},
496    {"native_setMuteArray","([ZZ)Z",                (void *)android_media_JetPlayer_setMuteArray},
497    {"native_setMuteFlag", "(IZZ)Z",                (void *)android_media_JetPlayer_setMuteFlag},
498    {"native_triggerClip", "(I)Z",                  (void *)android_media_JetPlayer_triggerClip},
499    {"native_clearQueue",  "()Z",                   (void *)android_media_JetPlayer_clearQueue},
500};
501
502#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
503#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
504
505
506int register_android_media_JetPlayer(JNIEnv *env)
507{
508    jclass jetPlayerClass = NULL;
509    javaJetPlayerFields.jetClass = NULL;
510    javaJetPlayerFields.postNativeEventInJava = NULL;
511    javaJetPlayerFields.nativePlayerInJavaObj = NULL;
512
513    // Get the JetPlayer java class
514    jetPlayerClass = env->FindClass(kClassPathName);
515    if (jetPlayerClass == NULL) {
516        ALOGE("Can't find %s", kClassPathName);
517        return -1;
518    }
519    javaJetPlayerFields.jetClass = (jclass)env->NewGlobalRef(jetPlayerClass);
520
521    // Get the mNativePlayerInJavaObj variable field
522    javaJetPlayerFields.nativePlayerInJavaObj = env->GetFieldID(
523            jetPlayerClass,
524            JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "I");
525    if (javaJetPlayerFields.nativePlayerInJavaObj == NULL) {
526        ALOGE("Can't find JetPlayer.%s", JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME);
527        return -1;
528    }
529
530    // Get the callback to post events from this native code to Java
531    javaJetPlayerFields.postNativeEventInJava = env->GetStaticMethodID(javaJetPlayerFields.jetClass,
532            JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;III)V");
533    if (javaJetPlayerFields.postNativeEventInJava == NULL) {
534        ALOGE("Can't find Jet.%s", JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME);
535        return -1;
536    }
537
538    return AndroidRuntime::registerNativeMethods(env,
539            kClassPathName, gMethods, NELEM(gMethods));
540}
541