android_media_MediaMetadataRetriever.cpp revision a52b4d764ebd79b29eac296a5c3a629cff1990c4
1/* 2** 3** Copyright 2008, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18//#define LOG_NDEBUG 0 19#define LOG_TAG "MediaMetadataRetrieverJNI" 20 21#include <assert.h> 22#include <utils/Log.h> 23#include <utils/threads.h> 24#include <core/SkBitmap.h> 25#include <media/mediametadataretriever.h> 26#include <private/media/VideoFrame.h> 27 28#include "jni.h" 29#include "JNIHelp.h" 30#include "android_runtime/AndroidRuntime.h" 31 32 33using namespace android; 34 35struct fields_t { 36 jfieldID context; 37 jclass bitmapClazz; 38 jmethodID bitmapConstructor; 39}; 40 41static fields_t fields; 42static Mutex sLock; 43static const char* const kClassPathName = "android/media/MediaMetadataRetriever"; 44 45static void process_media_retriever_call(JNIEnv *env, status_t opStatus, const char* exception, const char *message) 46{ 47 if (opStatus == (status_t) INVALID_OPERATION) { 48 jniThrowException(env, "java/lang/IllegalStateException", NULL); 49 } else if (opStatus != (status_t) OK) { 50 if (strlen(message) > 230) { 51 // If the message is too long, don't bother displaying the status code. 52 jniThrowException( env, exception, message); 53 } else { 54 char msg[256]; 55 // Append the status code to the message. 56 sprintf(msg, "%s: status = 0x%X", message, opStatus); 57 jniThrowException( env, exception, msg); 58 } 59 } 60} 61 62static MediaMetadataRetriever* getRetriever(JNIEnv* env, jobject thiz) 63{ 64 // No lock is needed, since it is called internally by other methods that are protected 65 MediaMetadataRetriever* retriever = (MediaMetadataRetriever*) env->GetIntField(thiz, fields.context); 66 return retriever; 67} 68 69static void setRetriever(JNIEnv* env, jobject thiz, int retriever) 70{ 71 // No lock is needed, since it is called internally by other methods that are protected 72 MediaMetadataRetriever *old = (MediaMetadataRetriever*) env->GetIntField(thiz, fields.context); 73 env->SetIntField(thiz, fields.context, retriever); 74} 75 76static void android_media_MediaMetadataRetriever_setDataSource(JNIEnv *env, jobject thiz, jstring path) 77{ 78 LOGV("setDataSource"); 79 Mutex::Autolock lock(sLock); 80 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 81 if (retriever == 0) { 82 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 83 return; 84 } 85 if (!path) { 86 jniThrowException(env, "java/lang/IllegalArgumentException", "Null pointer"); 87 return; 88 } 89 90 const char *pathStr = env->GetStringUTFChars(path, NULL); 91 if (!pathStr) { // OutOfMemoryError exception already thrown 92 return; 93 } 94 95 // Don't let somebody trick us in to reading some random block of memory 96 if (strncmp("mem://", pathStr, 6) == 0) { 97 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid pathname"); 98 return; 99 } 100 101 process_media_retriever_call(env, retriever->setDataSource(pathStr), "java/lang/RuntimeException", "setDataSource failed"); 102 env->ReleaseStringUTFChars(path, pathStr); 103} 104 105static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) 106{ 107 LOGV("setDataSource"); 108 Mutex::Autolock lock(sLock); 109 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 110 if (retriever == 0) { 111 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 112 return; 113 } 114 if (!fileDescriptor) { 115 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 116 return; 117 } 118 int fd = getParcelFileDescriptorFD(env, fileDescriptor); 119 if (offset < 0 || length < 0 || fd < 0) { 120 if (offset < 0) { 121 LOGE("negative offset (%lld)", offset); 122 } 123 if (length < 0) { 124 LOGE("negative length (%lld)", length); 125 } 126 if (fd < 0) { 127 LOGE("invalid file descriptor"); 128 } 129 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 130 return; 131 } 132 process_media_retriever_call(env, retriever->setDataSource(fd, offset, length), "java/lang/RuntimeException", "setDataSource failed"); 133} 134 135static void android_media_MediaMetadataRetriever_setMode(JNIEnv *env, jobject thiz, jint mode) 136{ 137 LOGV("setMode"); 138 Mutex::Autolock lock(sLock); 139 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 140 if (retriever == 0) { 141 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 142 return; 143 } 144 process_media_retriever_call(env, retriever->setMode(mode), "java/lang/RuntimeException", "setMode failed"); 145} 146 147static int android_media_MediaMetadataRetriever_getMode(JNIEnv *env, jobject thiz) 148{ 149 LOGV("getMode"); 150 Mutex::Autolock lock(sLock); 151 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 152 if (retriever == 0) { 153 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 154 return -1; // Error 155 } 156 int mode = -1; 157 retriever->getMode(&mode); 158 return mode; 159} 160 161static jobject android_media_MediaMetadataRetriever_captureFrame(JNIEnv *env, jobject thiz) 162{ 163 LOGV("captureFrame"); 164 Mutex::Autolock lock(sLock); 165 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 166 if (retriever == 0) { 167 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 168 return NULL; 169 } 170 171 // Call native method to retrieve a video frame 172 VideoFrame *videoFrame = NULL; 173 sp<IMemory> frameMemory = retriever->captureFrame(); 174 if (frameMemory != 0) { // cast the shared structure to a VideoFrame object 175 videoFrame = static_cast<VideoFrame *>(frameMemory->pointer()); 176 } 177 if (videoFrame == NULL) { 178 LOGE("captureFrame: videoFrame is a NULL pointer"); 179 return NULL; 180 } 181 182 // Create a SkBitmap to hold the pixels 183 SkBitmap *bitmap = new SkBitmap(); 184 if (bitmap == NULL) { 185 LOGE("captureFrame: cannot instantiate a SkBitmap object."); 186 return NULL; 187 } 188 bitmap->setConfig(SkBitmap::kRGB_565_Config, videoFrame->mDisplayWidth, videoFrame->mDisplayHeight); 189 if (!bitmap->allocPixels()) { 190 delete bitmap; 191 LOGE("failed to allocate pixel buffer"); 192 return NULL; 193 } 194 memcpy((uint8_t*)bitmap->getPixels(), (uint8_t*)videoFrame + sizeof(VideoFrame), videoFrame->mSize); 195 196 // Since internally SkBitmap uses reference count to manage the reference to 197 // its pixels, it is important that the pixels (along with SkBitmap) be 198 // available after creating the Bitmap is returned to Java app. 199 return env->NewObject(fields.bitmapClazz, fields.bitmapConstructor, (int) bitmap, true, NULL, -1); 200} 201 202static jbyteArray android_media_MediaMetadataRetriever_extractAlbumArt(JNIEnv *env, jobject thiz) 203{ 204 LOGV("extractAlbumArt"); 205 Mutex::Autolock lock(sLock); 206 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 207 if (retriever == 0) { 208 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 209 return NULL; 210 } 211 MediaAlbumArt* mediaAlbumArt = NULL; 212 sp<IMemory> albumArtMemory = retriever->extractAlbumArt(); 213 if (albumArtMemory != 0) { // cast the shared structure to a MediaAlbumArt object 214 mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer()); 215 } 216 if (mediaAlbumArt == NULL) { 217 LOGE("extractAlbumArt: Call to extractAlbumArt failed."); 218 return NULL; 219 } 220 221 unsigned int len = mediaAlbumArt->mSize; 222 char* data = (char*) mediaAlbumArt + sizeof(MediaAlbumArt); 223 jbyteArray array = env->NewByteArray(len); 224 if (!array) { // OutOfMemoryError exception has already been thrown. 225 LOGE("extractAlbumArt: OutOfMemoryError is thrown."); 226 } else { 227 jbyte* bytes = env->GetByteArrayElements(array, NULL); 228 if (bytes != NULL) { 229 memcpy(bytes, data, len); 230 env->ReleaseByteArrayElements(array, bytes, 0); 231 } 232 } 233 234 // No need to delete mediaAlbumArt here 235 return array; 236} 237 238static jobject android_media_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jobject thiz, jint keyCode) 239{ 240 LOGV("extractMetadata"); 241 Mutex::Autolock lock(sLock); 242 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 243 if (retriever == 0) { 244 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 245 return NULL; 246 } 247 const char* value = retriever->extractMetadata(keyCode); 248 if (!value) { 249 LOGV("extractMetadata: Metadata is not found"); 250 return NULL; 251 } 252 LOGV("extractMetadata: value (%s) for keyCode(%d)", value, keyCode); 253 return env->NewStringUTF(value); 254} 255 256static void android_media_MediaMetadataRetriever_release(JNIEnv *env, jobject thiz) 257{ 258 LOGV("release"); 259 Mutex::Autolock lock(sLock); 260 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 261 delete retriever; 262 setRetriever(env, thiz, 0); 263} 264 265static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz) 266{ 267 LOGV("native_finalize"); 268 269 // No lock is needed, since android_media_MediaMetadataRetriever_release() is protected 270 android_media_MediaMetadataRetriever_release(env, thiz); 271} 272 273// This function gets a field ID, which in turn causes class initialization. 274// It is called from a static block in MediaMetadataRetriever, which won't run until the 275// first time an instance of this class is used. 276static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env) 277{ 278 jclass clazz = env->FindClass(kClassPathName); 279 if (clazz == NULL) { 280 jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaMetadataRetriever"); 281 return; 282 } 283 284 fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 285 if (fields.context == NULL) { 286 jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaMetadataRetriever.mNativeContext"); 287 return; 288 } 289 290 fields.bitmapClazz = env->FindClass("android/graphics/Bitmap"); 291 if (fields.bitmapClazz == NULL) { 292 jniThrowException(env, "java/lang/RuntimeException", "Can't find android/graphics/Bitmap"); 293 return; 294 } 295 296 fields.bitmapConstructor = env->GetMethodID(fields.bitmapClazz, "<init>", "(IZ[BI)V"); 297 if (fields.bitmapConstructor == NULL) { 298 jniThrowException(env, "java/lang/RuntimeException", "Can't find Bitmap constructor"); 299 return; 300 } 301} 302 303static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz) 304{ 305 LOGV("native_setup"); 306 MediaMetadataRetriever* retriever = new MediaMetadataRetriever(); 307 if (retriever == 0) { 308 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 309 return; 310 } 311 setRetriever(env, thiz, (int)retriever); 312} 313 314// JNI mapping between Java methods and native methods 315static JNINativeMethod nativeMethods[] = { 316 {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSource}, 317 {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD}, 318 {"setMode", "(I)V", (void *)android_media_MediaMetadataRetriever_setMode}, 319 {"getMode", "()I", (void *)android_media_MediaMetadataRetriever_getMode}, 320 {"captureFrame", "()Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_captureFrame}, 321 {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata}, 322 {"extractAlbumArt", "()[B", (void *)android_media_MediaMetadataRetriever_extractAlbumArt}, 323 {"release", "()V", (void *)android_media_MediaMetadataRetriever_release}, 324 {"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize}, 325 {"native_setup", "()V", (void *)android_media_MediaMetadataRetriever_native_setup}, 326 {"native_init", "()V", (void *)android_media_MediaMetadataRetriever_native_init}, 327}; 328 329// This function only registers the native methods, and is called from 330// JNI_OnLoad in android_media_MediaPlayer.cpp 331int register_android_media_MediaMetadataRetriever(JNIEnv *env) 332{ 333 return AndroidRuntime::registerNativeMethods 334 (env, kClassPathName, nativeMethods, NELEM(nativeMethods)); 335} 336