native-media-jni.c revision 7f7fde185e2739a88ba1847fa583a7dec459ea43
197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten/*
297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten * Copyright (C) 2010 The Android Open Source Project
397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten *
497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten * Licensed under the Apache License, Version 2.0 (the "License");
597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten * you may not use this file except in compliance with the License.
697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten * You may obtain a copy of the License at
797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten *
897bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten *      http://www.apache.org/licenses/LICENSE-2.0
997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten *
1097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten * Unless required by applicable law or agreed to in writing, software
1197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten * distributed under the License is distributed on an "AS IS" BASIS,
1297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten * See the License for the specific language governing permissions and
1497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten * limitations under the License.
1597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten */
1697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
1797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten#include <assert.h>
1897bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten#include <jni.h>
1997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten#include <pthread.h>
2097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten#include <string.h>
21ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten//#define LOG_NDEBUG 0
2297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten#define LOG_TAG "NativeMedia"
2397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten#include <utils/Log.h>
24f183d0fd23f1c0f45ceaf280d404f1b0709a699aJean-Michel Trivi
25c6853892c94800e72c0bd676d5d2136d48cea76eGlenn Kasten#include <OMXAL/OpenMAXAL.h>
26c6853892c94800e72c0bd676d5d2136d48cea76eGlenn Kasten#include <OMXAL/OpenMAXAL_Android.h>
27f183d0fd23f1c0f45ceaf280d404f1b0709a699aJean-Michel Trivi
28295395c087000a9d804f8d12d58dea4cd9e7d26aJean-Michel Trivi#include <android/native_window_jni.h>
2997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
3097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten// engine interfaces
3197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kastenstatic XAObjectItf engineObject = NULL;
32ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenstatic XAEngineItf engineEngine = NULL;
3397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
3497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten// output mix interfaces
3597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kastenstatic XAObjectItf outputMixObject = NULL;
3697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
3797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten// streaming media player interfaces
38eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivistatic XAObjectItf             playerObj = NULL;
39eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivistatic XAPlayItf               playerPlayItf = NULL;
40eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivistatic XAAndroidBufferQueueItf playerBQItf = NULL;
4137dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivistatic XAStreamInformationItf  playerStreamInfoItf = NULL;
42ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenstatic XAVolumeItf             playerVolItf = NULL;
43ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
44d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi// number of required interfaces for the MediaPlayer creation
4537dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi#define NB_MAXAL_INTERFACES 3 // XAAndroidBufferQueueItf, XAStreamInformationItf and XAPlayItf
4697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
4739310fca2e30101fa6e5168da443581cc60c20bfGlenn Kasten// video sink for the player
48295395c087000a9d804f8d12d58dea4cd9e7d26aJean-Michel Trivistatic ANativeWindow* theNativeWindow;
4997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
50ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten// number of buffers in our buffer queue, an arbitrary number
51d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi#define NB_BUFFERS 16
52ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
53d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi// we're streaming MPEG-2 transport stream data, operate on transport stream block size
54d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi#define MPEG2_TS_BLOCK_SIZE 188
55ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
56ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten// number of MPEG-2 transport stream blocks per buffer, an arbitrary number
57ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten#define BLOCKS_PER_BUFFER 20
58ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
59d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi// determines how much memory we're dedicating to memory caching
60ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten#define BUFFER_SIZE (BLOCKS_PER_BUFFER*MPEG2_TS_BLOCK_SIZE)
61d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi
62d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi// where we cache in memory the data to play
63ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten// note this memory is re-used by the buffer queue callback
64d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivichar dataCache[BUFFER_SIZE * NB_BUFFERS];
65ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
66eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi// handle of the file to play
67eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel TriviFILE *file;
68ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
6970c49ae2867094072a4365423417ea452bf82231Jean-Michel Trivi// has the app reached the end of the file
70ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenjboolean reachedEof = JNI_FALSE;
71ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
726bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi// constant to identify a buffer context which is the end of the stream to decode
736bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivistatic const int kEosBufferCntxt = 1980; // a magic value we can compare against
746bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi
75ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten// for mutual exclusion between callback thread and application thread(s)
76ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenpthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
77ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenpthread_cond_t cond = PTHREAD_COND_INITIALIZER;
78ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
79ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten// whether a discontinuity is in progress
80ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenjboolean discontinuity = JNI_FALSE;
81ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
82ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenstatic jboolean enqueueInitialBuffers(jboolean discontinuity);
83eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi
84a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi// Callback for XAPlayItf through which we receive the XA_PLAYEVENT_HEADATEND event */
85a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivivoid PlayCallback(XAPlayItf caller, void *pContext, XAuint32 event) {
86a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi    if (event & XA_PLAYEVENT_HEADATEND) {
87de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block        ALOGV("XA_PLAYEVENT_HEADATEND received, all MP2TS data has been decoded\n");
88a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi    }
89a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi}
90a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi
91eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi// AndroidBufferQueueItf callback for an audio player
92eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel TriviXAresult AndroidBufferQueueCallback(
93eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        XAAndroidBufferQueueItf caller,
9437dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        void *pCallbackContext,        /* input */
9537dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        void *pBufferContext,          /* input */
9637dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        void *pBufferData,             /* input */
97d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi        XAuint32 dataSize,             /* input */
98d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi        XAuint32 dataUsed,             /* input */
99d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi        const XAAndroidBufferItem *pItems,/* input */
100d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi        XAuint32 itemsLength           /* input */)
101eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi{
102ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    XAresult res;
103ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    int ok;
104ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
105ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // pCallbackContext was specified as NULL at RegisterCallback and is unused here
106ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    assert(NULL == pCallbackContext);
107ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
108ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // note there is never any contention on this mutex unless a discontinuity request is active
109ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    ok = pthread_mutex_lock(&mutex);
110ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    assert(0 == ok);
111ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
112ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // was a discontinuity requested?
113ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    if (discontinuity) {
114ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        // FIXME sorry, can't rewind after EOS
115ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        if (!reachedEof) {
116ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            // clear the buffer queue
117ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            res = (*playerBQItf)->Clear(playerBQItf);
118ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            assert(XA_RESULT_SUCCESS == res);
119ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            // rewind the data source so we are guaranteed to be at an appropriate point
120ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            rewind(file);
121ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            // Enqueue the initial buffers, with a discontinuity indicator on first buffer
122ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            (void) enqueueInitialBuffers(JNI_TRUE);
123ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        }
124ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        // acknowledge the discontinuity request
125ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        discontinuity = JNI_FALSE;
126ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        ok = pthread_cond_signal(&cond);
127ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        assert(0 == ok);
128ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        goto exit;
129ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    }
130ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
1316bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi    if ((pBufferData == NULL) && (pBufferContext != NULL)) {
1326bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi        const int processedCommand = *(int *)pBufferContext;
1336bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi        if (kEosBufferCntxt == processedCommand) {
134de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block            ALOGV("EOS was processed\n");
1356bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi            // our buffer with the EOS message has been consumed
1366bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi            assert(0 == dataSize);
1376bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi            goto exit;
1386bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi        }
13937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    }
14037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi
141ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // pBufferData is a pointer to a buffer that we previously Enqueued
142ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    assert(BUFFER_SIZE == dataSize);
143ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    assert(dataCache <= (char *) pBufferData && (char *) pBufferData <
144ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            &dataCache[BUFFER_SIZE * NB_BUFFERS]);
145ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    assert(0 == (((char *) pBufferData - dataCache) % BUFFER_SIZE));
146ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
14737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi#if 0
14837dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    // sample code to use the XAVolumeItf
14937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    XAAndroidBufferQueueState state;
15037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    (*caller)->GetState(caller, &state);
15137dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    switch (state.index) {
15237dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    case 300:
15337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        (*playerVolItf)->SetVolumeLevel(playerVolItf, -600); // -6dB
154de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block        ALOGV("setting volume to -6dB");
15537dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        break;
15637dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    case 400:
15737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        (*playerVolItf)->SetVolumeLevel(playerVolItf, -1200); // -12dB
158de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block        ALOGV("setting volume to -12dB");
15937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        break;
16037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    case 500:
16137dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        (*playerVolItf)->SetVolumeLevel(playerVolItf, 0); // full volume
162de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block        ALOGV("setting volume to 0dB (full volume)");
16337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        break;
16437dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    case 600:
16537dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_TRUE); // mute
166de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block        ALOGV("muting player");
16737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        break;
16837dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    case 700:
16937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        (*playerVolItf)->SetMute(playerVolItf, XA_BOOLEAN_FALSE); // unmute
170de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block        ALOGV("unmuting player");
17137dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        break;
17237dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    case 800:
17337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        (*playerVolItf)->SetStereoPosition(playerVolItf, -1000);
17437dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_TRUE);
175de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block        ALOGV("pan sound to the left (hard-left)");
17637dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        break;
17737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    case 900:
17837dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        (*playerVolItf)->EnableStereoPosition(playerVolItf, XA_BOOLEAN_FALSE);
179de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block        ALOGV("disabling stereo position");
18037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        break;
18137dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    default:
18237dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        break;
18337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    }
18437dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi#endif
18537dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi
186ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // don't bother trying to read more data once we've hit EOF
187ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    if (reachedEof) {
188ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        goto exit;
189ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    }
190ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
191ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    size_t nbRead;
192ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // note we do call fread from multiple threads, but never concurrently
193ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    nbRead = fread(pBufferData, BUFFER_SIZE, 1, file);
194ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    if (nbRead > 0) {
195ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        assert(1 == nbRead);
196ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        res = (*caller)->Enqueue(caller, NULL /*pBufferContext*/,
197d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi                pBufferData /*pData*/,
198ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten                nbRead * BUFFER_SIZE /*dataLength*/,
199d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi                NULL /*pMsg*/,
200d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi                0 /*msgLength*/);
201ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        assert(XA_RESULT_SUCCESS == res);
202ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    } else {
203d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi        // signal EOS
204ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        XAAndroidBufferItem msgEos[1];
205ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        msgEos[0].itemKey = XA_ANDROID_ITEMKEY_EOS;
206ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        msgEos[0].itemSize = 0;
207d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi        // EOS message has no parameters, so the total size of the message is the size of the key
208d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi        //   plus the size if itemSize, both XAuint32
2096bc8af4e67051af7c86c311cb9c50e294e547500Jean-Michel Trivi        res = (*caller)->Enqueue(caller, (void *)&kEosBufferCntxt /*pBufferContext*/,
21037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi                NULL /*pData*/, 0 /*dataLength*/,
211ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten                msgEos /*pMsg*/,
212ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten                // FIXME == sizeof(BufferItem)? */
21337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi                sizeof(XAuint32)*2 /*msgLength*/);
214ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        assert(XA_RESULT_SUCCESS == res);
215ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        reachedEof = JNI_TRUE;
216eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    }
217eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi
218ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenexit:
219ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    ok = pthread_mutex_unlock(&mutex);
220ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    assert(0 == ok);
221eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    return XA_RESULT_SUCCESS;
222eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi}
223eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi
224eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi
22537dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivivoid StreamChangeCallback (XAStreamInformationItf caller,
22637dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        XAuint32 eventId,
22737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        XAuint32 streamIndex,
22837dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        void * pEventData,
22937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        void * pContext )
23037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi{
231de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block    ALOGV("StreamChangeCallback called for stream %u", streamIndex);
232ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // pContext was specified as NULL at RegisterStreamChangeCallback and is unused here
233ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    assert(NULL == pContext);
234ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    switch (eventId) {
235ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    case XA_STREAMCBEVENT_PROPERTYCHANGE: {
236ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        /** From spec 1.0.1:
237ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            "This event indicates that stream property change has occurred.
238ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            The streamIndex parameter identifies the stream with the property change.
239ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            The pEventData parameter for this event is not used and shall be ignored."
240ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten         */
241ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
242ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        XAresult res;
24337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        XAuint32 domain;
244ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        res = (*caller)->QueryStreamType(caller, streamIndex, &domain);
245ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        assert(XA_RESULT_SUCCESS == res);
246ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        switch (domain) {
247ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        case XA_DOMAINTYPE_VIDEO: {
248ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            XAVideoStreamInformation videoInfo;
249ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            res = (*caller)->QueryStreamInformation(caller, streamIndex, &videoInfo);
250ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            assert(XA_RESULT_SUCCESS == res);
2517f7fde185e2739a88ba1847fa583a7dec459ea43Steve Block            ALOGI("Found video size %u x %u, codec ID=%u, frameRate=%u, bitRate=%u, duration=%u ms",
252ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten                        videoInfo.width, videoInfo.height, videoInfo.codecId, videoInfo.frameRate,
253ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten                        videoInfo.bitRate, videoInfo.duration);
254ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        } break;
255ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        default:
256ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            fprintf(stderr, "Unexpected domain %u\n", domain);
257ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            break;
25837dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        }
259ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        } break;
260ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    default:
261ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        fprintf(stderr, "Unexpected stream event ID %u\n", eventId);
262ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        break;
26337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    }
26437dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi}
26537dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi
26637dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi
26797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten// create the engine and output mix objects
26897bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kastenvoid Java_com_example_nativemedia_NativeMedia_createEngine(JNIEnv* env, jclass clazz)
26997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten{
270eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    XAresult res;
27197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
27297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // create engine
273eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = xaCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
274eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
27597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
27697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // realize the engine
277eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = (*engineObject)->Realize(engineObject, XA_BOOLEAN_FALSE);
278eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
27997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
28097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // get the engine interface, which is needed in order to create other objects
281eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = (*engineObject)->GetInterface(engineObject, XA_IID_ENGINE, &engineEngine);
282eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
28397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
28497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // create output mix
285eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
286eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
28797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
28897bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // realize the output mix
289eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = (*outputMixObject)->Realize(outputMixObject, XA_BOOLEAN_FALSE);
290eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
29197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
29297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten}
29397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
29497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
295ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten// Enqueue the initial buffers, and optionally signal a discontinuity in the first buffer
296ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenstatic jboolean enqueueInitialBuffers(jboolean discontinuity)
297ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten{
298ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
299ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    /* Fill our cache */
300ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    size_t nbRead;
301ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    nbRead = fread(dataCache, BUFFER_SIZE, NB_BUFFERS, file);
302ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    if (nbRead <= 0) {
303ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        // could be premature EOF or I/O error
304ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        LOGE("Error filling cache, exiting\n");
305ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        return JNI_FALSE;
306ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    }
307ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    assert(1 <= nbRead && nbRead <= NB_BUFFERS);
308de7c7da8460de9fb1e8739978f25e1463e2e1666Steve Block    ALOGV("Initially queueing %u buffers of %u bytes each", nbRead, BUFFER_SIZE);
309ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
310ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    /* Enqueue the content of our cache before starting to play,
311ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten       we don't want to starve the player */
312ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    size_t i;
313ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    for (i = 0; i < nbRead; i++) {
314ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        XAresult res;
315ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        if (discontinuity) {
316ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            // signal discontinuity
317ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            XAAndroidBufferItem items[1];
318ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            items[0].itemKey = XA_ANDROID_ITEMKEY_DISCONTINUITY;
319ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            items[0].itemSize = 0;
320ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            // DISCONTINUITY message has no parameters,
321ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            //   so the total size of the message is the size of the key
322ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            //   plus the size if itemSize, both XAuint32
323ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            res = (*playerBQItf)->Enqueue(playerBQItf, NULL /*pBufferContext*/,
324ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten                    dataCache + i*BUFFER_SIZE, BUFFER_SIZE, items /*pMsg*/,
325ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten                    // FIXME == sizeof(BufferItem)? */
326ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten                    sizeof(XAuint32)*2 /*msgLength*/);
327ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            discontinuity = JNI_FALSE;
328ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        } else {
329ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            res = (*playerBQItf)->Enqueue(playerBQItf, NULL /*pBufferContext*/,
330ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten                    dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0);
331ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        }
332ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        assert(XA_RESULT_SUCCESS == res);
333ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    }
334ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
335ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    return JNI_TRUE;
336ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten}
337ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
338ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
33997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten// create streaming media player
34097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kastenjboolean Java_com_example_nativemedia_NativeMedia_createStreamingMediaPlayer(JNIEnv* env,
34197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten        jclass clazz, jstring filename)
34297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten{
343eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    XAresult res;
34497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
34597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // convert Java string to UTF-8
34697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    const char *utf8 = (*env)->GetStringUTFChars(env, filename, NULL);
34797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    assert(NULL != utf8);
34897bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
349eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    // open the file to play
350eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    file = fopen(utf8, "rb");
351eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    if (file == NULL) {
352eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        LOGE("Failed to open %s", utf8);
353eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        return JNI_FALSE;
354eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    }
355eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi
356eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    // configure data source
357d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi    XADataLocator_AndroidBufferQueue loc_abq = { XA_DATALOCATOR_ANDROIDBUFFERQUEUE, NB_BUFFERS };
358d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi    XADataFormat_MIME format_mime = {
359c3b82a293ed06001ba6d50f111608160c6065ef2Glenn Kasten            XA_DATAFORMAT_MIME, XA_ANDROID_MIME_MP2TS, XA_CONTAINERTYPE_MPEG_TS };
360eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    XADataSource dataSrc = {&loc_abq, &format_mime};
36197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
36297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // configure audio sink
363d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi    XADataLocator_OutputMix loc_outmix = { XA_DATALOCATOR_OUTPUTMIX, outputMixObject };
364d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi    XADataSink audioSnk = { &loc_outmix, NULL };
36597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
36697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // configure image video sink
367295395c087000a9d804f8d12d58dea4cd9e7d26aJean-Michel Trivi    XADataLocator_NativeDisplay loc_nd = {
368ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten            XA_DATALOCATOR_NATIVEDISPLAY,        // locatorType
36939310fca2e30101fa6e5168da443581cc60c20bfGlenn Kasten            // the video sink must be an ANativeWindow created from a Surface or SurfaceTexture
370ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten            (void*)theNativeWindow,              // hWindow
371ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten            // must be NULL
372ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten            NULL                                 // hDisplay
373ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten    };
37497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    XADataSink imageVideoSink = {&loc_nd, NULL};
37597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
376eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    // declare interfaces to use
37737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    XAboolean     required[NB_MAXAL_INTERFACES]
37837dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi                           = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE,           XA_BOOLEAN_TRUE};
37937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    XAInterfaceID iidArray[NB_MAXAL_INTERFACES]
38060ca9f9ef02f6e486c3338cb811f603dd7825c05Glenn Kasten                           = {XA_IID_PLAY,     XA_IID_ANDROIDBUFFERQUEUESOURCE,
38160ca9f9ef02f6e486c3338cb811f603dd7825c05Glenn Kasten                                               XA_IID_STREAMINFORMATION};
38237dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi
383eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi
38497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // create media player
385eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObj, &dataSrc,
386eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi            NULL, &audioSnk, &imageVideoSink, NULL, NULL,
387d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi            NB_MAXAL_INTERFACES /*XAuint32 numInterfaces*/,
388eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi            iidArray /*const XAInterfaceID *pInterfaceIds*/,
389eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi            required /*const XAboolean *pInterfaceRequired*/);
390eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
39197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
39297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // release the Java string and UTF-8
39397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    (*env)->ReleaseStringUTFChars(env, filename, utf8);
39497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
39597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // realize the player
396eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = (*playerObj)->Realize(playerObj, XA_BOOLEAN_FALSE);
397eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
39897bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
39997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // get the play interface
400eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = (*playerObj)->GetInterface(playerObj, XA_IID_PLAY, &playerPlayItf);
401eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
402eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi
40337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    // get the stream information interface (for video size)
40437dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    res = (*playerObj)->GetInterface(playerObj, XA_IID_STREAMINFORMATION, &playerStreamInfoItf);
40537dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
40637dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi
40737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    // get the volume interface
40837dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    res = (*playerObj)->GetInterface(playerObj, XA_IID_VOLUME, &playerVolItf);
40937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
41037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi
411eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    // get the Android buffer queue interface
41260ca9f9ef02f6e486c3338cb811f603dd7825c05Glenn Kasten    res = (*playerObj)->GetInterface(playerObj, XA_IID_ANDROIDBUFFERQUEUESOURCE, &playerBQItf);
413eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
414eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi
415ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // specify which events we want to be notified of
416ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    res = (*playerBQItf)->SetCallbackEventsMask(playerBQItf, XA_ANDROIDBUFFERQUEUEEVENT_PROCESSED);
417ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
418a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi    // use the play interface to set up a callback for the XA_PLAYEVENT_HEADATEND event */
419a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi    res = (*playerPlayItf)->SetCallbackEventsMask(playerPlayItf, XA_PLAYEVENT_HEADATEND);
420a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
421a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi    res = (*playerPlayItf)->RegisterCallback(playerPlayItf,
422a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi            PlayCallback /*callback*/, NULL /*pContext*/);
423a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
424a07e6cd61b12a5c6ed78adaa88a08abd028f5a64Jean-Michel Trivi
425eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    // register the callback from which OpenMAX AL can retrieve the data to play
426d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi    res = (*playerBQItf)->RegisterCallback(playerBQItf, AndroidBufferQueueCallback, NULL);
42737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
42837dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi
42937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    // we want to be notified of the video size once it's found, so we register a callback for that
43037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    res = (*playerStreamInfoItf)->RegisterStreamChangeCallback(playerStreamInfoItf,
43137dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi            StreamChangeCallback, NULL);
432d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi
433ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // enqueue the initial buffers
434ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    if (!enqueueInitialBuffers(JNI_FALSE)) {
435d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi        return JNI_FALSE;
436d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi    }
437d158d31a6bbb06426b71c3d097b7768bc3fb79a3Jean-Michel Trivi
438eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    // prepare the player
439eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PAUSED);
440eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
441eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi
44237dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    // set the volume
44337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    res = (*playerVolItf)->SetVolumeLevel(playerVolItf, 0);//-300);
44437dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    assert(XA_RESULT_SUCCESS == res);
44537dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi
44637dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    // start the playback
447eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PLAYING);
448eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        assert(XA_RESULT_SUCCESS == res);
44997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
45097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    return JNI_TRUE;
45197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten}
45297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
45397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
45497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten// set the playing state for the streaming media player
45597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kastenvoid Java_com_example_nativemedia_NativeMedia_setPlayingStreamingMediaPlayer(JNIEnv* env,
45697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten        jclass clazz, jboolean isPlaying)
45797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten{
458eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    XAresult res;
45997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
46097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // make sure the streaming media player was created
461eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    if (NULL != playerPlayItf) {
46297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
46397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten        // set the player's state
464eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        res = (*playerPlayItf)->SetPlayState(playerPlayItf, isPlaying ?
46597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten            XA_PLAYSTATE_PLAYING : XA_PLAYSTATE_PAUSED);
466eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        assert(XA_RESULT_SUCCESS == res);
46797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
46897bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    }
46997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
47097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten}
47197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
47297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
47397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten// shut down the native media system
47497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kastenvoid Java_com_example_nativemedia_NativeMedia_shutdown(JNIEnv* env, jclass clazz)
47597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten{
47697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // destroy streaming media player object, and invalidate all associated interfaces
477eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    if (playerObj != NULL) {
478eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        (*playerObj)->Destroy(playerObj);
479eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        playerObj = NULL;
480eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        playerPlayItf = NULL;
481eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        playerBQItf = NULL;
482ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        playerStreamInfoItf = NULL;
483ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        playerVolItf = NULL;
48497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    }
48597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
48697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // destroy output mix object, and invalidate all associated interfaces
48797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    if (outputMixObject != NULL) {
48897bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten        (*outputMixObject)->Destroy(outputMixObject);
48997bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten        outputMixObject = NULL;
49097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    }
49197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
49297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    // destroy engine object, and invalidate all associated interfaces
49397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    if (engineObject != NULL) {
49497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten        (*engineObject)->Destroy(engineObject);
49597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten        engineObject = NULL;
49697bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten        engineEngine = NULL;
49797bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten    }
49897bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
499eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    // close the file
500eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    if (file != NULL) {
501eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi        fclose(file);
502b66b0745d45f4c0c7620749185f7fb3306804ce3Glenn Kasten        file = NULL;
503eae4df541ba1d46f65d37e959baf2127aa632c93Jean-Michel Trivi    }
504295395c087000a9d804f8d12d58dea4cd9e7d26aJean-Michel Trivi
505295395c087000a9d804f8d12d58dea4cd9e7d26aJean-Michel Trivi    // make sure we don't leak native windows
506ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten    if (theNativeWindow != NULL) {
507ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten        ANativeWindow_release(theNativeWindow);
508b66b0745d45f4c0c7620749185f7fb3306804ce3Glenn Kasten        theNativeWindow = NULL;
509ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten    }
51097bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten}
51197bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
51297bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten
51397bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten// set the surface
51497bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kastenvoid Java_com_example_nativemedia_NativeMedia_setSurface(JNIEnv *env, jclass clazz, jobject surface)
51597bdbe13fc48640babe6c1ce270660476f04c3dfGlenn Kasten{
516295395c087000a9d804f8d12d58dea4cd9e7d26aJean-Michel Trivi    // obtain a native window from a Java surface
517295395c087000a9d804f8d12d58dea4cd9e7d26aJean-Michel Trivi    theNativeWindow = ANativeWindow_fromSurface(env, surface);
518ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten}
519ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten
520ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten
521ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten// rewind the streaming media player
522ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kastenvoid Java_com_example_nativemedia_NativeMedia_rewindStreamingMediaPlayer(JNIEnv *env, jclass clazz)
523ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten{
524ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    XAresult res;
525ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
526ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    // make sure the streaming media player was created
527ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    if (NULL != playerBQItf && NULL != file) {
528ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        // first wait for buffers currently in queue to be drained
529ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        int ok;
530ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        ok = pthread_mutex_lock(&mutex);
531ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        assert(0 == ok);
532ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        discontinuity = JNI_TRUE;
533ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        // wait for discontinuity request to be observed by buffer queue callback
534ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        // FIXME sorry, can't rewind after EOS
535ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        while (discontinuity && !reachedEof) {
536ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            ok = pthread_cond_wait(&cond, &mutex);
537ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten            assert(0 == ok);
538ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        }
539ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        ok = pthread_mutex_unlock(&mutex);
540ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten        assert(0 == ok);
541ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten    }
542ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten
543ac8c7318e1d7ec1358bbf924e1bc2cee45b44fc6Glenn Kasten}
544