19a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten/*
29a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten * Copyright (C) 2010 The Android Open Source Project
39a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten *
49a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten * Licensed under the Apache License, Version 2.0 (the "License");
59a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten * you may not use this file except in compliance with the License.
69a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten * You may obtain a copy of the License at
79a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten *
89a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten *      http://www.apache.org/licenses/LICENSE-2.0
99a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten *
109a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten * Unless required by applicable law or agreed to in writing, software
119a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten * distributed under the License is distributed on an "AS IS" BASIS,
129a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten * See the License for the specific language governing permissions and
149a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten * limitations under the License.
159a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten */
169a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
179a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten#include <assert.h>
189a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten#include <jni.h>
199a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten#include <pthread.h>
209a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten#include <string.h>
2134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten//#define LOG_NDEBUG 0
229a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten#define LOG_TAG "NativeMedia"
239a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten#include <utils/Log.h>
24c09fe859aabccb8b18e6c857d087686b37d48c1fJean-Michel Trivi
25a6c5e52ded343b557152156c33d33a10d29bf6f1Glenn Kasten#include <OMXAL/OpenMAXAL.h>
26a6c5e52ded343b557152156c33d33a10d29bf6f1Glenn Kasten#include <OMXAL/OpenMAXAL_Android.h>
27c09fe859aabccb8b18e6c857d087686b37d48c1fJean-Michel Trivi
2873d888bb7b15745c6456dc0fab97c39854827d2aJean-Michel Trivi#include <android/native_window_jni.h>
299a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
309a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten// engine interfaces
319a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kastenstatic XAObjectItf engineObject = NULL;
3234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenstatic XAEngineItf engineEngine = NULL;
339a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
349a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten// output mix interfaces
359a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kastenstatic XAObjectItf outputMixObject = NULL;
369a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
379a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten// streaming media player interfaces
382f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivistatic XAObjectItf             playerObj = NULL;
392f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivistatic XAPlayItf               playerPlayItf = NULL;
402f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivistatic XAAndroidBufferQueueItf playerBQItf = NULL;
41fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivistatic XAStreamInformationItf  playerStreamInfoItf = NULL;
4234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenstatic XAVolumeItf             playerVolItf = NULL;
4334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
44f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi// number of required interfaces for the MediaPlayer creation
45fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi#define NB_MAXAL_INTERFACES 3 // XAAndroidBufferQueueItf, XAStreamInformationItf and XAPlayItf
469a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4749dfad6a8f91f0cce729f6db143f26c188caf523Glenn Kasten// video sink for the player
4873d888bb7b15745c6456dc0fab97c39854827d2aJean-Michel Trivistatic ANativeWindow* theNativeWindow;
499a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
5034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten// number of buffers in our buffer queue, an arbitrary number
51f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi#define NB_BUFFERS 16
5234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
53f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi// we're streaming MPEG-2 transport stream data, operate on transport stream block size
54f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi#define MPEG2_TS_BLOCK_SIZE 188
5534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
5634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten// number of MPEG-2 transport stream blocks per buffer, an arbitrary number
5734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten#define BLOCKS_PER_BUFFER 20
5834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
59f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi// determines how much memory we're dedicating to memory caching
6034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten#define BUFFER_SIZE (BLOCKS_PER_BUFFER*MPEG2_TS_BLOCK_SIZE)
61f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi
62f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi// where we cache in memory the data to play
6334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten// note this memory is re-used by the buffer queue callback
64f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivichar dataCache[BUFFER_SIZE * NB_BUFFERS];
6534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
662f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi// handle of the file to play
672f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel TriviFILE *file;
6834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
695b21a0626e173d407aa3835e5cffcaa9b582016dJean-Michel Trivi// has the app reached the end of the file
7034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenjboolean reachedEof = JNI_FALSE;
7134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
72e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi// constant to identify a buffer context which is the end of the stream to decode
73e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivistatic const int kEosBufferCntxt = 1980; // a magic value we can compare against
74e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi
7534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten// for mutual exclusion between callback thread and application thread(s)
7634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenpthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
7734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenpthread_cond_t cond = PTHREAD_COND_INITIALIZER;
7834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
7934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten// whether a discontinuity is in progress
8034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenjboolean discontinuity = JNI_FALSE;
8134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
8234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenstatic jboolean enqueueInitialBuffers(jboolean discontinuity);
832f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi
84f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi// Callback for XAPlayItf through which we receive the XA_PLAYEVENT_HEADATEND event */
85f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivivoid PlayCallback(XAPlayItf caller, void *pContext, XAuint32 event) {
86f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi    if (event & XA_PLAYEVENT_HEADATEND) {
87f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi        LOGV("XA_PLAYEVENT_HEADATEND received, all MP2TS data has been decoded\n");
88f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi    }
89f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi}
90f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi
912f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi// AndroidBufferQueueItf callback for an audio player
922f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel TriviXAresult AndroidBufferQueueCallback(
932f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        XAAndroidBufferQueueItf caller,
94fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        void *pCallbackContext,        /* input */
95fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        void *pBufferContext,          /* input */
96fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        void *pBufferData,             /* input */
97f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi        XAuint32 dataSize,             /* input */
98f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi        XAuint32 dataUsed,             /* input */
99f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi        const XAAndroidBufferItem *pItems,/* input */
100f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi        XAuint32 itemsLength           /* input */)
1012f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi{
10234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    XAresult res;
10334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    int ok;
10434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
10534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // pCallbackContext was specified as NULL at RegisterCallback and is unused here
10634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    assert(NULL == pCallbackContext);
10734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
10834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // note there is never any contention on this mutex unless a discontinuity request is active
10934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    ok = pthread_mutex_lock(&mutex);
11034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    assert(0 == ok);
11134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
11234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // was a discontinuity requested?
11334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    if (discontinuity) {
11434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        // FIXME sorry, can't rewind after EOS
11534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        if (!reachedEof) {
11634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            // clear the buffer queue
11734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            res = (*playerBQItf)->Clear(playerBQItf);
11834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            assert(XA_RESULT_SUCCESS == res);
11934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            // rewind the data source so we are guaranteed to be at an appropriate point
12034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            rewind(file);
12134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            // Enqueue the initial buffers, with a discontinuity indicator on first buffer
12234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            (void) enqueueInitialBuffers(JNI_TRUE);
12334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        }
12434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        // acknowledge the discontinuity request
12534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        discontinuity = JNI_FALSE;
12634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        ok = pthread_cond_signal(&cond);
12734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        assert(0 == ok);
12834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        goto exit;
12934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    }
13034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
131e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi    if ((pBufferData == NULL) && (pBufferContext != NULL)) {
132e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi        const int processedCommand = *(int *)pBufferContext;
133e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi        if (kEosBufferCntxt == processedCommand) {
134e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi            LOGV("EOS was processed\n");
135e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi            // our buffer with the EOS message has been consumed
136e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi            assert(0 == dataSize);
137e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi            goto exit;
138e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi        }
139fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    }
140fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi
14134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // pBufferData is a pointer to a buffer that we previously Enqueued
14234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    assert(BUFFER_SIZE == dataSize);
14334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    assert(dataCache <= (char *) pBufferData && (char *) pBufferData <
14434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            &dataCache[BUFFER_SIZE * NB_BUFFERS]);
14534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    assert(0 == (((char *) pBufferData - dataCache) % BUFFER_SIZE));
14634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
147fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi#if 0
148fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    // sample code to use the XAVolumeItf
149fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    XAAndroidBufferQueueState state;
150fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    (*caller)->GetState(caller, &state);
151fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    switch (state.index) {
152fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    case 300:
153fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        (*playerVolItf)->SetVolumeLevel(playerVolItf, -600); // -6dB
154fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        LOGV("setting volume to -6dB");
155fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        break;
156fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    case 400:
157fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        (*playerVolItf)->SetVolumeLevel(playerVolItf, -1200); // -12dB
158fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        LOGV("setting volume to -12dB");
159fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        break;
160fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    case 500:
161fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        (*playerVolItf)->SetVolumeLevel(playerVolItf, 0); // full volume
162fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        LOGV("setting volume to 0dB (full volume)");
163fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        break;
164fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    case 600:
165fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_TRUE); // mute
166fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        LOGV("muting player");
167fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        break;
168fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    case 700:
169fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_FALSE); // unmute
170fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        LOGV("unmuting player");
171fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        break;
172fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    case 800:
173fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        (*playerVolItf)->SetStereoPosition(playerVolItf, -1000);
174fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_TRUE);
175fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        LOGV("pan sound to the left (hard-left)");
176fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        break;
177fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    case 900:
178fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_FALSE);
179fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        LOGV("disabling stereo position");
180fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        break;
181fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    default:
182fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        break;
183fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    }
184fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi#endif
185fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi
18634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // don't bother trying to read more data once we've hit EOF
18734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    if (reachedEof) {
18834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        goto exit;
18934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    }
19034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
19134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    size_t nbRead;
19234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // note we do call fread from multiple threads, but never concurrently
19334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    nbRead = fread(pBufferData, BUFFER_SIZE, 1, file);
19434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    if (nbRead > 0) {
19534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        assert(1 == nbRead);
19634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        res = (*caller)->Enqueue(caller, NULL /*pBufferContext*/,
197f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi                pBufferData /*pData*/,
19834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten                nbRead * BUFFER_SIZE /*dataLength*/,
199f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi                NULL /*pMsg*/,
200f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi                0 /*msgLength*/);
20134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        assert(XA_RESULT_SUCCESS == res);
20234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    } else {
203f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi        // signal EOS
20434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        XAAndroidBufferItem msgEos[1];
20534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        msgEos[0].itemKey = XA_ANDROID_ITEMKEY_EOS;
20634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        msgEos[0].itemSize = 0;
207f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi        // EOS message has no parameters, so the total size of the message is the size of the key
208f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi        //   plus the size if itemSize, both XAuint32
209e5c2733e99086fb726db9c1cf6cb3f04ae08defbJean-Michel Trivi        res = (*caller)->Enqueue(caller, (void *)&kEosBufferCntxt /*pBufferContext*/,
210fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi                NULL /*pData*/, 0 /*dataLength*/,
21134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten                msgEos /*pMsg*/,
21234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten                // FIXME == sizeof(BufferItem)? */
213fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi                sizeof(XAuint32)*2 /*msgLength*/);
21434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        assert(XA_RESULT_SUCCESS == res);
21534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        reachedEof = JNI_TRUE;
2162f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    }
2172f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi
21834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenexit:
21934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    ok = pthread_mutex_unlock(&mutex);
22034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    assert(0 == ok);
2212f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    return XA_RESULT_SUCCESS;
2222f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi}
2232f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi
2242f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi
225fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivivoid StreamChangeCallback (XAStreamInformationItf caller,
226fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        XAuint32 eventId,
227fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        XAuint32 streamIndex,
228fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        void * pEventData,
229fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        void * pContext )
230fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi{
23134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    LOGV("StreamChangeCallback called for stream %u", streamIndex);
23234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // pContext was specified as NULL at RegisterStreamChangeCallback and is unused here
23334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    assert(NULL == pContext);
23434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    switch (eventId) {
23534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    case XA_STREAMCBEVENT_PROPERTYCHANGE: {
23634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        /** From spec 1.0.1:
23734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            "This event indicates that stream property change has occurred.
23834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            The streamIndex parameter identifies the stream with the property change.
23934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            The pEventData parameter for this event is not used and shall be ignored."
24034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten         */
24134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
24234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        XAresult res;
243fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        XAuint32 domain;
24434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        res = (*caller)->QueryStreamType(caller, streamIndex, &domain);
24534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        assert(XA_RESULT_SUCCESS == res);
24634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        switch (domain) {
24734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        case XA_DOMAINTYPE_VIDEO: {
24834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            XAVideoStreamInformation videoInfo;
24934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            res = (*caller)->QueryStreamInformation(caller, streamIndex, &videoInfo);
25034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            assert(XA_RESULT_SUCCESS == res);
25134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            LOGI("Found video size %u x %u, codec ID=%u, frameRate=%u, bitRate=%u, duration=%u ms",
25234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten                        videoInfo.width, videoInfo.height, videoInfo.codecId, videoInfo.frameRate,
25334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten                        videoInfo.bitRate, videoInfo.duration);
25434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        } break;
25534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        default:
25634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            fprintf(stderr, "Unexpected domain %u\n", domain);
25734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            break;
258fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi        }
25934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        } break;
26034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    default:
26134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        fprintf(stderr, "Unexpected stream event ID %u\n", eventId);
26234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        break;
263fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    }
264fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi}
265fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi
266fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi
2679a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten// create the engine and output mix objects
2689a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kastenvoid Java_com_example_nativemedia_NativeMedia_createEngine(JNIEnv* env, jclass clazz)
2699a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten{
2702f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    XAresult res;
2719a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
2729a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // create engine
2732f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = xaCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
2742f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
2759a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
2769a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // realize the engine
2772f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = (*engineObject)->Realize(engineObject, XA_BOOLEAN_FALSE);
2782f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
2799a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
2809a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // get the engine interface, which is needed in order to create other objects
2812f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = (*engineObject)->GetInterface(engineObject, XA_IID_ENGINE, &engineEngine);
2822f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
2839a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
2849a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // create output mix
2852f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
2862f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
2879a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
2889a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // realize the output mix
2892f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = (*outputMixObject)->Realize(outputMixObject, XA_BOOLEAN_FALSE);
2902f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
2919a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
2929a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten}
2939a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
2949a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
29534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten// Enqueue the initial buffers, and optionally signal a discontinuity in the first buffer
29634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenstatic jboolean enqueueInitialBuffers(jboolean discontinuity)
29734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten{
29834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
29934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    /* Fill our cache */
30034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    size_t nbRead;
30134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    nbRead = fread(dataCache, BUFFER_SIZE, NB_BUFFERS, file);
30234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    if (nbRead <= 0) {
30334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        // could be premature EOF or I/O error
30434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        LOGE("Error filling cache, exiting\n");
30534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        return JNI_FALSE;
30634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    }
30734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    assert(1 <= nbRead && nbRead <= NB_BUFFERS);
30834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    LOGV("Initially queueing %u buffers of %u bytes each", nbRead, BUFFER_SIZE);
30934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
31034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    /* Enqueue the content of our cache before starting to play,
31134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten       we don't want to starve the player */
31234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    size_t i;
31334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    for (i = 0; i < nbRead; i++) {
31434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        XAresult res;
31534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        if (discontinuity) {
31634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            // signal discontinuity
31734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            XAAndroidBufferItem items[1];
31834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            items[0].itemKey = XA_ANDROID_ITEMKEY_DISCONTINUITY;
31934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            items[0].itemSize = 0;
32034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            // DISCONTINUITY message has no parameters,
32134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            //   so the total size of the message is the size of the key
32234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            //   plus the size if itemSize, both XAuint32
32334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            res = (*playerBQItf)->Enqueue(playerBQItf, NULL /*pBufferContext*/,
32434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten                    dataCache + i*BUFFER_SIZE, BUFFER_SIZE, items /*pMsg*/,
32534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten                    // FIXME == sizeof(BufferItem)? */
32634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten                    sizeof(XAuint32)*2 /*msgLength*/);
32734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            discontinuity = JNI_FALSE;
32834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        } else {
32934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            res = (*playerBQItf)->Enqueue(playerBQItf, NULL /*pBufferContext*/,
33034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten                    dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0);
33134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        }
33234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        assert(XA_RESULT_SUCCESS == res);
33334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    }
33434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
33534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    return JNI_TRUE;
33634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten}
33734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
33834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
3399a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten// create streaming media player
3409a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kastenjboolean Java_com_example_nativemedia_NativeMedia_createStreamingMediaPlayer(JNIEnv* env,
3419a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten        jclass clazz, jstring filename)
3429a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten{
3432f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    XAresult res;
3449a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
3459a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // convert Java string to UTF-8
3469a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    const char *utf8 = (*env)->GetStringUTFChars(env, filename, NULL);
3479a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    assert(NULL != utf8);
3489a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
3492f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    // open the file to play
3502f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    file = fopen(utf8, "rb");
3512f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    if (file == NULL) {
3522f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        LOGE("Failed to open %s", utf8);
3532f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        return JNI_FALSE;
3542f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    }
3552f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi
3562f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    // configure data source
357f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi    XADataLocator_AndroidBufferQueue loc_abq = { XA_DATALOCATOR_ANDROIDBUFFERQUEUE, NB_BUFFERS };
358f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi    XADataFormat_MIME format_mime = {
359adf3525f038c77f6a122b4e88762bde9cc0a48bbGlenn Kasten            XA_DATAFORMAT_MIME, XA_ANDROID_MIME_MP2TS, XA_CONTAINERTYPE_MPEG_TS };
3602f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    XADataSource dataSrc = {&loc_abq, &format_mime};
3619a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
3629a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // configure audio sink
363f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi    XADataLocator_OutputMix loc_outmix = { XA_DATALOCATOR_OUTPUTMIX, outputMixObject };
364f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi    XADataSink audioSnk = { &loc_outmix, NULL };
3659a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
3669a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // configure image video sink
36773d888bb7b15745c6456dc0fab97c39854827d2aJean-Michel Trivi    XADataLocator_NativeDisplay loc_nd = {
36813a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten            XA_DATALOCATOR_NATIVEDISPLAY,        // locatorType
36949dfad6a8f91f0cce729f6db143f26c188caf523Glenn Kasten            // the video sink must be an ANativeWindow created from a Surface or SurfaceTexture
37013a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten            (void*)theNativeWindow,              // hWindow
37113a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten            // must be NULL
37213a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten            NULL                                 // hDisplay
37313a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten    };
3749a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    XADataSink imageVideoSink = {&loc_nd, NULL};
3759a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
3762f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    // declare interfaces to use
377fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    XAboolean     required[NB_MAXAL_INTERFACES]
378fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi                           = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE,           XA_BOOLEAN_TRUE};
379fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    XAInterfaceID iidArray[NB_MAXAL_INTERFACES]
380c3d6dd225415ac68b1868575e793eb352c7105e2Glenn Kasten                           = {XA_IID_PLAY,     XA_IID_ANDROIDBUFFERQUEUESOURCE,
381c3d6dd225415ac68b1868575e793eb352c7105e2Glenn Kasten                                               XA_IID_STREAMINFORMATION};
382fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi
3832f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi
3849a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // create media player
3852f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObj, &dataSrc,
3862f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi            NULL, &audioSnk, &imageVideoSink, NULL, NULL,
387f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi            NB_MAXAL_INTERFACES /*XAuint32 numInterfaces*/,
3882f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi            iidArray /*const XAInterfaceID *pInterfaceIds*/,
3892f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi            required /*const XAboolean *pInterfaceRequired*/);
3902f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
3919a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
3929a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // release the Java string and UTF-8
3939a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    (*env)->ReleaseStringUTFChars(env, filename, utf8);
3949a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
3959a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // realize the player
3962f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = (*playerObj)->Realize(playerObj, XA_BOOLEAN_FALSE);
3972f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
3989a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
3999a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // get the play interface
4002f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = (*playerObj)->GetInterface(playerObj, XA_IID_PLAY, &playerPlayItf);
4012f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
4022f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi
403fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    // get the stream information interface (for video size)
404fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    res = (*playerObj)->GetInterface(playerObj, XA_IID_STREAMINFORMATION, &playerStreamInfoItf);
405fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
406fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi
407fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    // get the volume interface
408fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    res = (*playerObj)->GetInterface(playerObj, XA_IID_VOLUME, &playerVolItf);
409fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
410fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi
4112f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    // get the Android buffer queue interface
412c3d6dd225415ac68b1868575e793eb352c7105e2Glenn Kasten    res = (*playerObj)->GetInterface(playerObj, XA_IID_ANDROIDBUFFERQUEUESOURCE, &playerBQItf);
4132f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
4142f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi
41534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // specify which events we want to be notified of
41634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    res = (*playerBQItf)->SetCallbackEventsMask(playerBQItf, XA_ANDROIDBUFFERQUEUEEVENT_PROCESSED);
41734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
418f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi    // use the play interface to set up a callback for the XA_PLAYEVENT_HEADATEND event */
419f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi    res = (*playerPlayItf)->SetCallbackEventsMask(playerPlayItf, XA_PLAYEVENT_HEADATEND);
420f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
421f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi    res = (*playerPlayItf)->RegisterCallback(playerPlayItf,
422f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi            PlayCallback /*callback*/, NULL /*pContext*/);
423f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
424f419fe271ff98424c9f0ddef13f436d4fb1ba538Jean-Michel Trivi
4252f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    // register the callback from which OpenMAX AL can retrieve the data to play
426f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi    res = (*playerBQItf)->RegisterCallback(playerBQItf, AndroidBufferQueueCallback, NULL);
427fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
428fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi
429fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    // we want to be notified of the video size once it's found, so we register a callback for that
430fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    res = (*playerStreamInfoItf)->RegisterStreamChangeCallback(playerStreamInfoItf,
431fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi            StreamChangeCallback, NULL);
432f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi
43334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // enqueue the initial buffers
43434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    if (!enqueueInitialBuffers(JNI_FALSE)) {
435f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi        return JNI_FALSE;
436f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi    }
437f65b260193e5ca669ec4479a7e1c4517e18cc6b0Jean-Michel Trivi
4382f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    // prepare the player
4392f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PAUSED);
4402f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
4412f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi
442fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    // set the volume
443fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    res = (*playerVolItf)->SetVolumeLevel(playerVolItf, 0);//-300);
444fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
445fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi
446fcc996296bdbf6c3949ad4312991fdde4ae2e157Jean-Michel Trivi    // start the playback
4472f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PLAYING);
4482f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        assert(XA_RESULT_SUCCESS == res);
4499a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4509a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    return JNI_TRUE;
4519a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten}
4529a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4539a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4549a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten// set the playing state for the streaming media player
4559a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kastenvoid Java_com_example_nativemedia_NativeMedia_setPlayingStreamingMediaPlayer(JNIEnv* env,
4569a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten        jclass clazz, jboolean isPlaying)
4579a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten{
4582f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    XAresult res;
4599a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4609a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // make sure the streaming media player was created
4612f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    if (NULL != playerPlayItf) {
4629a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4639a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten        // set the player's state
4642f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        res = (*playerPlayItf)->SetPlayState(playerPlayItf, isPlaying ?
4659a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten            XA_PLAYSTATE_PLAYING : XA_PLAYSTATE_PAUSED);
4662f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        assert(XA_RESULT_SUCCESS == res);
4679a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4689a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    }
4699a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4709a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten}
4719a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4729a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4739a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten// shut down the native media system
4749a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kastenvoid Java_com_example_nativemedia_NativeMedia_shutdown(JNIEnv* env, jclass clazz)
4759a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten{
4769a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // destroy streaming media player object, and invalidate all associated interfaces
4772f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    if (playerObj != NULL) {
4782f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        (*playerObj)->Destroy(playerObj);
4792f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        playerObj = NULL;
4802f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        playerPlayItf = NULL;
4812f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        playerBQItf = NULL;
48234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        playerStreamInfoItf = NULL;
48334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        playerVolItf = NULL;
4849a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    }
4859a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4869a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // destroy output mix object, and invalidate all associated interfaces
4879a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    if (outputMixObject != NULL) {
4889a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten        (*outputMixObject)->Destroy(outputMixObject);
4899a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten        outputMixObject = NULL;
4909a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    }
4919a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4929a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    // destroy engine object, and invalidate all associated interfaces
4939a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    if (engineObject != NULL) {
4949a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten        (*engineObject)->Destroy(engineObject);
4959a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten        engineObject = NULL;
4969a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten        engineEngine = NULL;
4979a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten    }
4989a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
4992f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    // close the file
5002f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    if (file != NULL) {
5012f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi        fclose(file);
5022a1042f54c8e61a0c2a062709367093100ea6f8fGlenn Kasten        file = NULL;
5032f4002b9f37501cd15a7e06cd8e19453d2770f30Jean-Michel Trivi    }
50473d888bb7b15745c6456dc0fab97c39854827d2aJean-Michel Trivi
50573d888bb7b15745c6456dc0fab97c39854827d2aJean-Michel Trivi    // make sure we don't leak native windows
50613a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten    if (theNativeWindow != NULL) {
50713a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten        ANativeWindow_release(theNativeWindow);
5082a1042f54c8e61a0c2a062709367093100ea6f8fGlenn Kasten        theNativeWindow = NULL;
50913a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten    }
5109a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten}
5119a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
5129a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten
5139a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten// set the surface
5149a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kastenvoid Java_com_example_nativemedia_NativeMedia_setSurface(JNIEnv *env, jclass clazz, jobject surface)
5159a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten{
51673d888bb7b15745c6456dc0fab97c39854827d2aJean-Michel Trivi    // obtain a native window from a Java surface
51773d888bb7b15745c6456dc0fab97c39854827d2aJean-Michel Trivi    theNativeWindow = ANativeWindow_fromSurface(env, surface);
51813a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten}
51913a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten
52013a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten
52113a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten// set the surface texture
52213a07de046bce3663b905a892dbaf770a54d982dGlenn Kastenvoid Java_com_example_nativemedia_NativeMedia_setSurfaceTexture(JNIEnv *env, jclass clazz,
52313a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten        jobject surfaceTexture)
52413a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten{
52513a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten    // obtain a native window from a Java surface texture
52613a07de046bce3663b905a892dbaf770a54d982dGlenn Kasten    theNativeWindow = ANativeWindow_fromSurfaceTexture(env, surfaceTexture);
5279a709c6410ac6fd3da51dd02dda72071c5bb9310Glenn Kasten}
52834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
52934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
53034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten// rewind the streaming media player
53134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kastenvoid Java_com_example_nativemedia_NativeMedia_rewindStreamingMediaPlayer(JNIEnv *env, jclass clazz)
53234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten{
53334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    XAresult res;
53434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
53534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    // make sure the streaming media player was created
53634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    if (NULL != playerBQItf && NULL != file) {
53734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        // first wait for buffers currently in queue to be drained
53834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        int ok;
53934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        ok = pthread_mutex_lock(&mutex);
54034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        assert(0 == ok);
54134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        discontinuity = JNI_TRUE;
54234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        // wait for discontinuity request to be observed by buffer queue callback
54334294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        // FIXME sorry, can't rewind after EOS
54434294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        while (discontinuity && !reachedEof) {
54534294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            ok = pthread_cond_wait(&cond, &mutex);
54634294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten            assert(0 == ok);
54734294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        }
54834294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        ok = pthread_mutex_unlock(&mutex);
54934294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten        assert(0 == ok);
55034294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten    }
55134294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten
55234294d068e5560ee29e1e326b7bf49a7068e6cb0Glenn Kasten}
553