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