native-media-jni.c revision 70c49ae2867094072a4365423417ea452bf82231
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <jni.h>
19#include <pthread.h>
20#include <string.h>
21#define LOG_TAG "NativeMedia"
22#include <utils/Log.h>
23
24#include "OMXAL/OpenMAXAL.h"
25#include "OMXAL/OpenMAXAL_Android.h"
26
27#include <android/native_window_jni.h>
28
29// define as 1 if ANativeWindow * is not supported as a video sink
30#define NO_NATIVE_WINDOW 1
31
32// engine interfaces
33static XAObjectItf engineObject = NULL;
34static XAEngineItf engineEngine;
35
36// output mix interfaces
37static XAObjectItf outputMixObject = NULL;
38
39// streaming media player interfaces
40static XAObjectItf             playerObj = NULL;
41static XAPlayItf               playerPlayItf = NULL;
42static XAAndroidBufferQueueItf playerBQItf = NULL;
43// number of required interfaces for the MediaPlayer creation
44#define NB_MAXAL_INTERFACES 2 // XAAndroidBufferQueueItf and XAPlayItf
45
46// cached surface where the video display happens
47#if NO_NATIVE_WINDOW
48static jobject theSurfaceOrSurfaceTexture;
49#else
50static ANativeWindow* theNativeWindow;
51#endif
52
53// number of buffers in our buffer queue
54#define NB_BUFFERS 16
55// we're streaming MPEG-2 transport stream data, operate on transport stream block size
56#define MPEG2_TS_BLOCK_SIZE 188
57// determines how much memory we're dedicating to memory caching
58#define BUFFER_SIZE 20*MPEG2_TS_BLOCK_SIZE // 20 is an arbitrary number chosen here
59
60// where we cache in memory the data to play
61char dataCache[BUFFER_SIZE * NB_BUFFERS];
62// handle of the file to play
63FILE *file;
64// has the app reached the end of the file
65char reachedEof = 0;
66
67// AndroidBufferQueueItf callback for an audio player
68XAresult AndroidBufferQueueCallback(
69        XAAndroidBufferQueueItf caller,
70        void *pContext,                /* input */
71        const void *pBufferData,       /* input */
72        XAuint32 dataSize,             /* input */
73        XAuint32 dataUsed,             /* input */
74        const XAAndroidBufferItem *pItems,/* input */
75        XAuint32 itemsLength           /* input */)
76{
77    // assert(BUFFER_SIZE <= dataSize);
78    size_t nbRead = fread((void*)pBufferData, 1, BUFFER_SIZE, file);
79    if (nbRead > 0) {
80        (*caller)->Enqueue(caller,
81                pBufferData /*pData*/,
82                nbRead /*dataLength*/,
83                NULL /*pMsg*/,
84                0 /*msgLength*/);
85    } else if (!reachedEof) {
86        // signal EOS
87        XAAndroidBufferItem msgEos;
88        msgEos.itemKey = XA_ANDROID_ITEMKEY_EOS;
89        msgEos.itemSize = 0;
90        // EOS message has no parameters, so the total size of the message is the size of the key
91        //   plus the size if itemSize, both XAuint32
92        (*caller)->Enqueue(caller, NULL /*pData*/, 0 /*dataLength*/,
93                        &msgEos /*pMsg*/,
94                        sizeof(XAuint32)*2 /*msgLength*/);
95        reachedEof = 1;
96    }
97
98    return XA_RESULT_SUCCESS;
99}
100
101
102// create the engine and output mix objects
103void Java_com_example_nativemedia_NativeMedia_createEngine(JNIEnv* env, jclass clazz)
104{
105    XAresult res;
106
107    // create engine
108    res = xaCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
109    assert(XA_RESULT_SUCCESS == res);
110
111    // realize the engine
112    res = (*engineObject)->Realize(engineObject, XA_BOOLEAN_FALSE);
113    assert(XA_RESULT_SUCCESS == res);
114
115    // get the engine interface, which is needed in order to create other objects
116    res = (*engineObject)->GetInterface(engineObject, XA_IID_ENGINE, &engineEngine);
117    assert(XA_RESULT_SUCCESS == res);
118
119    // create output mix
120    res = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
121    assert(XA_RESULT_SUCCESS == res);
122
123    // realize the output mix
124    res = (*outputMixObject)->Realize(outputMixObject, XA_BOOLEAN_FALSE);
125    assert(XA_RESULT_SUCCESS == res);
126
127}
128
129
130// create streaming media player
131jboolean Java_com_example_nativemedia_NativeMedia_createStreamingMediaPlayer(JNIEnv* env,
132        jclass clazz, jstring filename)
133{
134    XAresult res;
135
136    // convert Java string to UTF-8
137    const char *utf8 = (*env)->GetStringUTFChars(env, filename, NULL);
138    assert(NULL != utf8);
139
140    // open the file to play
141    file = fopen(utf8, "rb");
142    if (file == NULL) {
143        LOGE("Failed to open %s", utf8);
144        return JNI_FALSE;
145    }
146
147    // configure data source
148    XADataLocator_AndroidBufferQueue loc_abq = { XA_DATALOCATOR_ANDROIDBUFFERQUEUE, NB_BUFFERS };
149    XADataFormat_MIME format_mime = {
150            XA_DATAFORMAT_MIME, (XAchar *)"video/mp2ts", XA_CONTAINERTYPE_MPEG_TS };
151    XADataSource dataSrc = {&loc_abq, &format_mime};
152
153    // configure audio sink
154    XADataLocator_OutputMix loc_outmix = { XA_DATALOCATOR_OUTPUTMIX, outputMixObject };
155    XADataSink audioSnk = { &loc_outmix, NULL };
156
157    // configure image video sink
158    XADataLocator_NativeDisplay loc_nd = {
159            XA_DATALOCATOR_NATIVEDISPLAY,        // locatorType
160#if NO_NATIVE_WINDOW
161            (void *) theSurfaceOrSurfaceTexture, // jobject
162            (void *) env                         // JNIEnv *env
163#else
164            // later the video sink can be an ANativeWindow created from a Surface or SurfaceTexture
165            (void*)theNativeWindow,              // hWindow
166            // must be NULL
167            NULL                                 // hDisplay
168#endif
169    };
170    XADataSink imageVideoSink = {&loc_nd, NULL};
171
172    // declare interfaces to use
173    XAboolean     required[NB_MAXAL_INTERFACES] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE};
174    XAInterfaceID iidArray[NB_MAXAL_INTERFACES] = {XA_IID_PLAY,     XA_IID_ANDROIDBUFFERQUEUE};
175
176    // create media player
177    res = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObj, &dataSrc,
178            NULL, &audioSnk, &imageVideoSink, NULL, NULL,
179            NB_MAXAL_INTERFACES /*XAuint32 numInterfaces*/,
180            iidArray /*const XAInterfaceID *pInterfaceIds*/,
181            required /*const XAboolean *pInterfaceRequired*/);
182    assert(XA_RESULT_SUCCESS == res);
183
184    // release the Java string and UTF-8
185    (*env)->ReleaseStringUTFChars(env, filename, utf8);
186
187    // realize the player
188    res = (*playerObj)->Realize(playerObj, XA_BOOLEAN_FALSE);
189    assert(XA_RESULT_SUCCESS == res);
190
191    // get the play interface
192    res = (*playerObj)->GetInterface(playerObj, XA_IID_PLAY, &playerPlayItf);
193    assert(XA_RESULT_SUCCESS == res);
194
195    // get the Android buffer queue interface
196    res = (*playerObj)->GetInterface(playerObj, XA_IID_ANDROIDBUFFERQUEUE, &playerBQItf);
197    assert(XA_RESULT_SUCCESS == res);
198
199    // register the callback from which OpenMAX AL can retrieve the data to play
200    res = (*playerBQItf)->RegisterCallback(playerBQItf, AndroidBufferQueueCallback, NULL);
201
202    /* Fill our cache */
203    if (fread(dataCache, 1, BUFFER_SIZE * NB_BUFFERS, file) <= 0) {
204        LOGE("Error filling cache, exiting\n");
205        return JNI_FALSE;
206    }
207    /* Enqueue the content of our cache before starting to play,
208       we don't want to starve the player */
209    int i;
210    for (i=0 ; i < NB_BUFFERS ; i++) {
211        res = (*playerBQItf)->Enqueue(playerBQItf, dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0);
212        assert(XA_RESULT_SUCCESS == res);
213    }
214
215
216    // prepare the player
217    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PAUSED);
218    assert(XA_RESULT_SUCCESS == res);
219
220    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PLAYING);
221        assert(XA_RESULT_SUCCESS == res);
222
223    return JNI_TRUE;
224}
225
226
227// set the playing state for the streaming media player
228void Java_com_example_nativemedia_NativeMedia_setPlayingStreamingMediaPlayer(JNIEnv* env,
229        jclass clazz, jboolean isPlaying)
230{
231    XAresult res;
232
233    // make sure the streaming media player was created
234    if (NULL != playerPlayItf) {
235
236        // set the player's state
237        res = (*playerPlayItf)->SetPlayState(playerPlayItf, isPlaying ?
238            XA_PLAYSTATE_PLAYING : XA_PLAYSTATE_PAUSED);
239        assert(XA_RESULT_SUCCESS == res);
240
241    }
242
243}
244
245
246// shut down the native media system
247void Java_com_example_nativemedia_NativeMedia_shutdown(JNIEnv* env, jclass clazz)
248{
249    // destroy streaming media player object, and invalidate all associated interfaces
250    if (playerObj != NULL) {
251        (*playerObj)->Destroy(playerObj);
252        playerObj = NULL;
253        playerPlayItf = NULL;
254        playerBQItf = NULL;
255    }
256
257    // destroy output mix object, and invalidate all associated interfaces
258    if (outputMixObject != NULL) {
259        (*outputMixObject)->Destroy(outputMixObject);
260        outputMixObject = NULL;
261    }
262
263    // destroy engine object, and invalidate all associated interfaces
264    if (engineObject != NULL) {
265        (*engineObject)->Destroy(engineObject);
266        engineObject = NULL;
267        engineEngine = NULL;
268    }
269
270    // close the file
271    if (file != NULL) {
272        fclose(file);
273    }
274
275#if !NO_NATIVE_WINDOW
276    // make sure we don't leak native windows
277    if (theNativeWindow != NULL) {
278        ANativeWindow_release(theNativeWindow);
279    }
280#endif
281}
282
283
284// set the surface
285void Java_com_example_nativemedia_NativeMedia_setSurface(JNIEnv *env, jclass clazz, jobject surface)
286{
287#if NO_NATIVE_WINDOW
288    theSurfaceOrSurfaceTexture = surface;
289#else
290    // obtain a native window from a Java surface
291    theNativeWindow = ANativeWindow_fromSurface(env, surface);
292#endif
293}
294
295
296// set the surface texture
297void Java_com_example_nativemedia_NativeMedia_setSurfaceTexture(JNIEnv *env, jclass clazz,
298        jobject surfaceTexture)
299{
300#if NO_NATIVE_WINDOW
301    theSurfaceOrSurfaceTexture = surfaceTexture;
302#else
303    // obtain a native window from a Java surface texture
304    theNativeWindow = ANativeWindow_fromSurfaceTexture(env, surfaceTexture);
305#endif
306}
307