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