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 LOGV("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 LOGV("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 LOGV("setting volume to -6dB"); 155 break; 156 case 400: 157 (*playerVolItf)->SetVolumeLevel(playerVolItf, -1200); // -12dB 158 LOGV("setting volume to -12dB"); 159 break; 160 case 500: 161 (*playerVolItf)->SetVolumeLevel(playerVolItf, 0); // full volume 162 LOGV("setting volume to 0dB (full volume)"); 163 break; 164 case 600: 165 (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_TRUE); // mute 166 LOGV("muting player"); 167 break; 168 case 700: 169 (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_FALSE); // unmute 170 LOGV("unmuting player"); 171 break; 172 case 800: 173 (*playerVolItf)->SetStereoPosition(playerVolItf, -1000); 174 (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_TRUE); 175 LOGV("pan sound to the left (hard-left)"); 176 break; 177 case 900: 178 (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_FALSE); 179 LOGV("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 LOGV("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 LOGI("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 LOGE("Error filling cache, exiting\n"); 305 return JNI_FALSE; 306 } 307 assert(1 <= nbRead && nbRead <= NB_BUFFERS); 308 LOGV("Initially queueing %u 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 LOGE("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 SurfaceTexture 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 // set the surface texture 522 void Java_com_example_nativemedia_NativeMedia_setSurfaceTexture(JNIEnv *env, jclass clazz, 523 jobject surfaceTexture) 524 { 525 // obtain a native window from a Java surface texture 526 theNativeWindow = ANativeWindow_fromSurfaceTexture(env, surfaceTexture); 527 } 528 529 530 // rewind the streaming media player 531 void Java_com_example_nativemedia_NativeMedia_rewindStreamingMediaPlayer(JNIEnv *env, jclass clazz) 532 { 533 XAresult res; 534 535 // make sure the streaming media player was created 536 if (NULL != playerBQItf && NULL != file) { 537 // first wait for buffers currently in queue to be drained 538 int ok; 539 ok = pthread_mutex_lock(&mutex); 540 assert(0 == ok); 541 discontinuity = JNI_TRUE; 542 // wait for discontinuity request to be observed by buffer queue callback 543 // FIXME sorry, can't rewind after EOS 544 while (discontinuity && !reachedEof) { 545 ok = pthread_cond_wait(&cond, &mutex); 546 assert(0 == ok); 547 } 548 ok = pthread_mutex_unlock(&mutex); 549 assert(0 == ok); 550 } 551 552 } 553