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