android_media_MediaMetadataRetriever.cpp revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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#ifdef LOG_TAG
19#undef LOG_TAG
20#define LOG_TAG "MediaMetadataRetriever"
21#endif
22
23#include <assert.h>
24#include <utils/Log.h>
25#include <utils/threads.h>
26#include <graphics/SkBitmap.h>
27#include <media/mediametadataretriever.h>
28#include "jni.h"
29#include "JNIHelp.h"
30#include "android_runtime/AndroidRuntime.h"
31
32using namespace android;
33
34static const char* const kClassPathName = "android/media/MediaMetadataRetriever";
35
36static void process_media_retriever_call(JNIEnv *env, status_t opStatus, const char* exception, const char *message)
37{
38    if (opStatus == (status_t) INVALID_OPERATION) {
39        jniThrowException(env, "java/lang/IllegalStateException", NULL);
40    } else if (opStatus != (status_t) OK) {
41        if (strlen(message) > 230) {
42            // If the message is too long, don't bother displaying the status code.
43            jniThrowException( env, exception, message);
44        } else {
45            char msg[256];
46            // Append the status code to the message.
47            sprintf(msg, "%s: status = 0x%X", message, opStatus);
48            jniThrowException( env, exception, msg);
49        }
50    }
51}
52
53static void android_media_MediaMetadataRetriever_setMode(JNIEnv *env, jobject thiz, jint mode)
54{
55    MediaMetadataRetriever::setMode(mode);
56}
57
58static void android_media_MediaMetadataRetriever_setDataSource(JNIEnv *env, jobject thiz, jstring path)
59{
60    if (!path) {
61        jniThrowException(env, "java/lang/IllegalArgumentException", "Null pointer");
62        return;
63    }
64
65    const char *pathStr = env->GetStringUTFChars(path, NULL);
66    if (!pathStr) {  // OutOfMemoryError exception already thrown
67        return;
68    }
69
70    // Don't let somebody trick us in to reading some random block of memory
71    if (strncmp("mem://", pathStr, 6) == 0) {
72        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid pathname");
73        return;
74    }
75
76    process_media_retriever_call(env, MediaMetadataRetriever::setDataSource(pathStr), "java/lang/RuntimeException", "setDataSource failed");
77    env->ReleaseStringUTFChars(path, pathStr);
78}
79
80static jobject android_media_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jobject thiz, jint keyCode)
81{
82    const char* value = MediaMetadataRetriever::extractMetadata(keyCode);
83    if (!value) {
84        LOGV("extractMetadata: Metadata is not found");
85        return NULL;
86    }
87    LOGV("extractMetadata: value (%s) for keyCode(%d)", value, keyCode);
88    return env->NewStringUTF(value);
89}
90
91static jobject android_media_MediaMetadataRetriever_captureFrame(JNIEnv *env, jobject thiz)
92{
93    // Call native MediaMetadataRetriever::captureFrame method
94    SkBitmap *bitmap = MediaMetadataRetriever::captureFrame();
95    if (!bitmap) {
96        return NULL;
97    }
98
99    // Create the bitmap by calling into Java!
100    jclass bitmapClazz = env->FindClass("android/graphics/Bitmap");
101    if (!bitmapClazz) {
102        LOGE("captureFrame: Bitmap class is not found");
103        return NULL;
104    }
105    jmethodID constructor = env->GetMethodID(bitmapClazz, "<init>", "(IZ[B)V");
106    if (!constructor) {
107        LOGE("captureFrame: Bitmap constructor is not found");
108        return NULL;
109    }
110    return env->NewObject(bitmapClazz, constructor, (int) bitmap, true, NULL);
111}
112
113static jbyteArray android_media_MediaMetadataRetriever_extractAlbumArt(JNIEnv *env, jobject thiz)
114{
115    MediaAlbumArt* mediaAlbumArt = MediaMetadataRetriever::extractAlbumArt();
116    if (!mediaAlbumArt) {
117        LOGE("extractAlbumArt: Call to extractAlbumArt failed.");
118        return NULL;
119    }
120
121    unsigned int len = mediaAlbumArt->getLength();
122    char* data = mediaAlbumArt->getData();
123    jbyteArray array = env->NewByteArray(len);
124    if (!array) {  // OutOfMemoryError exception has already been thrown.
125        LOGE("extractAlbumArt: OutOfMemoryError is thrown.");
126    } else {
127        jbyte* bytes = env->GetByteArrayElements(array, NULL);
128        memcpy(bytes, data, len);
129        env->ReleaseByteArrayElements(array, bytes, 0);
130    }
131    delete []data;
132    delete mediaAlbumArt;
133    return array;
134}
135
136static void android_media_MediaMetadataRetriever_release(JNIEnv *env, jobject thiz)
137{
138    MediaMetadataRetriever::release();
139}
140
141static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz)
142{
143    MediaMetadataRetriever::release();
144}
145
146static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
147{
148    MediaMetadataRetriever::create();
149}
150
151// JNI mapping between Java methods and native methods
152static JNINativeMethod nativeMethods[] = {
153        {"setMode",         "(I)V", (void *)android_media_MediaMetadataRetriever_setMode},
154        {"setDataSource",   "(Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSource},
155        {"captureFrame",    "()Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_captureFrame},
156        {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
157        {"extractAlbumArt", "()[B", (void *)android_media_MediaMetadataRetriever_extractAlbumArt},
158        {"release",         "()V", (void *)android_media_MediaMetadataRetriever_release},
159        {"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize},
160        {"native_setup",    "()V", (void *)android_media_MediaMetadataRetriever_native_setup},
161};
162
163// Register native mehtods with Android runtime environment
164int register_android_media_MediaMetadataRetriever(JNIEnv *env)
165{
166    jclass clazz = env->FindClass(kClassPathName);
167    if (clazz == NULL) {
168        LOGE("Can't find class: %s", kClassPathName);
169        return -1;
170    }
171
172    return AndroidRuntime::registerNativeMethods
173    (env, kClassPathName, nativeMethods, NELEM(nativeMethods));
174}
175