native-media-jni.c revision 295395c087000a9d804f8d12d58dea4cd9e7d26a
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//FIXME shouldn't be needed here, but needed for declaration of XA_IID_ANDROIDBUFFERQUEUE
24#include "SLES/OpenSLES.h"
25#include "SLES/OpenSLES_Android.h"
26#include "OMXAL/OpenMAXAL.h"
27#include "OMXAL/OpenMAXAL_Android.h"
28#include <android/native_window_jni.h>
29
30// engine interfaces
31static XAObjectItf engineObject = NULL;
32static XAEngineItf engineEngine;
33
34// output mix interfaces
35static XAObjectItf outputMixObject = NULL;
36
37// streaming media player interfaces
38static XAObjectItf             playerObj = NULL;
39static XAPlayItf               playerPlayItf = NULL;
40static XAAndroidBufferQueueItf playerBQItf = NULL;
41
42// cached surface where the video display happens
43static ANativeWindow* theNativeWindow;
44
45// handle of the file to play
46FILE *file;
47
48// AndroidBufferQueueItf callback for an audio player
49XAresult AndroidBufferQueueCallback(
50        XAAndroidBufferQueueItf caller,
51        void *pContext,
52        XAuint32 bufferId,
53        XAuint32 bufferLength,
54        void *pBufferDataLocation)
55
56{
57    size_t nbRead = fread(pBufferDataLocation, 1, bufferLength, file);
58
59    XAAbufferQueueEvent event = XA_ANDROIDBUFFERQUEUE_EVENT_NONE;
60    if (nbRead <= 0) {
61        event = XA_ANDROIDBUFFERQUEUE_EVENT_EOS;
62    } else {
63        event = XA_ANDROIDBUFFERQUEUE_EVENT_NONE; // no event to report
64    }
65
66    // enqueue the data right-away because in this example we're reading from a file, so we
67    // can afford to do that. When streaming from the network, we would write from our cache
68    // to this queue.
69    // last param is NULL because we've already written the data in the buffer queue
70    (*caller)->Enqueue(caller, bufferId, nbRead, event, NULL);
71
72    return XA_RESULT_SUCCESS;
73}
74
75
76// create the engine and output mix objects
77void Java_com_example_nativemedia_NativeMedia_createEngine(JNIEnv* env, jclass clazz)
78{
79    XAresult res;
80
81    // create engine
82    res = xaCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
83    assert(XA_RESULT_SUCCESS == res);
84
85    // realize the engine
86    res = (*engineObject)->Realize(engineObject, XA_BOOLEAN_FALSE);
87    assert(XA_RESULT_SUCCESS == res);
88
89    // get the engine interface, which is needed in order to create other objects
90    res = (*engineObject)->GetInterface(engineObject, XA_IID_ENGINE, &engineEngine);
91    assert(XA_RESULT_SUCCESS == res);
92
93    // create output mix
94    res = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
95    assert(XA_RESULT_SUCCESS == res);
96
97    // realize the output mix
98    res = (*outputMixObject)->Realize(outputMixObject, XA_BOOLEAN_FALSE);
99    assert(XA_RESULT_SUCCESS == res);
100
101}
102
103
104// create streaming media player
105jboolean Java_com_example_nativemedia_NativeMedia_createStreamingMediaPlayer(JNIEnv* env,
106        jclass clazz, jstring filename)
107{
108    XAresult res;
109
110    // convert Java string to UTF-8
111    const char *utf8 = (*env)->GetStringUTFChars(env, filename, NULL);
112    assert(NULL != utf8);
113
114    // open the file to play
115    file = fopen(utf8, "rb");
116    if (file == NULL) {
117        LOGE("Failed to open %s", utf8);
118        return JNI_FALSE;
119    }
120
121    // configure data source
122    XADataLocator_AndroidBufferQueue loc_abq = {XA_DATALOCATOR_ANDROIDBUFFERQUEUE,
123            0, 0 /* number of buffers and size of queue are ignored for now, subject to change */};
124    XADataFormat_MIME format_mime = {XA_DATAFORMAT_MIME, NULL, XA_CONTAINERTYPE_UNSPECIFIED};
125    XADataSource dataSrc = {&loc_abq, &format_mime};
126
127    // configure audio sink
128    XADataLocator_OutputMix loc_outmix = {XA_DATALOCATOR_OUTPUTMIX, outputMixObject};
129    XADataSink audioSnk = {&loc_outmix, NULL};
130
131    // configure image video sink
132    XADataLocator_NativeDisplay loc_nd = {
133            XA_DATALOCATOR_NATIVEDISPLAY /* locatorType */,
134            // currently the video sink only works on ANativeWindow created from a Surface
135            (void*)theNativeWindow       /* hWindow */,
136            // ignored here
137            0                            /* hDisplay */};
138    XADataSink imageVideoSink = {&loc_nd, NULL};
139
140    // declare interfaces to use
141    XAboolean     required[2] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE};
142    XAInterfaceID iidArray[2] = {XA_IID_PLAY,     XA_IID_ANDROIDBUFFERQUEUE};
143
144    // create media player
145    res = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObj, &dataSrc,
146            NULL, &audioSnk, &imageVideoSink, NULL, NULL,
147            2        /*XAuint32 numInterfaces*/,
148            iidArray /*const XAInterfaceID *pInterfaceIds*/,
149            required /*const XAboolean *pInterfaceRequired*/);
150    assert(XA_RESULT_SUCCESS == res);
151
152    // release the Java string and UTF-8
153    (*env)->ReleaseStringUTFChars(env, filename, utf8);
154
155    // realize the player
156    res = (*playerObj)->Realize(playerObj, XA_BOOLEAN_FALSE);
157    assert(XA_RESULT_SUCCESS == res);
158
159    // get the play interface
160    res = (*playerObj)->GetInterface(playerObj, XA_IID_PLAY, &playerPlayItf);
161    assert(XA_RESULT_SUCCESS == res);
162
163    // get the Android buffer queue interface
164    res = (*playerObj)->GetInterface(playerObj, XA_IID_ANDROIDBUFFERQUEUE, &playerBQItf);
165    assert(XA_RESULT_SUCCESS == res);
166
167    // register the callback from which OpenMAX AL can retrieve the data to play
168    res = (*playerBQItf)->RegisterCallback(playerBQItf, AndroidBufferQueueCallback, &playerBQItf);
169
170    // prepare the player
171    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PAUSED);
172    assert(XA_RESULT_SUCCESS == res);
173
174    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PLAYING);
175        assert(XA_RESULT_SUCCESS == res);
176
177    return JNI_TRUE;
178}
179
180
181// set the playing state for the streaming media player
182void Java_com_example_nativemedia_NativeMedia_setPlayingStreamingMediaPlayer(JNIEnv* env,
183        jclass clazz, jboolean isPlaying)
184{
185    XAresult res;
186
187    // make sure the streaming media player was created
188    if (NULL != playerPlayItf) {
189
190        // set the player's state
191        res = (*playerPlayItf)->SetPlayState(playerPlayItf, isPlaying ?
192            XA_PLAYSTATE_PLAYING : XA_PLAYSTATE_PAUSED);
193        assert(XA_RESULT_SUCCESS == res);
194
195    }
196
197}
198
199
200// shut down the native media system
201void Java_com_example_nativemedia_NativeMedia_shutdown(JNIEnv* env, jclass clazz)
202{
203    // destroy streaming media player object, and invalidate all associated interfaces
204    if (playerObj != NULL) {
205        (*playerObj)->Destroy(playerObj);
206        playerObj = NULL;
207        playerPlayItf = NULL;
208        playerBQItf = NULL;
209    }
210
211    // destroy output mix object, and invalidate all associated interfaces
212    if (outputMixObject != NULL) {
213        (*outputMixObject)->Destroy(outputMixObject);
214        outputMixObject = NULL;
215    }
216
217    // destroy engine object, and invalidate all associated interfaces
218    if (engineObject != NULL) {
219        (*engineObject)->Destroy(engineObject);
220        engineObject = NULL;
221        engineEngine = NULL;
222    }
223
224    // close the file
225    if (file != NULL) {
226        fclose(file);
227    }
228
229    // make sure we don't leak native windows
230    ANativeWindow_release(theNativeWindow);
231}
232
233
234// set the surface
235void Java_com_example_nativemedia_NativeMedia_setSurface(JNIEnv *env, jclass clazz, jobject surface)
236{
237    // obtain a native window from a Java surface
238    theNativeWindow = ANativeWindow_fromSurface(env, surface);
239}
240