android_media_MediaMetadataRetriever.cpp revision b798689749c64baba81f02e10cf2157c747d6b46
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; 43 44static void process_media_retriever_call(JNIEnv *env, status_t opStatus, const char* exception, const char *message) 45{ 46 if (opStatus == (status_t) INVALID_OPERATION) { 47 jniThrowException(env, "java/lang/IllegalStateException", NULL); 48 } else if (opStatus != (status_t) OK) { 49 if (strlen(message) > 230) { 50 // If the message is too long, don't bother displaying the status code. 51 jniThrowException( env, exception, message); 52 } else { 53 char msg[256]; 54 // Append the status code to the message. 55 sprintf(msg, "%s: status = 0x%X", message, opStatus); 56 jniThrowException( env, exception, msg); 57 } 58 } 59} 60 61static MediaMetadataRetriever* getRetriever(JNIEnv* env, jobject thiz) 62{ 63 // No lock is needed, since it is called internally by other methods that are protected 64 MediaMetadataRetriever* retriever = (MediaMetadataRetriever*) env->GetIntField(thiz, fields.context); 65 return retriever; 66} 67 68static void setRetriever(JNIEnv* env, jobject thiz, int retriever) 69{ 70 // No lock is needed, since it is called internally by other methods that are protected 71 MediaMetadataRetriever *old = (MediaMetadataRetriever*) env->GetIntField(thiz, fields.context); 72 env->SetIntField(thiz, fields.context, retriever); 73} 74 75static void android_media_MediaMetadataRetriever_setDataSource(JNIEnv *env, jobject thiz, jstring path) 76{ 77 LOGV("setDataSource"); 78 Mutex::Autolock lock(sLock); 79 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 80 if (retriever == 0) { 81 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 82 return; 83 } 84 if (!path) { 85 jniThrowException(env, "java/lang/IllegalArgumentException", "Null pointer"); 86 return; 87 } 88 89 const char *pathStr = env->GetStringUTFChars(path, NULL); 90 if (!pathStr) { // OutOfMemoryError exception already thrown 91 return; 92 } 93 94 // Don't let somebody trick us in to reading some random block of memory 95 if (strncmp("mem://", pathStr, 6) == 0) { 96 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid pathname"); 97 return; 98 } 99 100 process_media_retriever_call(env, retriever->setDataSource(pathStr), "java/lang/RuntimeException", "setDataSource failed"); 101 env->ReleaseStringUTFChars(path, pathStr); 102} 103 104static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) 105{ 106 LOGV("setDataSource"); 107 Mutex::Autolock lock(sLock); 108 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 109 if (retriever == 0) { 110 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 111 return; 112 } 113 if (!fileDescriptor) { 114 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 115 return; 116 } 117 int fd = getParcelFileDescriptorFD(env, fileDescriptor); 118 if (offset < 0 || length < 0 || fd < 0) { 119 if (offset < 0) { 120 LOGE("negative offset (%lld)", offset); 121 } 122 if (length < 0) { 123 LOGE("negative length (%lld)", length); 124 } 125 if (fd < 0) { 126 LOGE("invalid file descriptor"); 127 } 128 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 129 return; 130 } 131 process_media_retriever_call(env, retriever->setDataSource(fd, offset, length), "java/lang/RuntimeException", "setDataSource failed"); 132} 133 134static void android_media_MediaMetadataRetriever_setMode(JNIEnv *env, jobject thiz, jint mode) 135{ 136 LOGV("setMode"); 137 Mutex::Autolock lock(sLock); 138 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 139 if (retriever == 0) { 140 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 141 return; 142 } 143 process_media_retriever_call(env, retriever->setMode(mode), "java/lang/RuntimeException", "setMode failed"); 144} 145 146static int android_media_MediaMetadataRetriever_getMode(JNIEnv *env, jobject thiz) 147{ 148 LOGV("getMode"); 149 Mutex::Autolock lock(sLock); 150 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 151 if (retriever == 0) { 152 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 153 return -1; // Error 154 } 155 int mode = -1; 156 retriever->getMode(&mode); 157 return mode; 158} 159 160static jobject android_media_MediaMetadataRetriever_captureFrame(JNIEnv *env, jobject thiz) 161{ 162 LOGV("captureFrame"); 163 Mutex::Autolock lock(sLock); 164 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 165 if (retriever == 0) { 166 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 167 return NULL; 168 } 169 170 // Call native method to retrieve a video frame 171 VideoFrame *videoFrame = NULL; 172 sp<IMemory> frameMemory = retriever->captureFrame(); 173 if (frameMemory != 0) { // cast the shared structure to a VideoFrame object 174 videoFrame = static_cast<VideoFrame *>(frameMemory->pointer()); 175 } 176 if (videoFrame == NULL) { 177 LOGE("captureFrame: videoFrame is a NULL pointer"); 178 return NULL; 179 } 180 181 // Create a SkBitmap to hold the pixels 182 SkBitmap *bitmap = new SkBitmap(); 183 if (bitmap == NULL) { 184 LOGE("captureFrame: cannot instantiate a SkBitmap object."); 185 return NULL; 186 } 187 bitmap->setConfig(SkBitmap::kRGB_565_Config, videoFrame->mDisplayWidth, videoFrame->mDisplayHeight); 188 if (!bitmap->allocPixels()) { 189 delete bitmap; 190 LOGE("failed to allocate pixel buffer"); 191 return NULL; 192 } 193 memcpy((uint8_t*)bitmap->getPixels(), (uint8_t*)videoFrame + sizeof(VideoFrame), videoFrame->mSize); 194 195 // Since internally SkBitmap uses reference count to manage the reference to 196 // its pixels, it is important that the pixels (along with SkBitmap) be 197 // available after creating the Bitmap is returned to Java app. 198 return env->NewObject(fields.bitmapClazz, fields.bitmapConstructor, (int) bitmap, true, NULL); 199} 200 201static jbyteArray android_media_MediaMetadataRetriever_extractAlbumArt(JNIEnv *env, jobject thiz) 202{ 203 LOGV("extractAlbumArt"); 204 Mutex::Autolock lock(sLock); 205 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 206 if (retriever == 0) { 207 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 208 return NULL; 209 } 210 MediaAlbumArt* mediaAlbumArt = NULL; 211 sp<IMemory> albumArtMemory = retriever->extractAlbumArt(); 212 if (albumArtMemory != 0) { // cast the shared structure to a MediaAlbumArt object 213 mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer()); 214 } 215 if (mediaAlbumArt == NULL) { 216 LOGE("extractAlbumArt: Call to extractAlbumArt failed."); 217 return NULL; 218 } 219 220 unsigned int len = mediaAlbumArt->mSize; 221 char* data = (char*) mediaAlbumArt + sizeof(MediaAlbumArt); 222 jbyteArray array = env->NewByteArray(len); 223 if (!array) { // OutOfMemoryError exception has already been thrown. 224 LOGE("extractAlbumArt: OutOfMemoryError is thrown."); 225 } else { 226 jbyte* bytes = env->GetByteArrayElements(array, NULL); 227 if (bytes != NULL) { 228 memcpy(bytes, data, len); 229 env->ReleaseByteArrayElements(array, bytes, 0); 230 } 231 } 232 233 // No need to delete mediaAlbumArt here 234 return array; 235} 236 237static jobject android_media_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jobject thiz, jint keyCode) 238{ 239 LOGV("extractMetadata"); 240 Mutex::Autolock lock(sLock); 241 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 242 if (retriever == 0) { 243 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); 244 return NULL; 245 } 246 const char* value = retriever->extractMetadata(keyCode); 247 if (!value) { 248 LOGV("extractMetadata: Metadata is not found"); 249 return NULL; 250 } 251 LOGV("extractMetadata: value (%s) for keyCode(%d)", value, keyCode); 252 return env->NewStringUTF(value); 253} 254 255static void android_media_MediaMetadataRetriever_release(JNIEnv *env, jobject thiz) 256{ 257 LOGV("release"); 258 Mutex::Autolock lock(sLock); 259 MediaMetadataRetriever* retriever = getRetriever(env, thiz); 260 delete retriever; 261 setRetriever(env, thiz, 0); 262} 263 264static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz) 265{ 266 LOGV("native_finalize"); 267 268 // No lock is needed, since android_media_MediaMetadataRetriever_release() is protected 269 android_media_MediaMetadataRetriever_release(env, thiz); 270} 271 272static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz) 273{ 274 LOGV("native_setup"); 275 MediaMetadataRetriever* retriever = new MediaMetadataRetriever(); 276 if (retriever == 0) { 277 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 278 return; 279 } 280 setRetriever(env, thiz, (int)retriever); 281} 282 283// JNI mapping between Java methods and native methods 284static JNINativeMethod nativeMethods[] = { 285 {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSource}, 286 {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD}, 287 {"setMode", "(I)V", (void *)android_media_MediaMetadataRetriever_setMode}, 288 {"getMode", "()I", (void *)android_media_MediaMetadataRetriever_getMode}, 289 {"captureFrame", "()Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_captureFrame}, 290 {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata}, 291 {"extractAlbumArt", "()[B", (void *)android_media_MediaMetadataRetriever_extractAlbumArt}, 292 {"release", "()V", (void *)android_media_MediaMetadataRetriever_release}, 293 {"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize}, 294 {"native_setup", "()V", (void *)android_media_MediaMetadataRetriever_native_setup}, 295}; 296 297// Register native mehtods with Android runtime environment 298int register_android_media_MediaMetadataRetriever(JNIEnv *env) 299{ 300 static const char* const kClassPathName = "android/media/MediaMetadataRetriever"; 301 jclass clazz = env->FindClass(kClassPathName); 302 if (clazz == NULL) { 303 LOGE("Can't find class: %s", kClassPathName); 304 return -1; 305 } 306 307 fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 308 if (fields.context == NULL) { 309 LOGE("Can't find MediaMetadataRetriever.mNativeContext"); 310 return -1; 311 } 312 313 fields.bitmapClazz = env->FindClass("android/graphics/Bitmap"); 314 if (fields.bitmapClazz == NULL) { 315 LOGE("Bitmap class is not found"); 316 return -1; 317 } 318 319 fields.bitmapConstructor = env->GetMethodID(fields.bitmapClazz, "<init>", "(IZ[B)V"); 320 if (fields.bitmapConstructor == NULL) { 321 LOGE("Bitmap constructor is not found"); 322 return -1; 323 } 324 325 return AndroidRuntime::registerNativeMethods 326 (env, kClassPathName, nativeMethods, NELEM(nativeMethods)); 327} 328