1/* 2 * Copyright (C) 2011 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// OpenMAX AL MediaPlayer command-line player 18 19#include <assert.h> 20#include <pthread.h> 21#include <stdio.h> 22#include <stdlib.h> 23#include <string.h> 24#include <fcntl.h> 25#include <sys/mman.h> 26#include <sys/stat.h> 27#include <unistd.h> 28#include <OMXAL/OpenMAXAL.h> 29#include <OMXAL/OpenMAXAL_Android.h> 30#include "nativewindow.h" 31 32#define MPEG2TS_PACKET_SIZE 188 // MPEG-2 transport stream packet size in bytes 33#define PACKETS_PER_BUFFER 20 // Number of MPEG-2 transport stream packets per buffer 34 35#define NB_BUFFERS 2 // Number of buffers in Android buffer queue 36 37// MPEG-2 transport stream packet 38typedef struct { 39 char data[MPEG2TS_PACKET_SIZE]; 40} MPEG2TS_Packet; 41 42// Globals shared between main thread and buffer queue callback 43MPEG2TS_Packet *packets; 44size_t totalPackets; // total number of packets in input file 45size_t numPackets; // number of packets to play, defaults to totalPackets - firstPacket 46size_t curPacket; // current packet index 47size_t discPacket; // discontinuity packet index, defaults to no discontinuity requested 48size_t afterDiscPacket; // packet index to switch to after the discontinuity 49size_t firstPacket; // first packet index to be played, defaults to zero 50size_t lastPacket; // last packet index to be played 51size_t formatPacket; // format change packet index, defaults to no format change requested 52XAmillisecond seekPos = XA_TIME_UNKNOWN; // seek to this position initially 53int pauseMs = -1; // pause after this many ms into playback 54XAboolean forceCallbackFailure = XA_BOOLEAN_FALSE; // force callback failures occasionally 55XAboolean sentEOS = XA_BOOLEAN_FALSE; // whether we have enqueued EOS yet 56 57// These are extensions to OpenMAX AL 1.0.1 values 58 59#define PREFETCHSTATUS_UNKNOWN ((XAuint32) 0) 60#define PREFETCHSTATUS_ERROR ((XAuint32) (-1)) 61 62// Mutex and condition shared with main program to protect prefetch_status 63 64static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 65static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 66XAuint32 prefetch_status = PREFETCHSTATUS_UNKNOWN; 67 68/* used to detect errors likely to have occured when the OpenMAX AL framework fails to open 69 * a resource, for instance because a file URI is invalid, or an HTTP server doesn't respond. 70 */ 71#define PREFETCHEVENT_ERROR_CANDIDATE \ 72 (XA_PREFETCHEVENT_STATUSCHANGE | XA_PREFETCHEVENT_FILLLEVELCHANGE) 73 74// stream event change callback 75void streamEventChangeCallback(XAStreamInformationItf caller __unused, XAuint32 eventId, 76 XAuint32 streamIndex, void *pEventData, void *pContext) 77{ 78 // context parameter is specified as NULL and is unused here 79 assert(NULL == pContext); 80 switch (eventId) { 81 case XA_STREAMCBEVENT_PROPERTYCHANGE: 82 printf("XA_STREAMCBEVENT_PROPERTYCHANGE on stream index %u, pEventData %p\n", streamIndex, 83 pEventData); 84 break; 85 default: 86 printf("Unknown stream event ID %u\n", eventId); 87 break; 88 } 89} 90 91// prefetch status callback 92void prefetchStatusCallback(XAPrefetchStatusItf caller, void *pContext, XAuint32 event) 93{ 94 // pContext is unused here, so we pass NULL 95 assert(pContext == NULL); 96 XApermille level = 0; 97 XAresult result; 98 result = (*caller)->GetFillLevel(caller, &level); 99 assert(XA_RESULT_SUCCESS == result); 100 XAuint32 status; 101 result = (*caller)->GetPrefetchStatus(caller, &status); 102 assert(XA_RESULT_SUCCESS == result); 103 if (event & XA_PREFETCHEVENT_FILLLEVELCHANGE) { 104 printf("PrefetchEventCallback: Buffer fill level is = %d\n", level); 105 } 106 if (event & XA_PREFETCHEVENT_STATUSCHANGE) { 107 printf("PrefetchEventCallback: Prefetch Status is = %u\n", status); 108 } 109 XAuint32 new_prefetch_status; 110 if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE)) 111 && (level == 0) && (status == XA_PREFETCHSTATUS_UNDERFLOW)) { 112 printf("PrefetchEventCallback: Error while prefetching data, exiting\n"); 113 new_prefetch_status = PREFETCHSTATUS_ERROR; 114 } else if (event == XA_PREFETCHEVENT_STATUSCHANGE) { 115 new_prefetch_status = status; 116 } else { 117 return; 118 } 119 int ok; 120 ok = pthread_mutex_lock(&mutex); 121 assert(ok == 0); 122 prefetch_status = new_prefetch_status; 123 ok = pthread_cond_signal(&cond); 124 assert(ok == 0); 125 ok = pthread_mutex_unlock(&mutex); 126 assert(ok == 0); 127} 128 129// playback event callback 130void playEventCallback(XAPlayItf caller, void *pContext, XAuint32 event) 131{ 132 // pContext is unused here, so we pass NULL 133 assert(NULL == pContext); 134 135 XAresult result; 136 XAmillisecond position; 137 result = (*caller)->GetPosition(caller, &position); 138 assert(XA_RESULT_SUCCESS == result); 139 140 if (XA_PLAYEVENT_HEADATEND & event) { 141 printf("XA_PLAYEVENT_HEADATEND current position=%u ms\n", position); 142 } 143 144 if (XA_PLAYEVENT_HEADATNEWPOS & event) { 145 printf("XA_PLAYEVENT_HEADATNEWPOS current position=%u ms\n", position); 146 } 147 148 if (XA_PLAYEVENT_HEADATMARKER & event) { 149 printf("XA_PLAYEVENT_HEADATMARKER current position=%u ms\n", position); 150 } 151} 152 153// Android buffer queue callback 154XAresult bufferQueueCallback( 155 XAAndroidBufferQueueItf caller, 156 void *pCallbackContext, 157 void *pBufferContext __unused, 158 void *pBufferData __unused, 159 XAuint32 dataSize __unused, 160 XAuint32 dataUsed __unused, 161 const XAAndroidBufferItem *pItems __unused, 162 XAuint32 itemsLength __unused) 163{ 164 XAPlayItf playerPlay = (XAPlayItf) pCallbackContext; 165 // enqueue the .ts data directly from mapped memory, so ignore the empty buffer pBufferData 166 if (curPacket <= lastPacket) { 167 static const XAAndroidBufferItem discontinuity = {XA_ANDROID_ITEMKEY_DISCONTINUITY, 0, {}}; 168 static const XAAndroidBufferItem eos = {XA_ANDROID_ITEMKEY_EOS, 0, {}}; 169 static const XAAndroidBufferItem formatChange = {XA_ANDROID_ITEMKEY_FORMAT_CHANGE, 0, {}}; 170 const XAAndroidBufferItem *items; 171 XAuint32 itemSize; 172 // compute number of packets to be enqueued in this buffer 173 XAuint32 packetsThisBuffer = lastPacket - curPacket; 174 if (packetsThisBuffer > PACKETS_PER_BUFFER) { 175 packetsThisBuffer = PACKETS_PER_BUFFER; 176 } 177 // last packet? this should only happen once 178 if (curPacket == lastPacket) { 179 if (sentEOS) { 180 printf("buffer completion callback after EOS\n"); 181 return XA_RESULT_SUCCESS; 182 } 183 printf("sending EOS\n"); 184 items = &eos; 185 itemSize = sizeof(eos); 186 sentEOS = XA_BOOLEAN_TRUE; 187 // discontinuity requested? 188 } else if (curPacket == discPacket) { 189 printf("sending discontinuity at packet %zu, then resuming at packet %zu\n", discPacket, 190 afterDiscPacket); 191 items = &discontinuity; 192 itemSize = sizeof(discontinuity); 193 curPacket = afterDiscPacket; 194 // format change requested? 195 } else if (curPacket == formatPacket) { 196 printf("sending format change"); 197 items = &formatChange; 198 itemSize = sizeof(formatChange); 199 // pure data with no items 200 } else { 201 items = NULL; 202 itemSize = 0; 203 } 204 XAresult result; 205 // enqueue the optional data and optional items; there is always at least one or the other 206 assert(packetsThisBuffer > 0 || itemSize > 0); 207 result = (*caller)->Enqueue(caller, NULL, &packets[curPacket], 208 sizeof(MPEG2TS_Packet) * packetsThisBuffer, items, itemSize); 209 assert(XA_RESULT_SUCCESS == result); 210 curPacket += packetsThisBuffer; 211 // display position periodically 212 if (curPacket % 1000 == 0) { 213 XAmillisecond position; 214 result = (*playerPlay)->GetPosition(playerPlay, &position); 215 assert(XA_RESULT_SUCCESS == result); 216 printf("Position after enqueueing packet %zu: %u ms\n", curPacket, position); 217 } 218 } 219 if (forceCallbackFailure && (curPacket % 1230 == 0)) { 220 return (XAresult) curPacket; 221 } else { 222 return XA_RESULT_SUCCESS; 223 } 224} 225 226// convert a domain type to string 227static const char *domainToString(XAuint32 domain) 228{ 229 switch (domain) { 230 case 0: // FIXME There's a private declaration '#define XA_DOMAINTYPE_CONTAINER 0' in src/data.h 231 // but we don't have access to it. Plan to file a bug with Khronos about this symbol. 232 return "media container"; 233#define _(x) case x: return #x; 234 _(XA_DOMAINTYPE_AUDIO) 235 _(XA_DOMAINTYPE_VIDEO) 236 _(XA_DOMAINTYPE_IMAGE) 237 _(XA_DOMAINTYPE_TIMEDTEXT) 238 _(XA_DOMAINTYPE_MIDI) 239 _(XA_DOMAINTYPE_VENDOR) 240 _(XA_DOMAINTYPE_UNKNOWN) 241#undef _ 242 default: 243 return "unknown"; 244 } 245} 246 247// main program 248int main(int argc, char **argv) 249{ 250 const char *prog = argv[0]; 251 int i; 252 253 XAboolean abq = XA_BOOLEAN_FALSE; // use AndroidBufferQueue, default is URI 254 XAboolean looping = XA_BOOLEAN_FALSE; 255 for (i = 1; i < argc; ++i) { 256 const char *arg = argv[i]; 257 if (arg[0] != '-') 258 break; 259 switch (arg[1]) { 260 case 'a': 261 abq = XA_BOOLEAN_TRUE; 262 break; 263 case 'c': 264 forceCallbackFailure = XA_BOOLEAN_TRUE; 265 break; 266 case 'd': 267 discPacket = atoi(&arg[2]); 268 break; 269 case 'D': 270 afterDiscPacket = atoi(&arg[2]); 271 break; 272 case 'f': 273 firstPacket = atoi(&arg[2]); 274 break; 275 case 'F': 276 formatPacket = atoi(&arg[2]); 277 break; 278 case 'l': 279 looping = XA_BOOLEAN_TRUE; 280 break; 281 case 'n': 282 numPackets = atoi(&arg[2]); 283 break; 284 case 'p': 285 pauseMs = atoi(&arg[2]); 286 break; 287 case 's': 288 seekPos = atoi(&arg[2]); 289 break; 290 default: 291 fprintf(stderr, "%s: unknown option %s\n", prog, arg); 292 break; 293 } 294 } 295 296 // check that exactly one URI was specified 297 if (argc - i != 1) { 298 fprintf(stderr, "usage: %s [-a] [-c] [-d#] [-D#] [-f#] [-F#] [-l] [-n#] [-p#] [-s#] uri\n", 299 prog); 300 fprintf(stderr, " -a Use Android buffer queue to supply data, default is URI\n"); 301 fprintf(stderr, " -c Force callback to return an error randomly, for debugging only\n"); 302 fprintf(stderr, " -d# Packet index to insert a discontinuity, default is none\n"); 303 fprintf(stderr, " -D# Packet index to switch to after the discontinuity\n"); 304 fprintf(stderr, " -f# First packet index, defaults to 0\n"); 305 fprintf(stderr, " -F# Packet index to insert a format change, default is none\n"); 306 fprintf(stderr, " -l Enable looping, for URI only\n"); 307 fprintf(stderr, " -n# Number of packets to enqueue\n"); 308 fprintf(stderr, " -p# Pause playback for 5 seconds after this many milliseconds\n"); 309 fprintf(stderr, " -s# Seek position in milliseconds, for URI only\n"); 310 return EXIT_FAILURE; 311 } 312 const char *uri = argv[i]; 313 314 // for AndroidBufferQueue, interpret URI as a filename and open 315 int fd = -1; 316 if (abq) { 317 fd = open(uri, O_RDONLY); 318 if (fd < 0) { 319 perror(uri); 320 goto close; 321 } 322 int ok; 323 struct stat statbuf; 324 ok = fstat(fd, &statbuf); 325 if (ok < 0) { 326 perror(uri); 327 goto close; 328 } 329 if (!S_ISREG(statbuf.st_mode)) { 330 fprintf(stderr, "%s: not an ordinary file\n", uri); 331 goto close; 332 } 333 void *ptr; 334 ptr = mmap(NULL, statbuf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, (off_t) 0); 335 if (ptr == MAP_FAILED) { 336 perror(uri); 337 goto close; 338 } 339 size_t filelen = statbuf.st_size; 340 if ((filelen % MPEG2TS_PACKET_SIZE) != 0) { 341 fprintf(stderr, "%s: warning file length %zu is not a multiple of %d\n", uri, filelen, 342 MPEG2TS_PACKET_SIZE); 343 } 344 packets = (MPEG2TS_Packet *) ptr; 345 totalPackets = filelen / MPEG2TS_PACKET_SIZE; 346 printf("%s has %zu total packets\n", uri, totalPackets); 347 if (firstPacket >= totalPackets) { 348 fprintf(stderr, "-f%zu ignored\n", firstPacket); 349 firstPacket = 0; 350 } 351 if (numPackets == 0) { 352 numPackets = totalPackets - firstPacket; 353 } else if (firstPacket + numPackets > totalPackets) { 354 fprintf(stderr, "-n%zu ignored\n", numPackets); 355 numPackets = totalPackets - firstPacket; 356 } 357 lastPacket = firstPacket + numPackets; 358 if (discPacket != 0 && (discPacket < firstPacket || discPacket >= lastPacket)) { 359 fprintf(stderr, "-d%zu ignored\n", discPacket); 360 discPacket = 0; 361 } 362 if (afterDiscPacket < firstPacket || afterDiscPacket >= lastPacket) { 363 fprintf(stderr, "-D%zu ignored\n", afterDiscPacket); 364 afterDiscPacket = 0; 365 } 366 if (formatPacket != 0 && (formatPacket < firstPacket || formatPacket >= lastPacket)) { 367 fprintf(stderr, "-F%zu ignored\n", formatPacket); 368 formatPacket = 0; 369 } 370 } 371 372 ANativeWindow *nativeWindow; 373 374 XAresult result; 375 XAObjectItf engineObject; 376 377 // create engine 378 result = xaCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); 379 assert(XA_RESULT_SUCCESS == result); 380 result = (*engineObject)->Realize(engineObject, XA_BOOLEAN_FALSE); 381 assert(XA_RESULT_SUCCESS == result); 382 XAEngineItf engineEngine; 383 result = (*engineObject)->GetInterface(engineObject, XA_IID_ENGINE, &engineEngine); 384 assert(XA_RESULT_SUCCESS == result); 385 386 // create output mix 387 XAObjectItf outputMixObject; 388 result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL); 389 assert(XA_RESULT_SUCCESS == result); 390 result = (*outputMixObject)->Realize(outputMixObject, XA_BOOLEAN_FALSE); 391 assert(XA_RESULT_SUCCESS == result); 392 393 // configure media source 394 XADataLocator_URI locUri; 395 locUri.locatorType = XA_DATALOCATOR_URI; 396 locUri.URI = (XAchar *) uri; 397 XADataFormat_MIME fmtMime; 398 fmtMime.formatType = XA_DATAFORMAT_MIME; 399 if (abq) { 400 fmtMime.mimeType = (XAchar *) XA_ANDROID_MIME_MP2TS; 401 fmtMime.containerType = XA_CONTAINERTYPE_MPEG_TS; 402 } else { 403 fmtMime.mimeType = NULL; 404 fmtMime.containerType = XA_CONTAINERTYPE_UNSPECIFIED; 405 } 406 XADataLocator_AndroidBufferQueue locABQ; 407 locABQ.locatorType = XA_DATALOCATOR_ANDROIDBUFFERQUEUE; 408 locABQ.numBuffers = NB_BUFFERS; 409 XADataSource dataSrc; 410 if (abq) { 411 dataSrc.pLocator = &locABQ; 412 } else { 413 dataSrc.pLocator = &locUri; 414 } 415 dataSrc.pFormat = &fmtMime; 416 417 // configure audio sink 418 XADataLocator_OutputMix locOM; 419 locOM.locatorType = XA_DATALOCATOR_OUTPUTMIX; 420 locOM.outputMix = outputMixObject; 421 XADataSink audioSnk; 422 audioSnk.pLocator = &locOM; 423 audioSnk.pFormat = NULL; 424 425 // configure video sink 426 nativeWindow = getNativeWindow(); 427 XADataLocator_NativeDisplay locND; 428 locND.locatorType = XA_DATALOCATOR_NATIVEDISPLAY; 429 locND.hWindow = nativeWindow; 430 locND.hDisplay = NULL; 431 XADataSink imageVideoSink; 432 imageVideoSink.pLocator = &locND; 433 imageVideoSink.pFormat = NULL; 434 435 // create media player 436 XAObjectItf playerObject; 437 XAInterfaceID ids[4] = {XA_IID_STREAMINFORMATION, XA_IID_PREFETCHSTATUS, XA_IID_SEEK, 438 XA_IID_ANDROIDBUFFERQUEUESOURCE}; 439 XAboolean req[4] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE, XA_BOOLEAN_FALSE, XA_BOOLEAN_TRUE}; 440 result = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObject, &dataSrc, NULL, 441 &audioSnk, nativeWindow != NULL ? &imageVideoSink : NULL, NULL, NULL, abq ? 4 : 3, ids, 442 req); 443 assert(XA_RESULT_SUCCESS == result); 444 445 // realize the player 446 result = (*playerObject)->Realize(playerObject, XA_BOOLEAN_FALSE); 447 assert(XA_RESULT_SUCCESS == result); 448 449 // get the play interface 450 XAPlayItf playerPlay; 451 result = (*playerObject)->GetInterface(playerObject, XA_IID_PLAY, &playerPlay); 452 assert(XA_RESULT_SUCCESS == result); 453 454 if (abq) { 455 456 // get the Android buffer queue interface 457 XAAndroidBufferQueueItf playerAndroidBufferQueue; 458 result = (*playerObject)->GetInterface(playerObject, XA_IID_ANDROIDBUFFERQUEUESOURCE, 459 &playerAndroidBufferQueue); 460 assert(XA_RESULT_SUCCESS == result); 461 462 // register the buffer queue callback 463 result = (*playerAndroidBufferQueue)->RegisterCallback(playerAndroidBufferQueue, 464 bufferQueueCallback, (void *) playerPlay); 465 assert(XA_RESULT_SUCCESS == result); 466 result = (*playerAndroidBufferQueue)->SetCallbackEventsMask(playerAndroidBufferQueue, 467 XA_ANDROIDBUFFERQUEUEEVENT_PROCESSED); 468 assert(XA_RESULT_SUCCESS == result); 469 470 // set the player's state to paused, to start prefetching 471 printf("start early prefetch\n"); 472 result = (*playerPlay)->SetPlayState(playerPlay, XA_PLAYSTATE_PAUSED); 473 assert(XA_RESULT_SUCCESS == result); 474 475 // enqueue the initial buffers until buffer queue is full 476 XAuint32 packetsThisBuffer; 477 for (curPacket = firstPacket; curPacket < lastPacket; curPacket += packetsThisBuffer) { 478 // handle the unlikely case of a very short .ts 479 packetsThisBuffer = lastPacket - curPacket; 480 if (packetsThisBuffer > PACKETS_PER_BUFFER) { 481 packetsThisBuffer = PACKETS_PER_BUFFER; 482 } 483 result = (*playerAndroidBufferQueue)->Enqueue(playerAndroidBufferQueue, NULL, 484 &packets[curPacket], MPEG2TS_PACKET_SIZE * packetsThisBuffer, NULL, 0); 485 if (XA_RESULT_BUFFER_INSUFFICIENT == result) { 486 printf("Enqueued initial %zu packets in %zu buffers\n", curPacket - firstPacket, 487 (curPacket - firstPacket + PACKETS_PER_BUFFER - 1) / PACKETS_PER_BUFFER); 488 break; 489 } 490 assert(XA_RESULT_SUCCESS == result); 491 } 492 493 } 494 495 // get the stream information interface 496 XAStreamInformationItf playerStreamInformation; 497 result = (*playerObject)->GetInterface(playerObject, XA_IID_STREAMINFORMATION, 498 &playerStreamInformation); 499 assert(XA_RESULT_SUCCESS == result); 500 501 // register the stream event change callback 502 result = (*playerStreamInformation)->RegisterStreamChangeCallback(playerStreamInformation, 503 streamEventChangeCallback, NULL); 504 assert(XA_RESULT_SUCCESS == result); 505 506 // get the prefetch status interface 507 XAPrefetchStatusItf playerPrefetchStatus; 508 result = (*playerObject)->GetInterface(playerObject, XA_IID_PREFETCHSTATUS, 509 &playerPrefetchStatus); 510 assert(XA_RESULT_SUCCESS == result); 511 512 // register prefetch status callback 513 result = (*playerPrefetchStatus)->RegisterCallback(playerPrefetchStatus, prefetchStatusCallback, 514 NULL); 515 assert(XA_RESULT_SUCCESS == result); 516 result = (*playerPrefetchStatus)->SetCallbackEventsMask(playerPrefetchStatus, 517 XA_PREFETCHEVENT_FILLLEVELCHANGE | XA_PREFETCHEVENT_STATUSCHANGE); 518 assert(XA_RESULT_SUCCESS == result); 519 520 // get the seek interface for seeking and/or looping 521 if (looping || seekPos != XA_TIME_UNKNOWN) { 522 XASeekItf playerSeek; 523 result = (*playerObject)->GetInterface(playerObject, XA_IID_SEEK, &playerSeek); 524 assert(XA_RESULT_SUCCESS == result); 525 if (seekPos != XA_TIME_UNKNOWN) { 526 result = (*playerSeek)->SetPosition(playerSeek, seekPos, XA_SEEKMODE_ACCURATE); 527 if (XA_RESULT_FEATURE_UNSUPPORTED == result) { 528 fprintf(stderr, "-s%u (seek to initial position) is unsupported\n", seekPos); 529 } else { 530 assert(XA_RESULT_SUCCESS == result); 531 } 532 } 533 if (looping) { 534 result = (*playerSeek)->SetLoop(playerSeek, XA_BOOLEAN_TRUE, (XAmillisecond) 0, 535 XA_TIME_UNKNOWN); 536 if (XA_RESULT_FEATURE_UNSUPPORTED) { 537 fprintf(stderr, "-l (looping) is unsupported\n"); 538 } else { 539 assert(XA_RESULT_SUCCESS == result); 540 } 541 } 542 } 543 544 // register play event callback 545 result = (*playerPlay)->RegisterCallback(playerPlay, playEventCallback, NULL); 546 assert(XA_RESULT_SUCCESS == result); 547 result = (*playerPlay)->SetCallbackEventsMask(playerPlay, 548 XA_PLAYEVENT_HEADATEND | XA_PLAYEVENT_HEADATMARKER | XA_PLAYEVENT_HEADATNEWPOS); 549 assert(XA_RESULT_SUCCESS == result); 550 551 // set a marker 552 result = (*playerPlay)->SetMarkerPosition(playerPlay, 5000); 553 assert(XA_RESULT_SUCCESS == result); 554 555 // set position update period 556 result = (*playerPlay)->SetPositionUpdatePeriod(playerPlay, 2000); 557 assert(XA_RESULT_SUCCESS == result); 558 559 // get the position before prefetch 560 XAmillisecond position; 561 result = (*playerPlay)->GetPosition(playerPlay, &position); 562 assert(XA_RESULT_SUCCESS == result); 563 printf("Position before prefetch: %u ms\n", position); 564 565 // get the duration before prefetch 566 XAmillisecond duration; 567 result = (*playerPlay)->GetDuration(playerPlay, &duration); 568 assert(XA_RESULT_SUCCESS == result); 569 if (XA_TIME_UNKNOWN == duration) 570 printf("Duration before prefetch: unknown as expected\n"); 571 else 572 printf("Duration before prefetch: %.1f (surprise!)\n", duration / 1000.0f); 573 574 // set the player's state to paused, to start prefetching 575 printf("start prefetch\n"); 576 result = (*playerPlay)->SetPlayState(playerPlay, XA_PLAYSTATE_PAUSED); 577 assert(XA_RESULT_SUCCESS == result); 578 579 // wait for prefetch status callback to indicate either sufficient data or error 580 pthread_mutex_lock(&mutex); 581 while (prefetch_status == PREFETCHSTATUS_UNKNOWN) { 582 pthread_cond_wait(&cond, &mutex); 583 } 584 pthread_mutex_unlock(&mutex); 585 if (prefetch_status == PREFETCHSTATUS_ERROR) { 586 fprintf(stderr, "Error during prefetch, exiting\n"); 587 goto destroyRes; 588 } 589 590 // get the position after prefetch 591 result = (*playerPlay)->GetPosition(playerPlay, &position); 592 assert(XA_RESULT_SUCCESS == result); 593 printf("Position after prefetch: %u ms\n", position); 594 595 // get duration again, now it should be known for the file source or unknown for TS 596 result = (*playerPlay)->GetDuration(playerPlay, &duration); 597 assert(XA_RESULT_SUCCESS == result); 598 if (duration == XA_TIME_UNKNOWN) { 599 printf("Duration after prefetch: unknown (expected for TS, unexpected for file)\n"); 600 } else { 601 printf("Duration after prefetch: %u ms (expected for file, unexpected for TS)\n", duration); 602 } 603 604 // query for media container information 605 result = (*playerStreamInformation)->QueryMediaContainerInformation(playerStreamInformation, 606 NULL); 607 assert(XA_RESULT_PARAMETER_INVALID == result); 608 XAMediaContainerInformation mediaContainerInformation; 609 // this verifies it is filling in all the fields 610 memset(&mediaContainerInformation, 0x55, sizeof(XAMediaContainerInformation)); 611 result = (*playerStreamInformation)->QueryMediaContainerInformation(playerStreamInformation, 612 &mediaContainerInformation); 613 assert(XA_RESULT_SUCCESS == result); 614 printf("Media container information:\n"); 615 printf(" containerType = %u\n", mediaContainerInformation.containerType); 616 printf(" mediaDuration = %u\n", mediaContainerInformation.mediaDuration); 617 printf(" numStreams = %u\n", mediaContainerInformation.numStreams); 618 619 // Now query for each the streams. Note that stream indices go up to and including 620 // mediaContainerInformation.numStreams, because stream 0 is the container itself, 621 // while stream 1 to mediaContainerInformation.numStreams are the contained streams. 622 XAuint32 streamIndex; 623 for (streamIndex = 0; streamIndex <= mediaContainerInformation.numStreams; ++streamIndex) { 624 XAuint32 domain; 625 XAuint16 nameSize; 626 XAchar name[64]; 627 printf("stream[%u]:\n", streamIndex); 628 if (streamIndex == 0) { 629 result = (*playerStreamInformation)->QueryStreamType(playerStreamInformation, 630 streamIndex, &domain); 631 assert(XA_RESULT_PARAMETER_INVALID == result); 632 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 633 streamIndex, &mediaContainerInformation); 634 //assert(XA_RESULT_PARAMETER_INVALID == result); 635 nameSize = sizeof(name); 636 result = (*playerStreamInformation)->QueryStreamName(playerStreamInformation, 637streamIndex, &nameSize, name); 638 //assert(XA_RESULT_PARAMETER_INVALID == result); 639 continue; 640 } 641 result = (*playerStreamInformation)->QueryStreamType(playerStreamInformation, streamIndex, 642 NULL); 643 assert(XA_RESULT_PARAMETER_INVALID == result); 644 domain = 12345; 645 result = (*playerStreamInformation)->QueryStreamType(playerStreamInformation, streamIndex, 646 &domain); 647 assert(XA_RESULT_SUCCESS == result); 648 printf(" QueryStreamType: domain = 0x%X (%s)\n", domain, domainToString(domain)); 649 nameSize = sizeof(name); 650 result = (*playerStreamInformation)->QueryStreamName(playerStreamInformation, streamIndex, 651 &nameSize, name); 652#if 0 653 assert(XA_RESULT_SUCCESS == result); 654 assert(sizeof(name) >= nameSize); 655 if (sizeof(name) != nameSize) { 656 assert('\0' == name[nameSize]); 657 } 658 printf(" QueryStreamName: nameSize=%u, name=\"%.*s\"\n", nameSize, nameSize, name); 659 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 660 streamIndex, NULL); 661 assert(XA_RESULT_PARAMETER_INVALID == result); 662#endif 663 664 printf(" QueryStreamInformation:\n"); 665 switch (domain) { 666#if 0 667 case 0: // FIXME container 668 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 669streamIndex, &mediaContainerInformation); 670 assert(XA_RESULT_SUCCESS == result); 671 printf(" containerType = %u (1=unspecified)\n", 672 mediaContainerInformation.containerType); 673 printf(" mediaDuration = %u\n", mediaContainerInformation.mediaDuration); 674 printf(" numStreams = %u\n", mediaContainerInformation.numStreams); 675 break; 676#endif 677 case XA_DOMAINTYPE_AUDIO: { 678 XAAudioStreamInformation audioStreamInformation; 679 memset(&audioStreamInformation, 0x55, sizeof(XAAudioStreamInformation)); 680 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 681 streamIndex, &audioStreamInformation); 682 assert(XA_RESULT_PARAMETER_INVALID == result); 683 printf(" codecId = %u\n", audioStreamInformation.codecId); 684 printf(" channels = %u\n", audioStreamInformation.channels); 685 printf(" sampleRate = %u\n", audioStreamInformation.sampleRate); 686 printf(" bitRate = %u\n", audioStreamInformation.bitRate); 687 printf(" langCountry = \"%s\"\n", audioStreamInformation.langCountry); 688 printf(" duration = %u\n", audioStreamInformation.duration); 689 } break; 690 case XA_DOMAINTYPE_VIDEO: { 691 XAVideoStreamInformation videoStreamInformation; 692 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 693 streamIndex, &videoStreamInformation); 694 assert(XA_RESULT_SUCCESS == result); 695 printf(" codecId = %u\n", videoStreamInformation.codecId); 696 printf(" width = %u\n", videoStreamInformation.width); 697 printf(" height = %u\n", videoStreamInformation.height); 698 printf(" frameRate = %u\n", videoStreamInformation.frameRate); 699 printf(" bitRate = %u\n", videoStreamInformation.bitRate); 700 printf(" duration = %u\n", videoStreamInformation.duration); 701 } break; 702 case XA_DOMAINTYPE_IMAGE: { 703 XAImageStreamInformation imageStreamInformation; 704 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 705 streamIndex, &imageStreamInformation); 706 assert(XA_RESULT_SUCCESS == result); 707 printf(" codecId = %u\n", imageStreamInformation.codecId); 708 printf(" width = %u\n", imageStreamInformation.width); 709 printf(" height = %u\n", imageStreamInformation.height); 710 printf(" presentationDuration = %u\n", imageStreamInformation.presentationDuration); 711 } break; 712 case XA_DOMAINTYPE_TIMEDTEXT: { 713 XATimedTextStreamInformation timedTextStreamInformation; 714 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 715 streamIndex, &timedTextStreamInformation); 716 assert(XA_RESULT_SUCCESS == result); 717 printf(" layer = %u\n", timedTextStreamInformation.layer); 718 printf(" width = %u\n", timedTextStreamInformation.width); 719 printf(" height = %u\n", timedTextStreamInformation.height); 720 printf(" tx = %u\n", timedTextStreamInformation.tx); 721 printf(" ty = %u\n", timedTextStreamInformation.ty); 722 printf(" bitrate = %u\n", timedTextStreamInformation.bitrate); 723 printf(" langCountry = \"%s\"\n", timedTextStreamInformation.langCountry); 724 printf(" duration = %u\n", timedTextStreamInformation.duration); 725 } break; 726 case XA_DOMAINTYPE_MIDI: { 727 XAMIDIStreamInformation midiStreamInformation; 728 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 729 streamIndex, &midiStreamInformation); 730 assert(XA_RESULT_SUCCESS == result); 731 printf(" channels = %u\n", midiStreamInformation.channels); 732 printf(" tracks = %u\n", midiStreamInformation.tracks); 733 printf(" bankType = %u\n", midiStreamInformation.bankType); 734 printf(" langCountry = \"%s\"\n", midiStreamInformation.langCountry); 735 printf(" duration = %u\n", midiStreamInformation.duration); 736 } break; 737 case XA_DOMAINTYPE_VENDOR: { 738 XAVendorStreamInformation vendorStreamInformation; 739 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 740 streamIndex, &vendorStreamInformation); 741 assert(XA_RESULT_SUCCESS == result); 742 printf(" VendorStreamInfo = %p\n", vendorStreamInformation.VendorStreamInfo); 743 } break; 744 case XA_DOMAINTYPE_UNKNOWN: { 745 // "It is not possible to query Information for streams identified as 746 // XA_DOMAINTYPE_UNKNOWN, any attempt to do so shall return a result of 747 // XA_RESULT_CONTENT_UNSUPPORTED." 748 char big[256]; 749 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 750 streamIndex, &big); 751 assert(XA_RESULT_CONTENT_UNSUPPORTED == result); 752 } break; 753 default: 754 break; 755 } 756 757 } 758 // Try one more stream index beyond the valid range 759 XAuint32 domain; 760 result = (*playerStreamInformation)->QueryStreamType(playerStreamInformation, streamIndex, 761 &domain); 762 assert(XA_RESULT_PARAMETER_INVALID == result); 763 XATimedTextStreamInformation big; 764 result = (*playerStreamInformation)->QueryStreamInformation(playerStreamInformation, 765 streamIndex, &big); 766 assert(XA_RESULT_PARAMETER_INVALID == result); 767 768 printf("QueryActiveStreams:\n"); 769 result = (*playerStreamInformation)->QueryActiveStreams(playerStreamInformation, NULL, NULL); 770 assert(XA_RESULT_PARAMETER_INVALID == result); 771 XAuint32 numStreams1 = 0x12345678; 772 result = (*playerStreamInformation)->QueryActiveStreams(playerStreamInformation, &numStreams1, 773 NULL); 774 assert(XA_RESULT_SUCCESS == result); 775 printf(" numStreams = %u\n", numStreams1); 776 XAboolean *activeStreams = calloc(numStreams1 + 1, sizeof(XAboolean)); 777 assert(NULL != activeStreams); 778 printf(" active stream(s) ="); 779 XAuint32 numStreams2 = numStreams1; 780 result = (*playerStreamInformation)->QueryActiveStreams(playerStreamInformation, &numStreams2, 781 activeStreams); 782 assert(XA_RESULT_SUCCESS == result); 783 assert(numStreams2 == numStreams1); 784 for (streamIndex = 0; streamIndex <= numStreams1; ++streamIndex) { 785 if (activeStreams[streamIndex]) 786 printf(" %u", streamIndex); 787 } 788 printf("\n"); 789 790 // SetActiveStream is untested 791 792 // start playing 793 printf("starting to play\n"); 794 result = (*playerPlay)->SetPlayState(playerPlay, XA_PLAYSTATE_PLAYING); 795 assert(XA_RESULT_SUCCESS == result); 796 797 // continue playing until end of media 798 for (;;) { 799 XAuint32 status; 800 result = (*playerPlay)->GetPlayState(playerPlay, &status); 801 assert(XA_RESULT_SUCCESS == result); 802 if (status == XA_PLAYSTATE_PAUSED) 803 break; 804 assert(status == XA_PLAYSTATE_PLAYING); 805 usleep(100000); 806 if (pauseMs >= 0) { 807 result = (*playerPlay)->GetPosition(playerPlay, &position); 808 assert(XA_RESULT_SUCCESS == result); 809 if ((int) position >= pauseMs) { 810 printf("Pausing for 5 seconds at position %u\n", position); 811 result = (*playerPlay)->SetPlayState(playerPlay, XA_PLAYSTATE_PAUSED); 812 assert(XA_RESULT_SUCCESS == result); 813 sleep(5); 814 // FIXME clear ABQ queue here 815 result = (*playerPlay)->SetPlayState(playerPlay, XA_PLAYSTATE_PLAYING); 816 assert(XA_RESULT_SUCCESS == result); 817 pauseMs = -1; 818 } 819 } 820 } 821 822 // wait a bit more in case of additional callbacks 823 printf("end of media\n"); 824 sleep(3); 825 826 // get final position 827 result = (*playerPlay)->GetPosition(playerPlay, &position); 828 assert(XA_RESULT_SUCCESS == result); 829 printf("Position at end: %u ms\n", position); 830 831 // get duration again, now it should be known 832 result = (*playerPlay)->GetDuration(playerPlay, &duration); 833 assert(XA_RESULT_SUCCESS == result); 834 if (duration == XA_TIME_UNKNOWN) { 835 printf("Duration at end: unknown\n"); 836 } else { 837 printf("Duration at end: %u ms\n", duration); 838 } 839 840destroyRes: 841 842 // destroy the player 843 (*playerObject)->Destroy(playerObject); 844 845 // destroy the output mix 846 (*outputMixObject)->Destroy(outputMixObject); 847 848 // destroy the engine 849 (*engineObject)->Destroy(engineObject); 850 851#if 0 852 if (nativeWindow != NULL) { 853 ANativeWindow_release(nativeWindow); 854 } 855#endif 856 857close: 858 if (fd >= 0) { 859 (void) close(fd); 860 } 861 862 disposeNativeWindow(); 863 864 return EXIT_SUCCESS; 865} 866