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