native-media-jni.c revision eae4df541ba1d46f65d37e959baf2127aa632c93
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
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
41// cached surface
42static jobject theSurface;
43static JNIEnv *theEnv;
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, 0, 0};
123    XADataFormat_MIME format_mime = {XA_DATAFORMAT_MIME, NULL, XA_CONTAINERTYPE_UNSPECIFIED};
124    XADataSource dataSrc = {&loc_abq, &format_mime};
125
126    // configure audio sink
127    XADataLocator_OutputMix loc_outmix = {XA_DATALOCATOR_OUTPUTMIX, outputMixObject};
128    XADataSink audioSnk = {&loc_outmix, NULL};
129
130    // configure image video sink
131    XADataLocator_NativeDisplay loc_nd = {XA_DATALOCATOR_NATIVEDISPLAY, theSurface, theEnv};
132    XADataSink imageVideoSink = {&loc_nd, NULL};
133
134    // declare interfaces to use
135    XAboolean     required[2] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE};
136    XAInterfaceID iidArray[2] = {XA_IID_PLAY,     XA_IID_ANDROIDBUFFERQUEUE};
137
138    // create media player
139    res = (*engineEngine)->CreateMediaPlayer(engineEngine, &playerObj, &dataSrc,
140            NULL, &audioSnk, &imageVideoSink, NULL, NULL,
141            2 /*XAuint32 numInterfaces*/,
142            iidArray /*const XAInterfaceID *pInterfaceIds*/,
143            required /*const XAboolean *pInterfaceRequired*/);
144    assert(XA_RESULT_SUCCESS == res);
145
146    // release the Java string and UTF-8
147    (*env)->ReleaseStringUTFChars(env, filename, utf8);
148
149    // realize the player
150    res = (*playerObj)->Realize(playerObj, XA_BOOLEAN_FALSE);
151    assert(XA_RESULT_SUCCESS == res);
152
153    // get the play interface
154    res = (*playerObj)->GetInterface(playerObj, XA_IID_PLAY, &playerPlayItf);
155    assert(XA_RESULT_SUCCESS == res);
156
157    // get the Android buffer queue interface
158    res = (*playerObj)->GetInterface(playerObj, XA_IID_ANDROIDBUFFERQUEUE, &playerBQItf);
159    assert(XA_RESULT_SUCCESS == res);
160
161    // register the callback from which OpenMAX AL can retrieve the data to play
162    res = (*playerBQItf)->RegisterCallback(playerBQItf, AndroidBufferQueueCallback, &playerBQItf);
163
164    // prepare the player
165    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PAUSED);
166    assert(XA_RESULT_SUCCESS == res);
167
168    res = (*playerPlayItf)->SetPlayState(playerPlayItf, XA_PLAYSTATE_PLAYING);
169        assert(XA_RESULT_SUCCESS == res);
170
171    return JNI_TRUE;
172}
173
174
175// set the playing state for the streaming media player
176void Java_com_example_nativemedia_NativeMedia_setPlayingStreamingMediaPlayer(JNIEnv* env,
177        jclass clazz, jboolean isPlaying)
178{
179    XAresult res;
180
181    // make sure the streaming media player was created
182    if (NULL != playerPlayItf) {
183
184        // set the player's state
185        res = (*playerPlayItf)->SetPlayState(playerPlayItf, isPlaying ?
186            XA_PLAYSTATE_PLAYING : XA_PLAYSTATE_PAUSED);
187        assert(XA_RESULT_SUCCESS == res);
188
189    }
190
191}
192
193
194// shut down the native media system
195void Java_com_example_nativemedia_NativeMedia_shutdown(JNIEnv* env, jclass clazz)
196{
197
198    // destroy streaming media player object, and invalidate all associated interfaces
199    if (playerObj != NULL) {
200        (*playerObj)->Destroy(playerObj);
201        playerObj = NULL;
202        playerPlayItf = NULL;
203        playerBQItf = NULL;
204    }
205
206    // destroy output mix object, and invalidate all associated interfaces
207    if (outputMixObject != NULL) {
208        (*outputMixObject)->Destroy(outputMixObject);
209        outputMixObject = NULL;
210    }
211
212    // destroy engine object, and invalidate all associated interfaces
213    if (engineObject != NULL) {
214        (*engineObject)->Destroy(engineObject);
215        engineObject = NULL;
216        engineEngine = NULL;
217    }
218
219    // close the file
220    if (file != NULL) {
221        fclose(file);
222    }
223}
224
225
226// set the surface
227void Java_com_example_nativemedia_NativeMedia_setSurface(JNIEnv *env, jclass clazz, jobject surface)
228{
229    theEnv = env;
230    theSurface = surface;
231}
232