Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2010 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 #include <assert.h>
     18 #include <jni.h>
     19 #include <pthread.h>
     20 #include <string.h>
     21 //#define LOG_NDEBUG 0
     22 #define LOG_TAG "NativeMedia"
     23 #include <utils/Log.h>
     24 
     25 #include <OMXAL/OpenMAXAL.h>
     26 #include <OMXAL/OpenMAXAL_Android.h>
     27 
     28 #include <android/native_window_jni.h>
     29 
     30 // engine interfaces
     31 static XAObjectItf engineObject = NULL;
     32 static XAEngineItf engineEngine = NULL;
     33 
     34 // output mix interfaces
     35 static XAObjectItf outputMixObject = NULL;
     36 
     37 // streaming media player interfaces
     38 static XAObjectItf             playerObj = NULL;
     39 static XAPlayItf               playerPlayItf = NULL;
     40 static XAAndroidBufferQueueItf playerBQItf = NULL;
     41 static XAStreamInformationItf  playerStreamInfoItf = NULL;
     42 static XAVolumeItf             playerVolItf = NULL;
     43 
     44 // number of required interfaces for the MediaPlayer creation
     45 #define NB_MAXAL_INTERFACES 3 // XAAndroidBufferQueueItf, XAStreamInformationItf and XAPlayItf
     46 
     47 // video sink for the player
     48 static ANativeWindow* theNativeWindow;
     49 
     50 // number of buffers in our buffer queue, an arbitrary number
     51 #define NB_BUFFERS 16
     52 
     53 // we're streaming MPEG-2 transport stream data, operate on transport stream block size
     54 #define MPEG2_TS_BLOCK_SIZE 188
     55 
     56 // number of MPEG-2 transport stream blocks per buffer, an arbitrary number
     57 #define BLOCKS_PER_BUFFER 20
     58 
     59 // determines how much memory we're dedicating to memory caching
     60 #define BUFFER_SIZE (BLOCKS_PER_BUFFER*MPEG2_TS_BLOCK_SIZE)
     61 
     62 // where we cache in memory the data to play
     63 // note this memory is re-used by the buffer queue callback
     64 char dataCache[BUFFER_SIZE * NB_BUFFERS];
     65 
     66 // handle of the file to play
     67 FILE *file;
     68 
     69 // has the app reached the end of the file
     70 jboolean reachedEof = JNI_FALSE;
     71 
     72 // constant to identify a buffer context which is the end of the stream to decode
     73 static const int kEosBufferCntxt = 1980; // a magic value we can compare against
     74 
     75 // for mutual exclusion between callback thread and application thread(s)
     76 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
     77 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
     78 
     79 // whether a discontinuity is in progress
     80 jboolean discontinuity = JNI_FALSE;
     81 
     82 static jboolean enqueueInitialBuffers(jboolean discontinuity);
     83 
     84 // Callback for XAPlayItf through which we receive the XA_PLAYEVENT_HEADATEND event */
     85 void PlayCallback(XAPlayItf caller, void *pContext, XAuint32 event) {
     86     if (event & XA_PLAYEVENT_HEADATEND) {
     87         ALOGV("XA_PLAYEVENT_HEADATEND received, all MP2TS data has been decoded\n");
     88     }
     89 }
     90 
     91 // AndroidBufferQueueItf callback for an audio player
     92 XAresult AndroidBufferQueueCallback(
     93         XAAndroidBufferQueueItf caller,
     94         void *pCallbackContext,        /* input */
     95         void *pBufferContext,          /* input */
     96         void *pBufferData,             /* input */
     97         XAuint32 dataSize,             /* input */
     98         XAuint32 dataUsed,             /* input */
     99         const XAAndroidBufferItem *pItems,/* input */
    100         XAuint32 itemsLength           /* input */)
    101 {
    102     XAresult res;
    103     int ok;
    104 
    105     // pCallbackContext was specified as NULL at RegisterCallback and is unused here
    106     assert(NULL == pCallbackContext);
    107 
    108     // note there is never any contention on this mutex unless a discontinuity request is active
    109     ok = pthread_mutex_lock(&mutex);
    110     assert(0 == ok);
    111 
    112     // was a discontinuity requested?
    113     if (discontinuity) {
    114         // FIXME sorry, can't rewind after EOS
    115         if (!reachedEof) {
    116             // clear the buffer queue
    117             res = (*playerBQItf)->Clear(playerBQItf);
    118             assert(XA_RESULT_SUCCESS == res);
    119             // rewind the data source so we are guaranteed to be at an appropriate point
    120             rewind(file);
    121             // Enqueue the initial buffers, with a discontinuity indicator on first buffer
    122             (void) enqueueInitialBuffers(JNI_TRUE);
    123         }
    124         // acknowledge the discontinuity request
    125         discontinuity = JNI_FALSE;
    126         ok = pthread_cond_signal(&cond);
    127         assert(0 == ok);
    128         goto exit;
    129     }
    130 
    131     if ((pBufferData == NULL) && (pBufferContext != NULL)) {
    132         const int processedCommand = *(int *)pBufferContext;
    133         if (kEosBufferCntxt == processedCommand) {
    134             ALOGV("EOS was processed\n");
    135             // our buffer with the EOS message has been consumed
    136             assert(0 == dataSize);
    137             goto exit;
    138         }
    139     }
    140 
    141     // pBufferData is a pointer to a buffer that we previously Enqueued
    142     assert(BUFFER_SIZE == dataSize);
    143     assert(dataCache <= (char *) pBufferData && (char *) pBufferData <
    144             &dataCache[BUFFER_SIZE * NB_BUFFERS]);
    145     assert(0 == (((char *) pBufferData - dataCache) % BUFFER_SIZE));
    146 
    147 #if 0
    148     // sample code to use the XAVolumeItf
    149     XAAndroidBufferQueueState state;
    150     (*caller)->GetState(caller, &state);
    151     switch (state.index) {
    152     case 300:
    153         (*playerVolItf)->SetVolumeLevel(playerVolItf, -600); // -6dB
    154         ALOGV("setting volume to -6dB");
    155         break;
    156     case 400:
    157         (*playerVolItf)->SetVolumeLevel(playerVolItf, -1200); // -12dB
    158         ALOGV("setting volume to -12dB");
    159         break;
    160     case 500:
    161         (*playerVolItf)->SetVolumeLevel(playerVolItf, 0); // full volume
    162         ALOGV("setting volume to 0dB (full volume)");
    163         break;
    164     case 600:
    165         (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_TRUE); // mute
    166         ALOGV("muting player");
    167         break;
    168     case 700:
    169         (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_FALSE); // unmute
    170         ALOGV("unmuting player");
    171         break;
    172     case 800:
    173         (*playerVolItf)->SetStereoPosition(playerVolItf, -1000);
    174         (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_TRUE);
    175         ALOGV("pan sound to the left (hard-left)");
    176         break;
    177     case 900:
    178         (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_FALSE);
    179         ALOGV("disabling stereo position");
    180         break;
    181     default:
    182         break;
    183     }
    184 #endif
    185 
    186     // don't bother trying to read more data once we've hit EOF
    187     if (reachedEof) {
    188         goto exit;
    189     }
    190 
    191     size_t nbRead;
    192     // note we do call fread from multiple threads, but never concurrently
    193     nbRead = fread(pBufferData, BUFFER_SIZE, 1, file);
    194     if (nbRead > 0) {
    195         assert(1 == nbRead);
    196         res = (*caller)->Enqueue(caller, NULL /*pBufferContext*/,
    197                 pBufferData /*pData*/,
    198                 nbRead * BUFFER_SIZE /*dataLength*/,
    199                 NULL /*pMsg*/,
    200                 0 /*msgLength*/);
    201         assert(XA_RESULT_SUCCESS == res);
    202     } else {
    203         // signal EOS
    204         XAAndroidBufferItem msgEos[1];
    205         msgEos[0].itemKey = XA_ANDROID_ITEMKEY_EOS;
    206         msgEos[0].itemSize = 0;
    207         // EOS message has no parameters, so the total size of the message is the size of the key
    208         //   plus the size if itemSize, both XAuint32
    209         res = (*caller)->Enqueue(caller, (void *)&kEosBufferCntxt /*pBufferContext*/,
    210                 NULL /*pData*/, 0 /*dataLength*/,
    211                 msgEos /*pMsg*/,
    212                 // FIXME == sizeof(BufferItem)? */
    213                 sizeof(XAuint32)*2 /*msgLength*/);
    214         assert(XA_RESULT_SUCCESS == res);
    215         reachedEof = JNI_TRUE;
    216     }
    217 
    218 exit:
    219     ok = pthread_mutex_unlock(&mutex);
    220     assert(0 == ok);
    221     return XA_RESULT_SUCCESS;
    222 }
    223 
    224 
    225 void StreamChangeCallback (XAStreamInformationItf caller,
    226         XAuint32 eventId,
    227         XAuint32 streamIndex,
    228         void * pEventData,
    229         void * pContext )
    230 {
    231     ALOGV("StreamChangeCallback called for stream %u", streamIndex);
    232     // pContext was specified as NULL at RegisterStreamChangeCallback and is unused here
    233     assert(NULL == pContext);
    234     switch (eventId) {
    235     case XA_STREAMCBEVENT_PROPERTYCHANGE: {
    236         /** From spec 1.0.1:
    237             "This event indicates that stream property change has occurred.
    238             The streamIndex parameter identifies the stream with the property change.
    239             The pEventData parameter for this event is not used and shall be ignored."
    240          */
    241 
    242         XAresult res;
    243         XAuint32 domain;
    244         res = (*caller)->QueryStreamType(caller, streamIndex, &domain);
    245         assert(XA_RESULT_SUCCESS == res);
    246         switch (domain) {
    247         case XA_DOMAINTYPE_VIDEO: {
    248             XAVideoStreamInformation videoInfo;
    249             res = (*caller)->QueryStreamInformation(caller, streamIndex, &videoInfo);
    250             assert(XA_RESULT_SUCCESS == res);
    251             ALOGI("Found video size %u x %u, codec ID=%u, frameRate=%u, bitRate=%u, duration=%u ms",
    252                         videoInfo.width, videoInfo.height, videoInfo.codecId, videoInfo.frameRate,
    253                         videoInfo.bitRate, videoInfo.duration);
    254         } break;
    255         default:
    256             fprintf(stderr, "Unexpected domain %u\n", domain);
    257             break;
    258         }
    259         } break;
    260     default:
    261         fprintf(stderr, "Unexpected stream event ID %u\n", eventId);
    262         break;
    263     }
    264 }
    265 
    266 
    267 // create the engine and output mix objects
    268 void Java_com_example_nativemedia_NativeMedia_createEngine(JNIEnv* env, jclass clazz)
    269 {
    270     XAresult res;
    271 
    272     // create engine
    273     res = xaCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    274     assert(XA_RESULT_SUCCESS == res);
    275 
    276     // realize the engine
    277     res = (*engineObject)->Realize(engineObject, XA_BOOLEAN_FALSE);
    278     assert(XA_RESULT_SUCCESS == res);
    279 
    280     // get the engine interface, which is needed in order to create other objects
    281     res = (*engineObject)->GetInterface(engineObject, XA_IID_ENGINE, &engineEngine);
    282     assert(XA_RESULT_SUCCESS == res);
    283 
    284     // create output mix
    285     res = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
    286     assert(XA_RESULT_SUCCESS == res);
    287 
    288     // realize the output mix
    289     res = (*outputMixObject)->Realize(outputMixObject, XA_BOOLEAN_FALSE);
    290     assert(XA_RESULT_SUCCESS == res);
    291 
    292 }
    293 
    294 
    295 // Enqueue the initial buffers, and optionally signal a discontinuity in the first buffer
    296 static jboolean enqueueInitialBuffers(jboolean discontinuity)
    297 {
    298 
    299     /* Fill our cache */
    300     size_t nbRead;
    301     nbRead = fread(dataCache, BUFFER_SIZE, NB_BUFFERS, file);
    302     if (nbRead <= 0) {
    303         // could be premature EOF or I/O error
    304         ALOGE("Error filling cache, exiting\n");
    305         return JNI_FALSE;
    306     }
    307     assert(1 <= nbRead && nbRead <= NB_BUFFERS);
    308     ALOGV("Initially queueing %zu buffers of %u bytes each", nbRead, BUFFER_SIZE);
    309 
    310     /* Enqueue the content of our cache before starting to play,
    311        we don't want to starve the player */
    312     size_t i;
    313     for (i = 0; i < nbRead; i++) {
    314         XAresult res;
    315         if (discontinuity) {
    316             // signal discontinuity
    317             XAAndroidBufferItem items[1];
    318             items[0].itemKey = XA_ANDROID_ITEMKEY_DISCONTINUITY;
    319             items[0].itemSize = 0;
    320             // DISCONTINUITY message has no parameters,
    321             //   so the total size of the message is the size of the key
    322             //   plus the size if itemSize, both XAuint32
    323             res = (*playerBQItf)->Enqueue(playerBQItf, NULL /*pBufferContext*/,
    324                     dataCache + i*BUFFER_SIZE, BUFFER_SIZE, items /*pMsg*/,
    325                     // FIXME == sizeof(BufferItem)? */
    326                     sizeof(XAuint32)*2 /*msgLength*/);
    327             discontinuity = JNI_FALSE;
    328         } else {
    329             res = (*playerBQItf)->Enqueue(playerBQItf, NULL /*pBufferContext*/,
    330                     dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0);
    331         }
    332         assert(XA_RESULT_SUCCESS == res);
    333     }
    334 
    335     return JNI_TRUE;
    336 }
    337 
    338 
    339 // create streaming media player
    340 jboolean Java_com_example_nativemedia_NativeMedia_createStreamingMediaPlayer(JNIEnv* env,
    341         jclass clazz, jstring filename)
    342 {
    343     XAresult res;
    344 
    345     // convert Java string to UTF-8
    346     const char *utf8 = (*env)->GetStringUTFChars(env, filename, NULL);
    347     assert(NULL != utf8);
    348 
    349     // open the file to play
    350     file = fopen(utf8, "rb");
    351     if (file == NULL) {
    352         ALOGE("Failed to open %s", utf8);
    353         return JNI_FALSE;
    354     }
    355 
    356     // configure data source
    357     XADataLocator_AndroidBufferQueue loc_abq = { XA_DATALOCATOR_ANDROIDBUFFERQUEUE, NB_BUFFERS };
    358     XADataFormat_MIME format_mime = {
    359             XA_DATAFORMAT_MIME, XA_ANDROID_MIME_MP2TS, XA_CONTAINERTYPE_MPEG_TS };
    360     XADataSource dataSrc = {&loc_abq, &format_mime};
    361 
    362     // configure audio sink
    363     XADataLocator_OutputMix loc_outmix = { XA_DATALOCATOR_OUTPUTMIX, outputMixObject };
    364     XADataSink audioSnk = { &loc_outmix, NULL };
    365 
    366     // configure image video sink
    367     XADataLocator_NativeDisplay loc_nd = {
    368             XA_DATALOCATOR_NATIVEDISPLAY,        // locatorType
    369             // the video sink must be an ANativeWindow
    370             // created from a Surface or SurfaceTextureClient
    371             (void*)theNativeWindow,              // hWindow
    372             // must be NULL
    373             NULL                                 // hDisplay
    374     };
    375     XADataSink imageVideoSink = {&loc_nd, NULL};
    376 
    377     // declare interfaces to use
    378     XAboolean     required[NB_MAXAL_INTERFACES]
    379                            = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE,           XA_BOOLEAN_TRUE};
    380     XAInterfaceID iidArray[NB_MAXAL_INTERFACES]
    381                            = {XA_IID_PLAY,     XA_IID_ANDROIDBUFFERQUEUESOURCE,
    382                                                XA_IID_STREAMINFORMATION};
    383 
    384 
    385     // create media player
    386     res = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObj, &dataSrc,
    387             NULL, &audioSnk, &imageVideoSink, NULL, NULL,
    388             NB_MAXAL_INTERFACES /*XAuint32 numInterfaces*/,
    389             iidArray /*const XAInterfaceID *pInterfaceIds*/,
    390             required /*const XAboolean *pInterfaceRequired*/);
    391     assert(XA_RESULT_SUCCESS == res);
    392 
    393     // release the Java string and UTF-8
    394     (*env)->ReleaseStringUTFChars(env, filename, utf8);
    395 
    396     // realize the player
    397     res = (*playerObj)->Realize(playerObj, XA_BOOLEAN_FALSE);
    398     assert(XA_RESULT_SUCCESS == res);
    399 
    400     // get the play interface
    401     res = (*playerObj)->GetInterface(playerObj, XA_IID_PLAY, &playerPlayItf);
    402     assert(XA_RESULT_SUCCESS == res);
    403 
    404     // get the stream information interface (for video size)
    405     res = (*playerObj)->GetInterface(playerObj, XA_IID_STREAMINFORMATION, &playerStreamInfoItf);
    406     assert(XA_RESULT_SUCCESS == res);
    407 
    408     // get the volume interface
    409     res = (*playerObj)->GetInterface(playerObj, XA_IID_VOLUME, &playerVolItf);
    410     assert(XA_RESULT_SUCCESS == res);
    411 
    412     // get the Android buffer queue interface
    413     res = (*playerObj)->GetInterface(playerObj, XA_IID_ANDROIDBUFFERQUEUESOURCE, &playerBQItf);
    414     assert(XA_RESULT_SUCCESS == res);
    415 
    416     // specify which events we want to be notified of
    417     res = (*playerBQItf)->SetCallbackEventsMask(playerBQItf, XA_ANDROIDBUFFERQUEUEEVENT_PROCESSED);
    418 
    419     // use the play interface to set up a callback for the XA_PLAYEVENT_HEADATEND event */
    420     res = (*playerPlayItf)->SetCallbackEventsMask(playerPlayItf, XA_PLAYEVENT_HEADATEND);
    421     assert(XA_RESULT_SUCCESS == res);
    422     res = (*playerPlayItf)->RegisterCallback(playerPlayItf,
    423             PlayCallback /*callback*/, NULL /*pContext*/);
    424     assert(XA_RESULT_SUCCESS == res);
    425 
    426     // register the callback from which OpenMAX AL can retrieve the data to play
    427     res = (*playerBQItf)->RegisterCallback(playerBQItf, AndroidBufferQueueCallback, NULL);
    428     assert(XA_RESULT_SUCCESS == res);
    429 
    430     // we want to be notified of the video size once it's found, so we register a callback for that
    431     res = (*playerStreamInfoItf)->RegisterStreamChangeCallback(playerStreamInfoItf,
    432             StreamChangeCallback, NULL);
    433 
    434     // enqueue the initial buffers
    435     if (!enqueueInitialBuffers(JNI_FALSE)) {
    436         return JNI_FALSE;
    437     }
    438 
    439     // prepare the player
    440     res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PAUSED);
    441     assert(XA_RESULT_SUCCESS == res);
    442 
    443     // set the volume
    444     res = (*playerVolItf)->SetVolumeLevel(playerVolItf, 0);//-300);
    445     assert(XA_RESULT_SUCCESS == res);
    446 
    447     // start the playback
    448     res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PLAYING);
    449     assert(XA_RESULT_SUCCESS == res);
    450 
    451     return JNI_TRUE;
    452 }
    453 
    454 
    455 // set the playing state for the streaming media player
    456 void Java_com_example_nativemedia_NativeMedia_setPlayingStreamingMediaPlayer(JNIEnv* env,
    457         jclass clazz, jboolean isPlaying)
    458 {
    459     XAresult res;
    460 
    461     // make sure the streaming media player was created
    462     if (NULL != playerPlayItf) {
    463 
    464         // set the player's state
    465         res = (*playerPlayItf)->SetPlayState(playerPlayItf, isPlaying ?
    466             XA_PLAYSTATE_PLAYING : XA_PLAYSTATE_PAUSED);
    467         assert(XA_RESULT_SUCCESS == res);
    468 
    469     }
    470 
    471 }
    472 
    473 
    474 // shut down the native media system
    475 void Java_com_example_nativemedia_NativeMedia_shutdown(JNIEnv* env, jclass clazz)
    476 {
    477     // destroy streaming media player object, and invalidate all associated interfaces
    478     if (playerObj != NULL) {
    479         (*playerObj)->Destroy(playerObj);
    480         playerObj = NULL;
    481         playerPlayItf = NULL;
    482         playerBQItf = NULL;
    483         playerStreamInfoItf = NULL;
    484         playerVolItf = NULL;
    485     }
    486 
    487     // destroy output mix object, and invalidate all associated interfaces
    488     if (outputMixObject != NULL) {
    489         (*outputMixObject)->Destroy(outputMixObject);
    490         outputMixObject = NULL;
    491     }
    492 
    493     // destroy engine object, and invalidate all associated interfaces
    494     if (engineObject != NULL) {
    495         (*engineObject)->Destroy(engineObject);
    496         engineObject = NULL;
    497         engineEngine = NULL;
    498     }
    499 
    500     // close the file
    501     if (file != NULL) {
    502         fclose(file);
    503         file = NULL;
    504     }
    505 
    506     // make sure we don't leak native windows
    507     if (theNativeWindow != NULL) {
    508         ANativeWindow_release(theNativeWindow);
    509         theNativeWindow = NULL;
    510     }
    511 }
    512 
    513 
    514 // set the surface
    515 void Java_com_example_nativemedia_NativeMedia_setSurface(JNIEnv *env, jclass clazz, jobject surface)
    516 {
    517     // obtain a native window from a Java surface
    518     theNativeWindow = ANativeWindow_fromSurface(env, surface);
    519 }
    520 
    521 
    522 // rewind the streaming media player
    523 void Java_com_example_nativemedia_NativeMedia_rewindStreamingMediaPlayer(JNIEnv *env, jclass clazz)
    524 {
    525     // make sure the streaming media player was created
    526     if (NULL != playerBQItf && NULL != file) {
    527         // first wait for buffers currently in queue to be drained
    528         int ok;
    529         ok = pthread_mutex_lock(&mutex);
    530         assert(0 == ok);
    531         discontinuity = JNI_TRUE;
    532         // wait for discontinuity request to be observed by buffer queue callback
    533         // FIXME sorry, can't rewind after EOS
    534         while (discontinuity && !reachedEof) {
    535             ok = pthread_cond_wait(&cond, &mutex);
    536             assert(0 == ok);
    537         }
    538         ok = pthread_mutex_unlock(&mutex);
    539         assert(0 == ok);
    540     }
    541 
    542 }
    543