Home | History | Annotate | Download | only in jni
      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 <SkBitmap.h>
     25 #include <media/IMediaHTTPService.h>
     26 #include <media/mediametadataretriever.h>
     27 #include <media/mediascanner.h>
     28 #include <nativehelper/ScopedLocalRef.h>
     29 #include <private/media/VideoFrame.h>
     30 
     31 #include "jni.h"
     32 #include <nativehelper/JNIHelp.h>
     33 #include "android_runtime/AndroidRuntime.h"
     34 #include "android_media_MediaDataSource.h"
     35 #include "android_media_Utils.h"
     36 #include "android_util_Binder.h"
     37 
     38 #include "android/graphics/GraphicsJNI.h"
     39 
     40 using namespace android;
     41 
     42 struct fields_t {
     43     jfieldID context;
     44     jclass bitmapClazz;  // Must be a global ref
     45     jmethodID createBitmapMethod;
     46     jmethodID createScaledBitmapMethod;
     47     jclass configClazz;  // Must be a global ref
     48     jmethodID createConfigMethod;
     49     jclass bitmapParamsClazz; // Must be a global ref
     50     jfieldID inPreferredConfig;
     51     jfieldID outActualConfig;
     52     jclass arrayListClazz; // Must be a global ref
     53     jmethodID arrayListInit;
     54     jmethodID arrayListAdd;
     55 };
     56 
     57 static fields_t fields;
     58 static Mutex sLock;
     59 static const char* const kClassPathName = "android/media/MediaMetadataRetriever";
     60 
     61 static void process_media_retriever_call(JNIEnv *env, status_t opStatus, const char* exception, const char *message)
     62 {
     63     if (opStatus == (status_t) INVALID_OPERATION) {
     64         jniThrowException(env, "java/lang/IllegalStateException", NULL);
     65     } else if (opStatus != (status_t) OK) {
     66         if (strlen(message) > 230) {
     67             // If the message is too long, don't bother displaying the status code.
     68             jniThrowException( env, exception, message);
     69         } else {
     70             char msg[256];
     71             // Append the status code to the message.
     72             sprintf(msg, "%s: status = 0x%X", message, opStatus);
     73             jniThrowException( env, exception, msg);
     74         }
     75     }
     76 }
     77 
     78 static sp<MediaMetadataRetriever> getRetriever(JNIEnv* env, jobject thiz)
     79 {
     80     // No lock is needed, since it is called internally by other methods that are protected
     81     MediaMetadataRetriever* retriever = (MediaMetadataRetriever*) env->GetLongField(thiz, fields.context);
     82     return retriever;
     83 }
     84 
     85 static void setRetriever(JNIEnv* env, jobject thiz, const sp<MediaMetadataRetriever> &retriever)
     86 {
     87     // No lock is needed, since it is called internally by other methods that are protected
     88 
     89     if (retriever != NULL) {
     90         retriever->incStrong(thiz);
     91     }
     92     sp<MediaMetadataRetriever> old = getRetriever(env, thiz);
     93     if (old != NULL) {
     94         old->decStrong(thiz);
     95     }
     96 
     97     env->SetLongField(thiz, fields.context, (jlong) retriever.get());
     98 }
     99 
    100 static void
    101 android_media_MediaMetadataRetriever_setDataSourceAndHeaders(
    102         JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,
    103         jobjectArray keys, jobjectArray values) {
    104 
    105     ALOGV("setDataSource");
    106     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
    107     if (retriever == 0) {
    108         jniThrowException(
    109                 env,
    110                 "java/lang/IllegalStateException", "No retriever available");
    111 
    112         return;
    113     }
    114 
    115     if (!path) {
    116         jniThrowException(
    117                 env, "java/lang/IllegalArgumentException", "Null pointer");
    118 
    119         return;
    120     }
    121 
    122     const char *tmp = env->GetStringUTFChars(path, NULL);
    123     if (!tmp) {  // OutOfMemoryError exception already thrown
    124         return;
    125     }
    126 
    127     String8 pathStr(tmp);
    128     env->ReleaseStringUTFChars(path, tmp);
    129     tmp = NULL;
    130 
    131     // Don't let somebody trick us in to reading some random block of memory
    132     if (strncmp("mem://", pathStr.string(), 6) == 0) {
    133         jniThrowException(
    134                 env, "java/lang/IllegalArgumentException", "Invalid pathname");
    135         return;
    136     }
    137 
    138     // We build a similar KeyedVector out of it.
    139     KeyedVector<String8, String8> headersVector;
    140     if (!ConvertKeyValueArraysToKeyedVector(
    141             env, keys, values, &headersVector)) {
    142         return;
    143     }
    144 
    145     sp<IMediaHTTPService> httpService;
    146     if (httpServiceBinderObj != NULL) {
    147         sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
    148         httpService = interface_cast<IMediaHTTPService>(binder);
    149     }
    150 
    151     process_media_retriever_call(
    152             env,
    153             retriever->setDataSource(
    154                 httpService,
    155                 pathStr.string(),
    156                 headersVector.size() > 0 ? &headersVector : NULL),
    157 
    158             "java/lang/RuntimeException",
    159             "setDataSource failed");
    160 }
    161 
    162 static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
    163 {
    164     ALOGV("setDataSource");
    165     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
    166     if (retriever == 0) {
    167         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
    168         return;
    169     }
    170     if (!fileDescriptor) {
    171         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    172         return;
    173     }
    174     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    175     if (offset < 0 || length < 0 || fd < 0) {
    176         if (offset < 0) {
    177             ALOGE("negative offset (%lld)", (long long)offset);
    178         }
    179         if (length < 0) {
    180             ALOGE("negative length (%lld)", (long long)length);
    181         }
    182         if (fd < 0) {
    183             ALOGE("invalid file descriptor");
    184         }
    185         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    186         return;
    187     }
    188     process_media_retriever_call(env, retriever->setDataSource(fd, offset, length), "java/lang/RuntimeException", "setDataSource failed");
    189 }
    190 
    191 static void android_media_MediaMetadataRetriever_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource)
    192 {
    193     ALOGV("setDataSourceCallback");
    194     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
    195     if (retriever == 0) {
    196         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
    197         return;
    198     }
    199     if (dataSource == NULL) {
    200         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    201         return;
    202     }
    203 
    204     sp<IDataSource> callbackDataSource = new JMediaDataSource(env, dataSource);
    205     process_media_retriever_call(env, retriever->setDataSource(callbackDataSource), "java/lang/RuntimeException", "setDataSourceCallback failed");
    206 }
    207 
    208 template<typename T>
    209 static void rotate0(T* dst, const T* src, size_t width, size_t height)
    210 {
    211     memcpy(dst, src, width * height * sizeof(T));
    212 }
    213 
    214 template<typename T>
    215 static void rotate90(T* dst, const T* src, size_t width, size_t height)
    216 {
    217     for (size_t i = 0; i < height; ++i) {
    218         for (size_t j = 0; j < width; ++j) {
    219             dst[j * height + height - 1 - i] = src[i * width + j];
    220         }
    221     }
    222 }
    223 
    224 template<typename T>
    225 static void rotate180(T* dst, const T* src, size_t width, size_t height)
    226 {
    227     for (size_t i = 0; i < height; ++i) {
    228         for (size_t j = 0; j < width; ++j) {
    229             dst[(height - 1 - i) * width + width - 1 - j] = src[i * width + j];
    230         }
    231     }
    232 }
    233 
    234 template<typename T>
    235 static void rotate270(T* dst, const T* src, size_t width, size_t height)
    236 {
    237     for (size_t i = 0; i < height; ++i) {
    238         for (size_t j = 0; j < width; ++j) {
    239             dst[(width - 1 - j) * height + i] = src[i * width + j];
    240         }
    241     }
    242 }
    243 
    244 template<typename T>
    245 static void rotate(T *dst, const T *src, size_t width, size_t height, int angle)
    246 {
    247     switch (angle) {
    248         case 0:
    249             rotate0(dst, src, width, height);
    250             break;
    251         case 90:
    252             rotate90(dst, src, width, height);
    253             break;
    254         case 180:
    255             rotate180(dst, src, width, height);
    256             break;
    257         case 270:
    258             rotate270(dst, src, width, height);
    259             break;
    260     }
    261 }
    262 
    263 static jobject getBitmapFromVideoFrame(
    264         JNIEnv *env, VideoFrame *videoFrame, jint dst_width, jint dst_height,
    265         SkColorType outColorType) {
    266     ALOGV("getBitmapFromVideoFrame: dimension = %dx%d, displaySize = %dx%d, bytes = %d",
    267             videoFrame->mWidth,
    268             videoFrame->mHeight,
    269             videoFrame->mDisplayWidth,
    270             videoFrame->mDisplayHeight,
    271             videoFrame->mSize);
    272 
    273     ScopedLocalRef<jobject> config(env,
    274             env->CallStaticObjectMethod(
    275                     fields.configClazz,
    276                     fields.createConfigMethod,
    277                     GraphicsJNI::colorTypeToLegacyBitmapConfig(outColorType)));
    278 
    279     uint32_t width, height, displayWidth, displayHeight;
    280     bool swapWidthAndHeight = false;
    281     if (videoFrame->mRotationAngle == 90 || videoFrame->mRotationAngle == 270) {
    282         width = videoFrame->mHeight;
    283         height = videoFrame->mWidth;
    284         swapWidthAndHeight = true;
    285         displayWidth = videoFrame->mDisplayHeight;
    286         displayHeight = videoFrame->mDisplayWidth;
    287     } else {
    288         width = videoFrame->mWidth;
    289         height = videoFrame->mHeight;
    290         displayWidth = videoFrame->mDisplayWidth;
    291         displayHeight = videoFrame->mDisplayHeight;
    292     }
    293 
    294     jobject jBitmap = env->CallStaticObjectMethod(
    295                             fields.bitmapClazz,
    296                             fields.createBitmapMethod,
    297                             width,
    298                             height,
    299                             config.get());
    300     if (jBitmap == NULL) {
    301         if (env->ExceptionCheck()) {
    302             env->ExceptionClear();
    303         }
    304         ALOGE("getBitmapFromVideoFrame: create Bitmap failed!");
    305         return NULL;
    306     }
    307 
    308     SkBitmap bitmap;
    309     GraphicsJNI::getSkBitmap(env, jBitmap, &bitmap);
    310 
    311     if (outColorType == kRGB_565_SkColorType) {
    312         rotate((uint16_t*)bitmap.getPixels(),
    313                (uint16_t*)((char*)videoFrame + sizeof(VideoFrame)),
    314                videoFrame->mWidth,
    315                videoFrame->mHeight,
    316                videoFrame->mRotationAngle);
    317     } else {
    318         rotate((uint32_t*)bitmap.getPixels(),
    319                (uint32_t*)((char*)videoFrame + sizeof(VideoFrame)),
    320                videoFrame->mWidth,
    321                videoFrame->mHeight,
    322                videoFrame->mRotationAngle);
    323     }
    324 
    325     if (dst_width <= 0 || dst_height <= 0) {
    326         dst_width = displayWidth;
    327         dst_height = displayHeight;
    328     } else {
    329         float factor = std::min((float)dst_width / (float)displayWidth,
    330                 (float)dst_height / (float)displayHeight);
    331         dst_width = std::round(displayWidth * factor);
    332         dst_height = std::round(displayHeight * factor);
    333     }
    334 
    335     if ((uint32_t)dst_width != width || (uint32_t)dst_height != height) {
    336         ALOGV("Bitmap dimension is scaled from %dx%d to %dx%d",
    337                 width, height, dst_width, dst_height);
    338         jobject scaledBitmap = env->CallStaticObjectMethod(fields.bitmapClazz,
    339                                 fields.createScaledBitmapMethod,
    340                                 jBitmap,
    341                                 dst_width,
    342                                 dst_height,
    343                                 true);
    344 
    345         env->DeleteLocalRef(jBitmap);
    346         return scaledBitmap;
    347     }
    348 
    349     return jBitmap;
    350 }
    351 
    352 static int getColorFormat(JNIEnv *env, jobject options) {
    353     if (options == NULL) {
    354         return HAL_PIXEL_FORMAT_RGBA_8888;
    355     }
    356 
    357     ScopedLocalRef<jobject> inConfig(env, env->GetObjectField(options, fields.inPreferredConfig));
    358     SkColorType prefColorType = GraphicsJNI::getNativeBitmapColorType(env, inConfig.get());
    359 
    360     if (prefColorType == kRGB_565_SkColorType) {
    361         return HAL_PIXEL_FORMAT_RGB_565;
    362     }
    363     return HAL_PIXEL_FORMAT_RGBA_8888;
    364 }
    365 
    366 static SkColorType setOutColorType(JNIEnv *env, int colorFormat, jobject options) {
    367     SkColorType outColorType = kN32_SkColorType;
    368     if (colorFormat == HAL_PIXEL_FORMAT_RGB_565) {
    369         outColorType = kRGB_565_SkColorType;
    370     }
    371 
    372     if (options != NULL) {
    373         ScopedLocalRef<jobject> config(env,
    374                 env->CallStaticObjectMethod(
    375                         fields.configClazz,
    376                         fields.createConfigMethod,
    377                         GraphicsJNI::colorTypeToLegacyBitmapConfig(outColorType)));
    378 
    379         env->SetObjectField(options, fields.outActualConfig, config.get());
    380     }
    381     return outColorType;
    382 }
    383 
    384 static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
    385         JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
    386 {
    387     ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
    388             (long long)timeUs, option, dst_width, dst_height);
    389     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
    390     if (retriever == 0) {
    391         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
    392         return NULL;
    393     }
    394 
    395     // Call native method to retrieve a video frame
    396     VideoFrame *videoFrame = NULL;
    397     sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
    398     if (frameMemory != 0) {  // cast the shared structure to a VideoFrame object
    399         videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
    400     }
    401     if (videoFrame == NULL) {
    402         ALOGE("getFrameAtTime: videoFrame is a NULL pointer");
    403         return NULL;
    404     }
    405 
    406     return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, kRGB_565_SkColorType);
    407 }
    408 
    409 static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
    410         JNIEnv *env, jobject thiz, jint index, jobject params)
    411 {
    412     ALOGV("getImageAtIndex: index %d", index);
    413     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
    414     if (retriever == 0) {
    415         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
    416         return NULL;
    417     }
    418 
    419     int colorFormat = getColorFormat(env, params);
    420 
    421     // Call native method to retrieve an image
    422     VideoFrame *videoFrame = NULL;
    423     sp<IMemory> frameMemory = retriever->getImageAtIndex(index, colorFormat);
    424     if (frameMemory != 0) {  // cast the shared structure to a VideoFrame object
    425         videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
    426     }
    427     if (videoFrame == NULL) {
    428         ALOGE("getImageAtIndex: videoFrame is a NULL pointer");
    429         return NULL;
    430     }
    431 
    432     SkColorType outColorType = setOutColorType(env, colorFormat, params);
    433 
    434     return getBitmapFromVideoFrame(env, videoFrame, -1, -1, outColorType);
    435 }
    436 
    437 static jobject android_media_MediaMetadataRetriever_getThumbnailImageAtIndex(
    438         JNIEnv *env, jobject thiz, jint index, jobject params, jint targetSize, jint maxPixels)
    439 {
    440     ALOGV("getThumbnailImageAtIndex: index %d", index);
    441 
    442     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
    443     if (retriever == 0) {
    444         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
    445         return NULL;
    446     }
    447 
    448     int colorFormat = getColorFormat(env, params);
    449     jint dst_width = -1, dst_height = -1;
    450 
    451     // Call native method to retrieve an image
    452     VideoFrame *videoFrame = NULL;
    453     sp<IMemory> frameMemory = retriever->getImageAtIndex(
    454             index, colorFormat, true /*metaOnly*/, true /*thumbnail*/);
    455     if (frameMemory != 0) {
    456         videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
    457         int32_t thumbWidth = videoFrame->mWidth;
    458         int32_t thumbHeight = videoFrame->mHeight;
    459         videoFrame = NULL;
    460         int64_t thumbPixels = thumbWidth * thumbHeight;
    461 
    462         // Here we try to use the included thumbnail if it's not too shabby.
    463         // If this fails ThumbnailUtils would have to decode the full image and
    464         // downscale which could take long.
    465         if (thumbWidth >= targetSize || thumbHeight >= targetSize
    466                 || thumbPixels * 6 >= maxPixels) {
    467             frameMemory = retriever->getImageAtIndex(
    468                     index, colorFormat, false /*metaOnly*/, true /*thumbnail*/);
    469             videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
    470 
    471             if (thumbPixels > maxPixels) {
    472                 int downscale = ceil(sqrt(thumbPixels / (float)maxPixels));
    473                 dst_width = thumbWidth / downscale;
    474                 dst_height = thumbHeight /downscale;
    475             }
    476         }
    477     }
    478     if (videoFrame == NULL) {
    479         ALOGV("getThumbnailImageAtIndex: no suitable thumbnails available");
    480         return NULL;
    481     }
    482 
    483     // Ignore rotation for thumbnail extraction to be consistent with
    484     // thumbnails extracted by BitmapFactory APIs.
    485     videoFrame->mRotationAngle = 0;
    486 
    487     SkColorType outColorType = setOutColorType(env, colorFormat, params);
    488 
    489     return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, outColorType);
    490 }
    491 
    492 static jobject android_media_MediaMetadataRetriever_getFrameAtIndex(
    493         JNIEnv *env, jobject thiz, jint frameIndex, jint numFrames, jobject params)
    494 {
    495     ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d", frameIndex, numFrames);
    496     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
    497     if (retriever == 0) {
    498         jniThrowException(env,
    499                 "java/lang/IllegalStateException", "No retriever available");
    500         return NULL;
    501     }
    502 
    503     int colorFormat = getColorFormat(env, params);
    504 
    505     std::vector<sp<IMemory> > frames;
    506     status_t err = retriever->getFrameAtIndex(&frames, frameIndex, numFrames, colorFormat);
    507     if (err != OK || frames.size() == 0) {
    508         jniThrowException(env,
    509                 "java/lang/IllegalStateException", "No frames from retriever");
    510         return NULL;
    511     }
    512     jobject arrayList = env->NewObject(fields.arrayListClazz, fields.arrayListInit);
    513     if (arrayList == NULL) {
    514         jniThrowException(env,
    515                 "java/lang/IllegalStateException", "Can't create bitmap array");
    516         return NULL;
    517     }
    518 
    519     SkColorType outColorType = setOutColorType(env, colorFormat, params);
    520 
    521     for (size_t i = 0; i < frames.size(); i++) {
    522         if (frames[i] == NULL || frames[i]->pointer() == NULL) {
    523             ALOGE("video frame at index %zu is a NULL pointer", frameIndex + i);
    524             continue;
    525         }
    526         VideoFrame *videoFrame = static_cast<VideoFrame *>(frames[i]->pointer());
    527         jobject bitmapObj = getBitmapFromVideoFrame(env, videoFrame, -1, -1, outColorType);
    528         env->CallBooleanMethod(arrayList, fields.arrayListAdd, bitmapObj);
    529         env->DeleteLocalRef(bitmapObj);
    530     }
    531     return arrayList;
    532 }
    533 
    534 static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
    535         JNIEnv *env, jobject thiz, jint pictureType)
    536 {
    537     ALOGV("getEmbeddedPicture: %d", pictureType);
    538     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
    539     if (retriever == 0) {
    540         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
    541         return NULL;
    542     }
    543     MediaAlbumArt* mediaAlbumArt = NULL;
    544 
    545     // FIXME:
    546     // Use pictureType to retrieve the intended embedded picture and also change
    547     // the method name to getEmbeddedPicture().
    548     sp<IMemory> albumArtMemory = retriever->extractAlbumArt();
    549     if (albumArtMemory != 0) {  // cast the shared structure to a MediaAlbumArt object
    550         mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer());
    551     }
    552     if (mediaAlbumArt == NULL) {
    553         ALOGE("getEmbeddedPicture: Call to getEmbeddedPicture failed.");
    554         return NULL;
    555     }
    556 
    557     jbyteArray array = env->NewByteArray(mediaAlbumArt->size());
    558     if (!array) {  // OutOfMemoryError exception has already been thrown.
    559         ALOGE("getEmbeddedPicture: OutOfMemoryError is thrown.");
    560     } else {
    561         const jbyte* data =
    562                 reinterpret_cast<const jbyte*>(mediaAlbumArt->data());
    563         env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data);
    564     }
    565 
    566     // No need to delete mediaAlbumArt here
    567     return array;
    568 }
    569 
    570 static jobject android_media_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jobject thiz, jint keyCode)
    571 {
    572     ALOGV("extractMetadata");
    573     sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
    574     if (retriever == 0) {
    575         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
    576         return NULL;
    577     }
    578     const char* value = retriever->extractMetadata(keyCode);
    579     if (!value) {
    580         ALOGV("extractMetadata: Metadata is not found");
    581         return NULL;
    582     }
    583     ALOGV("extractMetadata: value (%s) for keyCode(%d)", value, keyCode);
    584     return env->NewStringUTF(value);
    585 }
    586 
    587 static void android_media_MediaMetadataRetriever_release(JNIEnv *env, jobject thiz)
    588 {
    589     ALOGV("release");
    590     Mutex::Autolock lock(sLock);
    591     setRetriever(env, thiz, NULL);
    592 }
    593 
    594 static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz)
    595 {
    596     ALOGV("native_finalize");
    597     // No lock is needed, since android_media_MediaMetadataRetriever_release() is protected
    598     android_media_MediaMetadataRetriever_release(env, thiz);
    599 }
    600 
    601 // This function gets a field ID, which in turn causes class initialization.
    602 // It is called from a static block in MediaMetadataRetriever, which won't run until the
    603 // first time an instance of this class is used.
    604 static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env)
    605 {
    606     ScopedLocalRef<jclass> clazz(env, env->FindClass(kClassPathName));
    607     if (clazz.get() == NULL) {
    608         return;
    609     }
    610 
    611     fields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
    612     if (fields.context == NULL) {
    613         return;
    614     }
    615 
    616     clazz.reset(env->FindClass("android/graphics/Bitmap"));
    617     if (clazz.get() == NULL) {
    618         return;
    619     }
    620     fields.bitmapClazz = (jclass) env->NewGlobalRef(clazz.get());
    621     if (fields.bitmapClazz == NULL) {
    622         return;
    623     }
    624     fields.createBitmapMethod =
    625             env->GetStaticMethodID(fields.bitmapClazz, "createBitmap",
    626                     "(IILandroid/graphics/Bitmap$Config;)"
    627                     "Landroid/graphics/Bitmap;");
    628     if (fields.createBitmapMethod == NULL) {
    629         return;
    630     }
    631     fields.createScaledBitmapMethod =
    632             env->GetStaticMethodID(fields.bitmapClazz, "createScaledBitmap",
    633                     "(Landroid/graphics/Bitmap;IIZ)"
    634                     "Landroid/graphics/Bitmap;");
    635     if (fields.createScaledBitmapMethod == NULL) {
    636         return;
    637     }
    638 
    639     clazz.reset(env->FindClass("android/graphics/Bitmap$Config"));
    640     if (clazz.get() == NULL) {
    641         return;
    642     }
    643     fields.configClazz = (jclass) env->NewGlobalRef(clazz.get());
    644     if (fields.configClazz == NULL) {
    645         return;
    646     }
    647     fields.createConfigMethod =
    648             env->GetStaticMethodID(fields.configClazz, "nativeToConfig",
    649                     "(I)Landroid/graphics/Bitmap$Config;");
    650     if (fields.createConfigMethod == NULL) {
    651         return;
    652     }
    653 
    654     clazz.reset(env->FindClass("android/media/MediaMetadataRetriever$BitmapParams"));
    655     if (clazz.get() == NULL) {
    656         return;
    657     }
    658     fields.bitmapParamsClazz = (jclass) env->NewGlobalRef(clazz.get());
    659     if (fields.bitmapParamsClazz == NULL) {
    660         return;
    661     }
    662     fields.inPreferredConfig = env->GetFieldID(fields.bitmapParamsClazz,
    663             "inPreferredConfig", "Landroid/graphics/Bitmap$Config;");
    664     if (fields.inPreferredConfig == NULL) {
    665         return;
    666     }
    667     fields.outActualConfig = env->GetFieldID(fields.bitmapParamsClazz,
    668             "outActualConfig", "Landroid/graphics/Bitmap$Config;");
    669     if (fields.outActualConfig == NULL) {
    670         return;
    671     }
    672 
    673     clazz.reset(env->FindClass("java/util/ArrayList"));
    674     if (clazz.get() == NULL) {
    675         return;
    676     }
    677     fields.arrayListClazz = (jclass) env->NewGlobalRef(clazz.get());
    678     if (fields.arrayListClazz == NULL) {
    679         return;
    680     }
    681     fields.arrayListInit = env->GetMethodID(clazz.get(), "<init>", "()V");
    682     if (fields.arrayListInit == NULL) {
    683         return;
    684     }
    685     fields.arrayListAdd = env->GetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z");
    686     if (fields.arrayListAdd == NULL) {
    687         return;
    688     }
    689 }
    690 
    691 static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
    692 {
    693     ALOGV("native_setup");
    694     sp<MediaMetadataRetriever> retriever = new MediaMetadataRetriever();
    695     if (retriever == 0) {
    696         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
    697         return;
    698     }
    699     setRetriever(env, thiz, retriever);
    700 }
    701 
    702 // JNI mapping between Java methods and native methods
    703 static const JNINativeMethod nativeMethods[] = {
    704         {
    705             "_setDataSource",
    706             "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
    707             (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders
    708         },
    709 
    710         {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V",
    711                 (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
    712         {"_setDataSource",   "(Landroid/media/MediaDataSource;)V",
    713                 (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
    714         {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;",
    715                 (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
    716         {
    717             "_getImageAtIndex",
    718             "(ILandroid/media/MediaMetadataRetriever$BitmapParams;)Landroid/graphics/Bitmap;",
    719             (void *)android_media_MediaMetadataRetriever_getImageAtIndex
    720         },
    721 
    722         {
    723             "getThumbnailImageAtIndex",
    724             "(ILandroid/media/MediaMetadataRetriever$BitmapParams;II)Landroid/graphics/Bitmap;",
    725             (void *)android_media_MediaMetadataRetriever_getThumbnailImageAtIndex
    726         },
    727 
    728         {
    729             "_getFrameAtIndex",
    730             "(IILandroid/media/MediaMetadataRetriever$BitmapParams;)Ljava/util/List;",
    731             (void *)android_media_MediaMetadataRetriever_getFrameAtIndex
    732         },
    733 
    734         {"extractMetadata", "(I)Ljava/lang/String;",
    735                 (void *)android_media_MediaMetadataRetriever_extractMetadata},
    736         {"getEmbeddedPicture", "(I)[B",
    737                 (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
    738         {"release",         "()V",
    739                 (void *)android_media_MediaMetadataRetriever_release},
    740         {"native_finalize", "()V",
    741                 (void *)android_media_MediaMetadataRetriever_native_finalize},
    742         {"native_setup",    "()V",
    743                 (void *)android_media_MediaMetadataRetriever_native_setup},
    744         {"native_init",     "()V",
    745                 (void *)android_media_MediaMetadataRetriever_native_init},
    746 };
    747 
    748 // This function only registers the native methods, and is called from
    749 // JNI_OnLoad in android_media_MediaPlayer.cpp
    750 int register_android_media_MediaMetadataRetriever(JNIEnv *env)
    751 {
    752     return AndroidRuntime::registerNativeMethods
    753         (env, kClassPathName, nativeMethods, NELEM(nativeMethods));
    754 }
    755