1cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten/*
2cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * Copyright (C) 2011 The Android Open Source Project
3cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten *
4cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * Licensed under the Apache License, Version 2.0 (the "License");
5cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * you may not use this file except in compliance with the License.
6cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * You may obtain a copy of the License at
7cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten *
8cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten *      http://www.apache.org/licenses/LICENSE-2.0
9cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten *
10cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * Unless required by applicable law or agreed to in writing, software
11cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * distributed under the License is distributed on an "AS IS" BASIS,
12cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * See the License for the specific language governing permissions and
14cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * limitations under the License.
15cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten */
16cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
17cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// OpenMAX AL MediaPlayer command-line player
18cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
19cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <assert.h>
20cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <pthread.h>
21cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <stdio.h>
22cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <stdlib.h>
23cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <fcntl.h>
24cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <sys/mman.h>
25cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <sys/stat.h>
26cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <unistd.h>
27cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <OMXAL/OpenMAXAL.h>
28cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include <OMXAL/OpenMAXAL_Android.h>
29cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#include "nativewindow.h"
30cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
31cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#define MPEG2TS_PACKET_SIZE 188  // MPEG-2 transport stream packet size in bytes
32cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#define PACKETS_PER_BUFFER 20    // Number of MPEG-2 transport stream packets per buffer
33cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
34cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#define NB_BUFFERS 2    // Number of buffers in Android buffer queue
35cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
36cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// MPEG-2 transport stream packet
37cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastentypedef struct {
38cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    char data[MPEG2TS_PACKET_SIZE];
39cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten} MPEG2TS_Packet;
40cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
41cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#if 0
42cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// Each buffer in Android buffer queue
43cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastentypedef struct {
44cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    MPEG2TS_Packet packets[PACKETS_PER_BUFFER];
45cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten} Buffer;
46cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#endif
47cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
48cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// Globals shared between main thread and buffer queue callback
49cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn KastenMPEG2TS_Packet *packets;
50cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastensize_t numPackets;
51cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastensize_t curPacket;
52cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastensize_t discPacket;
53cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
54cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// These are extensions to OpenMAX AL 1.0.1 values
55cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
56cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#define PREFETCHSTATUS_UNKNOWN ((XAuint32) 0)
57cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#define PREFETCHSTATUS_ERROR   ((XAuint32) (-1))
58cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
59cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// Mutex and condition shared with main program to protect prefetch_status
60cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
61cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastenstatic pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
62cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastenstatic pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
63cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn KastenXAuint32 prefetch_status = PREFETCHSTATUS_UNKNOWN;
64cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
65cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten/* used to detect errors likely to have occured when the OpenMAX AL framework fails to open
66cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten * a resource, for instance because a file URI is invalid, or an HTTP server doesn't respond.
67cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten */
68cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#define PREFETCHEVENT_ERROR_CANDIDATE \
69cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        (XA_PREFETCHEVENT_STATUSCHANGE | XA_PREFETCHEVENT_FILLLEVELCHANGE)
70cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
71cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// stream event change callback
72cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastenvoid streamEventChangeCallback(XAStreamInformationItf caller, XAuint32 eventId,
73cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAuint32 streamIndex, void *pEventData, void *pContext)
74cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten{
75cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // context parameter is specified as NULL and is unused here
76cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(NULL == pContext);
77cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    switch (eventId) {
78cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    case XA_STREAMCBEVENT_PROPERTYCHANGE:
79cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("XA_STREAMCBEVENT_PROPERTYCHANGE on stream index %u, pEventData %p\n", streamIndex,
80cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                pEventData);
81cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        break;
82cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    default:
83cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("Unknown stream event ID %u\n", eventId);
84cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        break;
85cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
86cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten}
87cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
88cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// prefetch status callback
89cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastenvoid prefetchStatusCallback(XAPrefetchStatusItf caller,  void *pContext, XAuint32 event)
90cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten{
91cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // pContext is unused here, so we pass NULL
92cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(pContext == NULL);
93cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XApermille level = 0;
94cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAresult result;
95cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*caller)->GetFillLevel(caller, &level);
96cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
97cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAuint32 status;
98cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*caller)->GetPrefetchStatus(caller, &status);
99cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
100cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (event & XA_PREFETCHEVENT_FILLLEVELCHANGE) {
101cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("PrefetchEventCallback: Buffer fill level is = %d\n", level);
102cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
103cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (event & XA_PREFETCHEVENT_STATUSCHANGE) {
104cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("PrefetchEventCallback: Prefetch Status is = %u\n", status);
105cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
106cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAuint32 new_prefetch_status;
107cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE))
108cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            && (level == 0) && (status == XA_PREFETCHSTATUS_UNDERFLOW)) {
109cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("PrefetchEventCallback: Error while prefetching data, exiting\n");
110cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        new_prefetch_status = PREFETCHSTATUS_ERROR;
111cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    } else if (event == XA_PREFETCHEVENT_STATUSCHANGE) {
112cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        new_prefetch_status = status;
113cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    } else {
114cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        return;
115cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
116cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    int ok;
117cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    ok = pthread_mutex_lock(&mutex);
118cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(ok == 0);
119cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    prefetch_status = new_prefetch_status;
120cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    ok = pthread_cond_signal(&cond);
121cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(ok == 0);
122cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    ok = pthread_mutex_unlock(&mutex);
123cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(ok == 0);
124cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten}
125cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
126cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// playback event callback
127cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastenvoid playEventCallback(XAPlayItf caller, void *pContext, XAuint32 event)
128cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten{
129cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // pContext is unused here, so we pass NULL
130cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(NULL == pContext);
131cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
132cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAmillisecond position;
133cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
134cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (XA_PLAYEVENT_HEADATEND & event) {
135cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("XA_PLAYEVENT_HEADATEND reached\n");
136cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        //SignalEos();
137cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
138cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
139cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAresult result;
140cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (XA_PLAYEVENT_HEADATNEWPOS & event) {
141cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        result = (*caller)->GetPosition(caller, &position);
142cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(XA_RESULT_SUCCESS == result);
143cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("XA_PLAYEVENT_HEADATNEWPOS current position=%ums\n", position);
144cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
145cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
146cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (XA_PLAYEVENT_HEADATMARKER & event) {
147cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        result = (*caller)->GetPosition(caller, &position);
148cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(XA_RESULT_SUCCESS == result);
149cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("XA_PLAYEVENT_HEADATMARKER current position=%ums\n", position);
150cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
151cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten}
152cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
153cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// Android buffer queue callback
154cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn KastenXAresult bufferQueueCallback(
155cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAAndroidBufferQueueItf caller,
156cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        void *pCallbackContext,
157cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        void *pBufferContext,
158cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        void *pBufferData,
159cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAuint32 dataSize,
160cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAuint32 dataUsed,
161cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        const XAAndroidBufferItem *pItems,
162cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAuint32 itemsLength)
163cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten{
164cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // enqueue the .ts data directly from mapped memory, so ignore the empty buffer pBufferData
165cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (curPacket <= numPackets) {
166cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        static const XAAndroidBufferItem discontinuity = {XA_ANDROID_ITEMKEY_DISCONTINUITY, 0};
167cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        static const XAAndroidBufferItem eos = {XA_ANDROID_ITEMKEY_EOS, 0};
168cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        const XAAndroidBufferItem *items;
169cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAuint32 itemSize;
170cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        // compute number of packets to be enqueued in this buffer
171cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAuint32 packetsThisBuffer = numPackets - curPacket;
172cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        if (packetsThisBuffer > PACKETS_PER_BUFFER) {
173cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            packetsThisBuffer = PACKETS_PER_BUFFER;
174cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        }
175cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        // last packet? this should only happen once
176cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        if (curPacket == numPackets) {
177cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            (void) write(1, "e", 1);
178cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            items = &eos;
179cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            itemSize = sizeof(eos);
180cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        // discontinuity requested?
181cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        } else if (curPacket == discPacket) {
182cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            printf("sending discontinuity, rewinding from beginning of stream\n");
183cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            items = &discontinuity;
184cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            itemSize = sizeof(discontinuity);
185cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            curPacket = 0;
186cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        // pure data with no items
187cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        } else {
188cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            items = NULL;
189cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            itemSize = 0;
190cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        }
191cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAresult result;
192cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        // enqueue the optional data and optional items; there is always at least one or the other
193cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(packetsThisBuffer > 0 || itemSize > 0);
194cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        result = (*caller)->Enqueue(caller, NULL, &packets[curPacket],
195cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                sizeof(MPEG2TS_Packet) * packetsThisBuffer, items, itemSize);
196cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(XA_RESULT_SUCCESS == result);
197cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        curPacket += packetsThisBuffer;
198cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
199cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    return XA_RESULT_SUCCESS;
200cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten}
201cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
202cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten// main program
203cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastenint main(int argc, char **argv)
204cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten{
205cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    const char *prog = argv[0];
206cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    int i;
207cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
208cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAboolean abq = XA_BOOLEAN_FALSE;   // use AndroidBufferQueue, default is URI
209cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAboolean looping = XA_BOOLEAN_FALSE;
210cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#ifdef REINITIALIZE
211cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    int reinit_counter = 0;
212cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#endif
213cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    for (i = 1; i < argc; ++i) {
214cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        const char *arg = argv[i];
215cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        if (arg[0] != '-')
216cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            break;
217cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        switch (arg[1]) {
218cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        case 'a':
219cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            abq = XA_BOOLEAN_TRUE;
220cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            break;
221cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        case 'd':
222cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            discPacket = atoi(&arg[2]);
223cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            break;
224cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        case 'l':
225cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            looping = XA_BOOLEAN_TRUE;
226cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            break;
227cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#ifdef REINITIALIZE
228cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        case 'r':
229cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            reinit_counter = atoi(&arg[2]);
230cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            break;
231cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#endif
232cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        default:
233cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            fprintf(stderr, "%s: unknown option %s\n", prog, arg);
234cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            break;
235cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        }
236cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
237cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
238cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // check that exactly one URI was specified
239cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (argc - i != 1) {
240cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        fprintf(stderr, "usage: %s [-a] [-d#] [-l] uri\n", prog);
241cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        return EXIT_FAILURE;
242cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
243cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    const char *uri = argv[i];
244cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
245cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // for AndroidBufferQueue, interpret URI as a filename and open
246cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    int fd = -1;
247cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (abq) {
248cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        fd = open(uri, O_RDONLY);
249cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        if (fd < 0) {
250cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            perror(uri);
251cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            goto close;
252cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        }
253cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        int ok;
254cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        struct stat statbuf;
255cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        ok = fstat(fd, &statbuf);
256cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        if (ok < 0) {
257cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            perror(uri);
258cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            goto close;
259cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        }
260cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        if (!S_ISREG(statbuf.st_mode)) {
261cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            fprintf(stderr, "%s: not an ordinary file\n", uri);
262cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            goto close;
263cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        }
264cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        void *ptr;
265cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        ptr = mmap(NULL, statbuf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, (off_t) 0);
266cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        if (ptr == MAP_FAILED) {
267cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            perror(uri);
268cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            goto close;
269cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        }
270cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        size_t filelen = statbuf.st_size;
271cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        if ((filelen % MPEG2TS_PACKET_SIZE) != 0) {
272cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            fprintf(stderr, "%s: warning file length %zu is not a multiple of %d\n", uri, filelen,
273cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                    MPEG2TS_PACKET_SIZE);
274cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        }
275cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        packets = (MPEG2TS_Packet *) ptr;
276cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        numPackets = filelen / MPEG2TS_PACKET_SIZE;
277cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("%s has %zu packets\n", uri, numPackets);
278cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
279cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
280cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    ANativeWindow *nativeWindow;
281cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
282cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#ifdef REINITIALIZE
283cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastenreinitialize:    ;
284cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#endif
285cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
286cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAresult result;
287cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAObjectItf engineObject;
288cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
289cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // create engine
290cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = xaCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
291cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
292cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*engineObject)->Realize(engineObject, XA_BOOLEAN_FALSE);
293cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
294cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAEngineItf engineEngine;
295cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*engineObject)->GetInterface(engineObject, XA_IID_ENGINE, &engineEngine);
296cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
297cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
298cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // create output mix
299cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAObjectItf outputMixObject;
300cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
301cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
302cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*outputMixObject)->Realize(outputMixObject, XA_BOOLEAN_FALSE);
303cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
304cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
305cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // configure media source
306cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XADataLocator_URI locUri;
307cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    locUri.locatorType = XA_DATALOCATOR_URI;
308cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    locUri.URI = (XAchar *) uri;
309cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XADataFormat_MIME fmtMime;
310cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    fmtMime.formatType = XA_DATAFORMAT_MIME;
311cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (abq) {
312adf3525f038c77f6a122b4e88762bde9cc0a48bbGlenn Kasten        fmtMime.mimeType = (XAchar *) XA_ANDROID_MIME_MP2TS;
313cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        fmtMime.containerType = XA_CONTAINERTYPE_MPEG_TS;
314cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    } else {
315cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        fmtMime.mimeType = NULL;
316cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        fmtMime.containerType = XA_CONTAINERTYPE_UNSPECIFIED;
317cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
318cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XADataLocator_AndroidBufferQueue locABQ;
319cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    locABQ.locatorType = XA_DATALOCATOR_ANDROIDBUFFERQUEUE;
320cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    locABQ.numBuffers = NB_BUFFERS;
321cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XADataSource dataSrc;
322cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (abq) {
323cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        dataSrc.pLocator = &locABQ;
324cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    } else {
325cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        dataSrc.pLocator = &locUri;
326cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
327cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    dataSrc.pFormat = &fmtMime;
328cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
329cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // configure audio sink
330cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XADataLocator_OutputMix locOM;
331cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    locOM.locatorType = XA_DATALOCATOR_OUTPUTMIX;
332cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    locOM.outputMix = outputMixObject;
333cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XADataSink audioSnk;
334cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    audioSnk.pLocator = &locOM;
335cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    audioSnk.pFormat = NULL;
336cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
337cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // configure video sink
338cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    nativeWindow = getNativeWindow();
339cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XADataLocator_NativeDisplay locND;
340cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    locND.locatorType = XA_DATALOCATOR_NATIVEDISPLAY;
341cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    locND.hWindow = nativeWindow;
342cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    locND.hDisplay = NULL;
343cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XADataSink imageVideoSink;
344cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    imageVideoSink.pLocator = &locND;
345cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    imageVideoSink.pFormat = NULL;
346cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
347cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // create media player
348cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAObjectItf playerObject;
349cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAInterfaceID ids[4] = {XA_IID_STREAMINFORMATION, XA_IID_PREFETCHSTATUS, XA_IID_SEEK,
350c3d6dd225415ac68b1868575e793eb352c7105e2Glenn Kasten            XA_IID_ANDROIDBUFFERQUEUESOURCE};
351cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAboolean req[4] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE};
352cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObject, &dataSrc, NULL,
353cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            &audioSnk, nativeWindow != NULL ? &imageVideoSink : NULL, NULL, NULL, abq ? 4 : 3, ids,
354cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            req);
355cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
356cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
357cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // realize the player
358cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerObject)->Realize(playerObject, XA_BOOLEAN_FALSE);
359cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
360cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
361cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (abq) {
362cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
363cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        // get the Android buffer queue interface
364cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAAndroidBufferQueueItf playerAndroidBufferQueue;
365c3d6dd225415ac68b1868575e793eb352c7105e2Glenn Kasten        result = (*playerObject)->GetInterface(playerObject, XA_IID_ANDROIDBUFFERQUEUESOURCE,
366cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                &playerAndroidBufferQueue);
367cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(XA_RESULT_SUCCESS == result);
368cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
369cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        // register the buffer queue callback
370cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        result = (*playerAndroidBufferQueue)->RegisterCallback(playerAndroidBufferQueue,
371cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                bufferQueueCallback, NULL);
372cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(XA_RESULT_SUCCESS == result);
373cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        result = (*playerAndroidBufferQueue)->SetCallbackEventsMask(playerAndroidBufferQueue,
374cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                XA_ANDROIDBUFFERQUEUEEVENT_PROCESSED);
375cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(XA_RESULT_SUCCESS == result);
376cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
377cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        // enqueue the initial buffers until buffer queue is full
378cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAuint32 packetsThisBuffer;
379cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        for (curPacket = 0; curPacket < numPackets; curPacket += packetsThisBuffer) {
380cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            // handle the unlikely case of a very short .ts
381cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            packetsThisBuffer = numPackets - curPacket;
382cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            if (packetsThisBuffer > PACKETS_PER_BUFFER) {
383cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                packetsThisBuffer = PACKETS_PER_BUFFER;
384cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            }
385cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            result = (*playerAndroidBufferQueue)->Enqueue(playerAndroidBufferQueue, NULL,
386cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                    &packets[curPacket], MPEG2TS_PACKET_SIZE * packetsThisBuffer, NULL, 0);
387cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            if (XA_RESULT_BUFFER_INSUFFICIENT == result) {
388cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                printf("Enqueued initial %u packets in %u buffers\n", curPacket, curPacket / PACKETS_PER_BUFFER);
389cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                break;
390cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            }
391cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            assert(XA_RESULT_SUCCESS == result);
392cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        }
393cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
394cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
395cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
396cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // get the stream information interface
397cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAStreamInformationItf playerStreamInformation;
398cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerObject)->GetInterface(playerObject, XA_IID_STREAMINFORMATION,
399cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            &playerStreamInformation);
400cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
401cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
402cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // register the stream event change callback
403cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerStreamInformation)->RegisterStreamChangeCallback(playerStreamInformation,
404cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            streamEventChangeCallback, NULL);
405cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
406cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
407cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // get the prefetch status interface
408cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAPrefetchStatusItf playerPrefetchStatus;
409cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerObject)->GetInterface(playerObject, XA_IID_PREFETCHSTATUS,
410cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            &playerPrefetchStatus);
411cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
412cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
413cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // register prefetch status callback
414cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPrefetchStatus)->RegisterCallback(playerPrefetchStatus, prefetchStatusCallback,
415cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            NULL);
416cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
417cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPrefetchStatus)->SetCallbackEventsMask(playerPrefetchStatus,
418cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            XA_PREFETCHEVENT_FILLLEVELCHANGE | XA_PREFETCHEVENT_STATUSCHANGE);
419cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
420cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
421cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // get the seek interface
422cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (looping) {
423cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XASeekItf playerSeek;
424cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        result = (*playerObject)->GetInterface(playerObject, XA_IID_SEEK, &playerSeek);
425cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(XA_RESULT_SUCCESS == result);
426cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        result = (*playerSeek)->SetLoop(playerSeek, XA_BOOLEAN_TRUE, (XAmillisecond) 0,
427cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten                XA_TIME_UNKNOWN);
428cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(XA_RESULT_SUCCESS == result);
429cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
430cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
431cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // get the play interface
432cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAPlayItf playerPlay;
433cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerObject)->GetInterface(playerObject, XA_IID_PLAY, &playerPlay);
434cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
435cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
436cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // register play event callback
437cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPlay)->RegisterCallback(playerPlay, playEventCallback, NULL);
438cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
439cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#if 0 // FIXME broken
440cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPlay)->SetCallbackEventsMask(playerPlay,
441cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            XA_PLAYEVENT_HEADATEND | XA_PLAYEVENT_HEADATMARKER | XA_PLAYEVENT_HEADATNEWPOS);
442cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
443cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#endif
444cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
445cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // set a marker
446cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPlay)->SetMarkerPosition(playerPlay, 10000);
447cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
448cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
449cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // set position update period
450cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPlay)->SetPositionUpdatePeriod(playerPlay, 1000);
451cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
452cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
453cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // get the duration
454cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    XAmillisecond duration;
455cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPlay)->GetDuration(playerPlay, &duration);
456cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
457cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (XA_TIME_UNKNOWN == duration)
458cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("Duration: unknown\n");
459cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    else
460cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        printf("Duration: %.1f\n", duration / 1000.0f);
461cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
462cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // set the player's state to paused, to start prefetching
463cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    printf("start prefetch\n");
464cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPlay)->SetPlayState(playerPlay, XA_PLAYSTATE_PAUSED);
465cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
466cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
467cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // wait for prefetch status callback to indicate either sufficient data or error
468cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    pthread_mutex_lock(&mutex);
469cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    while (prefetch_status == PREFETCHSTATUS_UNKNOWN) {
470cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        pthread_cond_wait(&cond, &mutex);
471cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
472cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    pthread_mutex_unlock(&mutex);
473cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (prefetch_status == PREFETCHSTATUS_ERROR) {
474cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        fprintf(stderr, "Error during prefetch, exiting\n");
475cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        goto destroyRes;
476cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
477cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
478cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // get duration again, now it should be known
479cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPlay)->GetDuration(playerPlay, &duration);
480cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
481cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (duration == XA_TIME_UNKNOWN) {
482cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        fprintf(stdout, "Content duration is unknown (after prefetch completed)\n");
483cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    } else {
484cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        fprintf(stdout, "Content duration is %u ms (after prefetch completed)\n", duration);
485cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
486cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
487cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // start playing
488cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    printf("starting to play\n");
489cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    result = (*playerPlay)->SetPlayState(playerPlay, XA_PLAYSTATE_PLAYING);
490cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    assert(XA_RESULT_SUCCESS == result);
491cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
492cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // continue playing until end of media
493cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    for (;;) {
494cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        XAuint32 status;
495cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        result = (*playerPlay)->GetPlayState(playerPlay, &status);
496cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(XA_RESULT_SUCCESS == result);
497cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        if (status == XA_PLAYSTATE_PAUSED)
498cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten            break;
499cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        assert(status == XA_PLAYSTATE_PLAYING);
500cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        sleep(1);
501cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
502cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
503cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // wait a bit more in case of additional callbacks
504cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    printf("end of media\n");
505cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    sleep(3);
506cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
507cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn KastendestroyRes:
508cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
509cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // destroy the player
510cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    (*playerObject)->Destroy(playerObject);
511cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
512cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // destroy the output mix
513cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    (*outputMixObject)->Destroy(outputMixObject);
514cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
515cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    // destroy the engine
516cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    (*engineObject)->Destroy(engineObject);
517cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
518cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#ifdef REINITIALIZE
519cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (--reinit_count > 0) {
520cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        prefetch_status = PREFETCHSTATUS_UNKNOWN;
521cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        goto reinitialize;
522cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
523cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#endif
524cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
525cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#if 0
526cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (nativeWindow != NULL) {
527cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        ANativeWindow_release(nativeWindow);
528cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
529cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten#endif
530cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
531cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kastenclose:
532cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    if (fd >= 0) {
533cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten        (void) close(fd);
534cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    }
535cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten
536cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten    return EXIT_SUCCESS;
537cb9de966a5f0b9773fee2aa7b5142d69d7cf7e37Glenn Kasten}
538