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