native-media-jni.c revision d158d31a6bbb06426b71c3d097b7768bc3fb79a3
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// engine interfaces
30static XAObjectItf engineObject = NULL;
31static XAEngineItf engineEngine;
32
33// output mix interfaces
34static XAObjectItf outputMixObject = NULL;
35
36// streaming media player interfaces
37static XAObjectItf             playerObj = NULL;
38static XAPlayItf               playerPlayItf = NULL;
39static XAAndroidBufferQueueItf playerBQItf = NULL;
40// number of required interfaces for the MediaPlayer creation
41#define NB_MAXAL_INTERFACES 2 // XAAndroidBufferQueueItf and XAPlayItf
42
43// cached surface where the video display happens
44static ANativeWindow* theNativeWindow;
45
46// number of buffers in our buffer queue
47#define NB_BUFFERS 16
48// we're streaming MPEG-2 transport stream data, operate on transport stream block size
49#define MPEG2_TS_BLOCK_SIZE 188
50// determines how much memory we're dedicating to memory caching
51#define BUFFER_SIZE 20*MPEG2_TS_BLOCK_SIZE // 20 is an arbitrary number chosen here
52
53// where we cache in memory the data to play
54char dataCache[BUFFER_SIZE * NB_BUFFERS];
55// handle of the file to play
56FILE *file;
57
58// AndroidBufferQueueItf callback for an audio player
59XAresult AndroidBufferQueueCallback(
60        XAAndroidBufferQueueItf caller,
61        void *pContext,                /* input */
62        const void *pBufferData,       /* input */
63        XAuint32 dataSize,             /* input */
64        XAuint32 dataUsed,             /* input */
65        const XAAndroidBufferItem *pItems,/* input */
66        XAuint32 itemsLength           /* input */)
67{
68    // assert(BUFFER_SIZE <= dataSize);
69    size_t nbRead = fread((void*)pBufferData, 1, BUFFER_SIZE, file);
70    if (nbRead > 0) {
71        (*caller)->Enqueue(caller,
72                pBufferData /*pData*/,
73                nbRead /*dataLength*/,
74                NULL /*pMsg*/,
75                0 /*msgLength*/);
76    } else {
77        // signal EOS
78        XAAndroidBufferItem msgEos;
79        msgEos.itemKey = XA_ANDROID_ITEMKEY_EOS;
80        msgEos.itemSize = 0;
81        // EOS message has no parameters, so the total size of the message is the size of the key
82        //   plus the size if itemSize, both XAuint32
83        (*caller)->Enqueue(caller, NULL /*pData*/, 0 /*dataLength*/,
84                        &msgEos /*pMsg*/,
85                        sizeof(XAuint32)*2 /*msgLength*/);
86    }
87
88    return XA_RESULT_SUCCESS;
89}
90
91
92// create the engine and output mix objects
93void Java_com_example_nativemedia_NativeMedia_createEngine(JNIEnv* env, jclass clazz)
94{
95    XAresult res;
96
97    // create engine
98    res = xaCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
99    assert(XA_RESULT_SUCCESS == res);
100
101    // realize the engine
102    res = (*engineObject)->Realize(engineObject, XA_BOOLEAN_FALSE);
103    assert(XA_RESULT_SUCCESS == res);
104
105    // get the engine interface, which is needed in order to create other objects
106    res = (*engineObject)->GetInterface(engineObject, XA_IID_ENGINE, &engineEngine);
107    assert(XA_RESULT_SUCCESS == res);
108
109    // create output mix
110    res = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
111    assert(XA_RESULT_SUCCESS == res);
112
113    // realize the output mix
114    res = (*outputMixObject)->Realize(outputMixObject, XA_BOOLEAN_FALSE);
115    assert(XA_RESULT_SUCCESS == res);
116
117}
118
119
120// create streaming media player
121jboolean Java_com_example_nativemedia_NativeMedia_createStreamingMediaPlayer(JNIEnv* env,
122        jclass clazz, jstring filename)
123{
124    XAresult res;
125
126    // convert Java string to UTF-8
127    const char *utf8 = (*env)->GetStringUTFChars(env, filename, NULL);
128    assert(NULL != utf8);
129
130    // open the file to play
131    file = fopen(utf8, "rb");
132    if (file == NULL) {
133        LOGE("Failed to open %s", utf8);
134        return JNI_FALSE;
135    }
136
137    // configure data source
138    XADataLocator_AndroidBufferQueue loc_abq = { XA_DATALOCATOR_ANDROIDBUFFERQUEUE, NB_BUFFERS };
139    XADataFormat_MIME format_mime = {
140            XA_DATAFORMAT_MIME, (XAchar *)"video/mp2ts", XA_CONTAINERTYPE_MPEG_TS };
141    XADataSource dataSrc = {&loc_abq, &format_mime};
142
143    // configure audio sink
144    XADataLocator_OutputMix loc_outmix = { XA_DATALOCATOR_OUTPUTMIX, outputMixObject };
145    XADataSink audioSnk = { &loc_outmix, NULL };
146
147    // configure image video sink
148    XADataLocator_NativeDisplay loc_nd = {
149            XA_DATALOCATOR_NATIVEDISPLAY /* locatorType */,
150            // currently the video sink only works on ANativeWindow created from a Surface
151            (void*)theNativeWindow       /* hWindow */,
152            // ignored here
153            0                            /* hDisplay */};
154    XADataSink imageVideoSink = {&loc_nd, NULL};
155
156    // declare interfaces to use
157    XAboolean     required[NB_MAXAL_INTERFACES] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE};
158    XAInterfaceID iidArray[NB_MAXAL_INTERFACES] = {XA_IID_PLAY,     XA_IID_ANDROIDBUFFERQUEUE};
159
160    // create media player
161    res = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObj, &dataSrc,
162            NULL, &audioSnk, &imageVideoSink, NULL, NULL,
163            NB_MAXAL_INTERFACES /*XAuint32 numInterfaces*/,
164            iidArray /*const XAInterfaceID *pInterfaceIds*/,
165            required /*const XAboolean *pInterfaceRequired*/);
166    assert(XA_RESULT_SUCCESS == res);
167
168    // release the Java string and UTF-8
169    (*env)->ReleaseStringUTFChars(env, filename, utf8);
170
171    // realize the player
172    res = (*playerObj)->Realize(playerObj, XA_BOOLEAN_FALSE);
173    assert(XA_RESULT_SUCCESS == res);
174
175    // get the play interface
176    res = (*playerObj)->GetInterface(playerObj, XA_IID_PLAY, &playerPlayItf);
177    assert(XA_RESULT_SUCCESS == res);
178
179    // get the Android buffer queue interface
180    res = (*playerObj)->GetInterface(playerObj, XA_IID_ANDROIDBUFFERQUEUE, &playerBQItf);
181    assert(XA_RESULT_SUCCESS == res);
182
183    // register the callback from which OpenMAX AL can retrieve the data to play
184    res = (*playerBQItf)->RegisterCallback(playerBQItf, AndroidBufferQueueCallback, NULL);
185
186    /* Fill our cache */
187    if (fread(dataCache, 1, BUFFER_SIZE * NB_BUFFERS, file) <= 0) {
188        LOGE("Error filling cache, exiting\n");
189        return JNI_FALSE;
190    }
191    /* Enqueue the content of our cache before starting to play,
192       we don't want to starve the player */
193    int i;
194    for (i=0 ; i < NB_BUFFERS ; i++) {
195        res = (*playerBQItf)->Enqueue(playerBQItf, dataCache + i*BUFFER_SIZE, BUFFER_SIZE, NULL, 0);
196        assert(XA_RESULT_SUCCESS == res);
197    }
198
199
200    // prepare the player
201    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PAUSED);
202    assert(XA_RESULT_SUCCESS == res);
203
204    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PLAYING);
205        assert(XA_RESULT_SUCCESS == res);
206
207    return JNI_TRUE;
208}
209
210
211// set the playing state for the streaming media player
212void Java_com_example_nativemedia_NativeMedia_setPlayingStreamingMediaPlayer(JNIEnv* env,
213        jclass clazz, jboolean isPlaying)
214{
215    XAresult res;
216
217    // make sure the streaming media player was created
218    if (NULL != playerPlayItf) {
219
220        // set the player's state
221        res = (*playerPlayItf)->SetPlayState(playerPlayItf, isPlaying ?
222            XA_PLAYSTATE_PLAYING : XA_PLAYSTATE_PAUSED);
223        assert(XA_RESULT_SUCCESS == res);
224
225    }
226
227}
228
229
230// shut down the native media system
231void Java_com_example_nativemedia_NativeMedia_shutdown(JNIEnv* env, jclass clazz)
232{
233    // destroy streaming media player object, and invalidate all associated interfaces
234    if (playerObj != NULL) {
235        (*playerObj)->Destroy(playerObj);
236        playerObj = NULL;
237        playerPlayItf = NULL;
238        playerBQItf = NULL;
239    }
240
241    // destroy output mix object, and invalidate all associated interfaces
242    if (outputMixObject != NULL) {
243        (*outputMixObject)->Destroy(outputMixObject);
244        outputMixObject = NULL;
245    }
246
247    // destroy engine object, and invalidate all associated interfaces
248    if (engineObject != NULL) {
249        (*engineObject)->Destroy(engineObject);
250        engineObject = NULL;
251        engineEngine = NULL;
252    }
253
254    // close the file
255    if (file != NULL) {
256        fclose(file);
257    }
258
259    // make sure we don't leak native windows
260    ANativeWindow_release(theNativeWindow);
261}
262
263
264// set the surface
265void Java_com_example_nativemedia_NativeMedia_setSurface(JNIEnv *env, jclass clazz, jobject surface)
266{
267    // obtain a native window from a Java surface
268    theNativeWindow = ANativeWindow_fromSurface(env, surface);
269}
270