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 created from a Surface or SurfaceTextureClient
    370             (void*)theNativeWindow,              // hWindow
    371             // must be NULL
    372             NULL                                 // hDisplay
    373     };
    374     XADataSink imageVideoSink = {&loc_nd, NULL};
    375 
    376     // declare interfaces to use
    377     XAboolean     required[NB_MAXAL_INTERFACES]
    378                            = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE,           XA_BOOLEAN_TRUE};
    379     XAInterfaceID iidArray[NB_MAXAL_INTERFACES]
    380                            = {XA_IID_PLAY,     XA_IID_ANDROIDBUFFERQUEUESOURCE,
    381                                                XA_IID_STREAMINFORMATION};
    382 
    383 
    384     // create media player
    385     res = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObj, &dataSrc,
    386             NULL, &audioSnk, &imageVideoSink, NULL, NULL,
    387             NB_MAXAL_INTERFACES /*XAuint32 numInterfaces*/,
    388             iidArray /*const XAInterfaceID *pInterfaceIds*/,
    389             required /*const XAboolean *pInterfaceRequired*/);
    390     assert(XA_RESULT_SUCCESS == res);
    391 
    392     // release the Java string and UTF-8
    393     (*env)->ReleaseStringUTFChars(env, filename, utf8);
    394 
    395     // realize the player
    396     res = (*playerObj)->Realize(playerObj, XA_BOOLEAN_FALSE);
    397     assert(XA_RESULT_SUCCESS == res);
    398 
    399     // get the play interface
    400     res = (*playerObj)->GetInterface(playerObj, XA_IID_PLAY, &playerPlayItf);
    401     assert(XA_RESULT_SUCCESS == res);
    402 
    403     // get the stream information interface (for video size)
    404     res = (*playerObj)->GetInterface(playerObj, XA_IID_STREAMINFORMATION, &playerStreamInfoItf);
    405     assert(XA_RESULT_SUCCESS == res);
    406 
    407     // get the volume interface
    408     res = (*playerObj)->GetInterface(playerObj, XA_IID_VOLUME, &playerVolItf);
    409     assert(XA_RESULT_SUCCESS == res);
    410 
    411     // get the Android buffer queue interface
    412     res = (*playerObj)->GetInterface(playerObj, XA_IID_ANDROIDBUFFERQUEUESOURCE, &playerBQItf);
    413     assert(XA_RESULT_SUCCESS == res);
    414 
    415     // specify which events we want to be notified of
    416     res = (*playerBQItf)->SetCallbackEventsMask(playerBQItf, XA_ANDROIDBUFFERQUEUEEVENT_PROCESSED);
    417 
    418     // use the play interface to set up a callback for the XA_PLAYEVENT_HEADATEND event */
    419     res = (*playerPlayItf)->SetCallbackEventsMask(playerPlayItf, XA_PLAYEVENT_HEADATEND);
    420     assert(XA_RESULT_SUCCESS == res);
    421     res = (*playerPlayItf)->RegisterCallback(playerPlayItf,
    422             PlayCallback /*callback*/, NULL /*pContext*/);
    423     assert(XA_RESULT_SUCCESS == res);
    424 
    425     // register the callback from which OpenMAX AL can retrieve the data to play
    426     res = (*playerBQItf)->RegisterCallback(playerBQItf, AndroidBufferQueueCallback, NULL);
    427     assert(XA_RESULT_SUCCESS == res);
    428 
    429     // we want to be notified of the video size once it's found, so we register a callback for that
    430     res = (*playerStreamInfoItf)->RegisterStreamChangeCallback(playerStreamInfoItf,
    431             StreamChangeCallback, NULL);
    432 
    433     // enqueue the initial buffers
    434     if (!enqueueInitialBuffers(JNI_FALSE)) {
    435         return JNI_FALSE;
    436     }
    437 
    438     // prepare the player
    439     res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PAUSED);
    440     assert(XA_RESULT_SUCCESS == res);
    441 
    442     // set the volume
    443     res = (*playerVolItf)->SetVolumeLevel(playerVolItf, 0);//-300);
    444     assert(XA_RESULT_SUCCESS == res);
    445 
    446     // start the playback
    447     res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PLAYING);
    448     assert(XA_RESULT_SUCCESS == res);
    449 
    450     return JNI_TRUE;
    451 }
    452 
    453 
    454 // set the playing state for the streaming media player
    455 void Java_com_example_nativemedia_NativeMedia_setPlayingStreamingMediaPlayer(JNIEnv* env,
    456         jclass clazz, jboolean isPlaying)
    457 {
    458     XAresult res;
    459 
    460     // make sure the streaming media player was created
    461     if (NULL != playerPlayItf) {
    462 
    463         // set the player's state
    464         res = (*playerPlayItf)->SetPlayState(playerPlayItf, isPlaying ?
    465             XA_PLAYSTATE_PLAYING : XA_PLAYSTATE_PAUSED);
    466         assert(XA_RESULT_SUCCESS == res);
    467 
    468     }
    469 
    470 }
    471 
    472 
    473 // shut down the native media system
    474 void Java_com_example_nativemedia_NativeMedia_shutdown(JNIEnv* env, jclass clazz)
    475 {
    476     // destroy streaming media player object, and invalidate all associated interfaces
    477     if (playerObj != NULL) {
    478         (*playerObj)->Destroy(playerObj);
    479         playerObj = NULL;
    480         playerPlayItf = NULL;
    481         playerBQItf = NULL;
    482         playerStreamInfoItf = NULL;
    483         playerVolItf = NULL;
    484     }
    485 
    486     // destroy output mix object, and invalidate all associated interfaces
    487     if (outputMixObject != NULL) {
    488         (*outputMixObject)->Destroy(outputMixObject);
    489         outputMixObject = NULL;
    490     }
    491 
    492     // destroy engine object, and invalidate all associated interfaces
    493     if (engineObject != NULL) {
    494         (*engineObject)->Destroy(engineObject);
    495         engineObject = NULL;
    496         engineEngine = NULL;
    497     }
    498 
    499     // close the file
    500     if (file != NULL) {
    501         fclose(file);
    502         file = NULL;
    503     }
    504 
    505     // make sure we don't leak native windows
    506     if (theNativeWindow != NULL) {
    507         ANativeWindow_release(theNativeWindow);
    508         theNativeWindow = NULL;
    509     }
    510 }
    511 
    512 
    513 // set the surface
    514 void Java_com_example_nativemedia_NativeMedia_setSurface(JNIEnv *env, jclass clazz, jobject surface)
    515 {
    516     // obtain a native window from a Java surface
    517     theNativeWindow = ANativeWindow_fromSurface(env, surface);
    518 }
    519 
    520 
    521 // rewind the streaming media player
    522 void Java_com_example_nativemedia_NativeMedia_rewindStreamingMediaPlayer(JNIEnv *env, jclass clazz)
    523 {
    524     // make sure the streaming media player was created
    525     if (NULL != playerBQItf && NULL != file) {
    526         // first wait for buffers currently in queue to be drained
    527         int ok;
    528         ok = pthread_mutex_lock(&mutex);
    529         assert(0 == ok);
    530         discontinuity = JNI_TRUE;
    531         // wait for discontinuity request to be observed by buffer queue callback
    532         // FIXME sorry, can't rewind after EOS
    533         while (discontinuity && !reachedEof) {
    534             ok = pthread_cond_wait(&cond, &mutex);
    535             assert(0 == ok);
    536         }
    537         ok = pthread_mutex_unlock(&mutex);
    538         assert(0 == ok);
    539     }
    540 
    541 }
    542