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