Home | History | Annotate | Download | only in sandbox
      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
     38 typedef struct {
     39     char data[MPEG2TS_PACKET_SIZE];
     40 } MPEG2TS_Packet;
     41 
     42 // Globals shared between main thread and buffer queue callback
     43 MPEG2TS_Packet *packets;
     44 size_t totalPackets;    // total number of packets in input file
     45 size_t numPackets;      // number of packets to play, defaults to totalPackets - firstPacket
     46 size_t curPacket;       // current packet index
     47 size_t discPacket;      // discontinuity packet index, defaults to no discontinuity requested
     48 size_t afterDiscPacket; // packet index to switch to after the discontinuity
     49 size_t firstPacket;     // first packet index to be played, defaults to zero
     50 size_t lastPacket;      // last packet index to be played
     51 size_t formatPacket;    // format change packet index, defaults to no format change requested
     52 XAmillisecond seekPos = XA_TIME_UNKNOWN;    // seek to this position initially
     53 int pauseMs = -1;       // pause after this many ms into playback
     54 XAboolean forceCallbackFailure = XA_BOOLEAN_FALSE;  // force callback failures occasionally
     55 XAboolean 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 
     64 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
     65 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
     66 XAuint32 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
     75 void 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
     92 void 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
    130 void 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
    154 XAresult 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
    227 static 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
    248 int 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,
    637 streamIndex, &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,
    669 streamIndex, &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 
    840 destroyRes:
    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 
    857 close:
    858     if (fd >= 0) {
    859         (void) close(fd);
    860     }
    861 
    862     disposeNativeWindow();
    863 
    864     return EXIT_SUCCESS;
    865 }
    866