OSDN Git Service

am d1ba4c13: am ae3bd63b: am a1572ffc: Merge "Doc update: Update preprod (staging...
[android-x86/frameworks-base.git] / media / jni / android_media_MediaMetadataRetriever.cpp
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 <private/media/VideoFrame.h>
29
30 #include "jni.h"
31 #include "JNIHelp.h"
32 #include "android_runtime/AndroidRuntime.h"
33 #include "android_media_Utils.h"
34 #include "android_util_Binder.h"
35
36
37 using namespace android;
38
39 struct fields_t {
40     jfieldID context;
41     jclass bitmapClazz;  // Must be a global ref
42     jfieldID nativeBitmap;
43     jmethodID createBitmapMethod;
44     jmethodID createScaledBitmapMethod;
45     jclass configClazz;  // Must be a global ref
46     jmethodID createConfigMethod;
47 };
48
49 static fields_t fields;
50 static Mutex sLock;
51 static const char* const kClassPathName = "android/media/MediaMetadataRetriever";
52
53 static void process_media_retriever_call(JNIEnv *env, status_t opStatus, const char* exception, const char *message)
54 {
55     if (opStatus == (status_t) INVALID_OPERATION) {
56         jniThrowException(env, "java/lang/IllegalStateException", NULL);
57     } else if (opStatus != (status_t) OK) {
58         if (strlen(message) > 230) {
59             // If the message is too long, don't bother displaying the status code.
60             jniThrowException( env, exception, message);
61         } else {
62             char msg[256];
63             // Append the status code to the message.
64             sprintf(msg, "%s: status = 0x%X", message, opStatus);
65             jniThrowException( env, exception, msg);
66         }
67     }
68 }
69
70 static MediaMetadataRetriever* getRetriever(JNIEnv* env, jobject thiz)
71 {
72     // No lock is needed, since it is called internally by other methods that are protected
73     MediaMetadataRetriever* retriever = (MediaMetadataRetriever*) env->GetLongField(thiz, fields.context);
74     return retriever;
75 }
76
77 static void setRetriever(JNIEnv* env, jobject thiz, MediaMetadataRetriever* retriever)
78 {
79     // No lock is needed, since it is called internally by other methods that are protected
80     MediaMetadataRetriever *old = (MediaMetadataRetriever*) env->GetLongField(thiz, fields.context);
81     env->SetLongField(thiz, fields.context, (jlong) retriever);
82 }
83
84 static void
85 android_media_MediaMetadataRetriever_setDataSourceAndHeaders(
86         JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,
87         jobjectArray keys, jobjectArray values) {
88
89     ALOGV("setDataSource");
90     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
91     if (retriever == 0) {
92         jniThrowException(
93                 env,
94                 "java/lang/IllegalStateException", "No retriever available");
95
96         return;
97     }
98
99     if (!path) {
100         jniThrowException(
101                 env, "java/lang/IllegalArgumentException", "Null pointer");
102
103         return;
104     }
105
106     const char *tmp = env->GetStringUTFChars(path, NULL);
107     if (!tmp) {  // OutOfMemoryError exception already thrown
108         return;
109     }
110
111     String8 pathStr(tmp);
112     env->ReleaseStringUTFChars(path, tmp);
113     tmp = NULL;
114
115     // Don't let somebody trick us in to reading some random block of memory
116     if (strncmp("mem://", pathStr.string(), 6) == 0) {
117         jniThrowException(
118                 env, "java/lang/IllegalArgumentException", "Invalid pathname");
119         return;
120     }
121
122     // We build a similar KeyedVector out of it.
123     KeyedVector<String8, String8> headersVector;
124     if (!ConvertKeyValueArraysToKeyedVector(
125             env, keys, values, &headersVector)) {
126         return;
127     }
128
129     sp<IMediaHTTPService> httpService;
130     if (httpServiceBinderObj != NULL) {
131         sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
132         httpService = interface_cast<IMediaHTTPService>(binder);
133     }
134
135     process_media_retriever_call(
136             env,
137             retriever->setDataSource(
138                 httpService,
139                 pathStr.string(),
140                 headersVector.size() > 0 ? &headersVector : NULL),
141
142             "java/lang/RuntimeException",
143             "setDataSource failed");
144 }
145
146 static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
147 {
148     ALOGV("setDataSource");
149     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
150     if (retriever == 0) {
151         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
152         return;
153     }
154     if (!fileDescriptor) {
155         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
156         return;
157     }
158     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
159     if (offset < 0 || length < 0 || fd < 0) {
160         if (offset < 0) {
161             ALOGE("negative offset (%lld)", (long long)offset);
162         }
163         if (length < 0) {
164             ALOGE("negative length (%lld)", (long long)length);
165         }
166         if (fd < 0) {
167             ALOGE("invalid file descriptor");
168         }
169         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
170         return;
171     }
172     process_media_retriever_call(env, retriever->setDataSource(fd, offset, length), "java/lang/RuntimeException", "setDataSource failed");
173 }
174
175 template<typename T>
176 static void rotate0(T* dst, const T* src, size_t width, size_t height)
177 {
178     memcpy(dst, src, width * height * sizeof(T));
179 }
180
181 template<typename T>
182 static void rotate90(T* dst, const T* src, size_t width, size_t height)
183 {
184     for (size_t i = 0; i < height; ++i) {
185         for (size_t j = 0; j < width; ++j) {
186             dst[j * height + height - 1 - i] = src[i * width + j];
187         }
188     }
189 }
190
191 template<typename T>
192 static void rotate180(T* dst, const T* src, size_t width, size_t height)
193 {
194     for (size_t i = 0; i < height; ++i) {
195         for (size_t j = 0; j < width; ++j) {
196             dst[(height - 1 - i) * width + width - 1 - j] = src[i * width + j];
197         }
198     }
199 }
200
201 template<typename T>
202 static void rotate270(T* dst, const T* src, size_t width, size_t height)
203 {
204     for (size_t i = 0; i < height; ++i) {
205         for (size_t j = 0; j < width; ++j) {
206             dst[(width - 1 - j) * height + i] = src[i * width + j];
207         }
208     }
209 }
210
211 template<typename T>
212 static void rotate(T *dst, const T *src, size_t width, size_t height, int angle)
213 {
214     switch (angle) {
215         case 0:
216             rotate0(dst, src, width, height);
217             break;
218         case 90:
219             rotate90(dst, src, width, height);
220             break;
221         case 180:
222             rotate180(dst, src, width, height);
223             break;
224         case 270:
225             rotate270(dst, src, width, height);
226             break;
227     }
228 }
229
230 static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, jobject thiz, jlong timeUs, jint option)
231 {
232     ALOGV("getFrameAtTime: %lld us option: %d", timeUs, option);
233     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
234     if (retriever == 0) {
235         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
236         return NULL;
237     }
238
239     // Call native method to retrieve a video frame
240     VideoFrame *videoFrame = NULL;
241     sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
242     if (frameMemory != 0) {  // cast the shared structure to a VideoFrame object
243         videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
244     }
245     if (videoFrame == NULL) {
246         ALOGE("getFrameAtTime: videoFrame is a NULL pointer");
247         return NULL;
248     }
249
250     ALOGV("Dimension = %dx%d and bytes = %d",
251             videoFrame->mDisplayWidth,
252             videoFrame->mDisplayHeight,
253             videoFrame->mSize);
254
255     jobject config = env->CallStaticObjectMethod(
256                         fields.configClazz,
257                         fields.createConfigMethod,
258                         SkBitmap::kRGB_565_Config);
259
260     uint32_t width, height;
261     bool swapWidthAndHeight = false;
262     if (videoFrame->mRotationAngle == 90 || videoFrame->mRotationAngle == 270) {
263         width = videoFrame->mHeight;
264         height = videoFrame->mWidth;
265         swapWidthAndHeight = true;
266     } else {
267         width = videoFrame->mWidth;
268         height = videoFrame->mHeight;
269     }
270
271     jobject jBitmap = env->CallStaticObjectMethod(
272                             fields.bitmapClazz,
273                             fields.createBitmapMethod,
274                             width,
275                             height,
276                             config);
277     if (jBitmap == NULL) {
278         if (env->ExceptionCheck()) {
279             env->ExceptionClear();
280         }
281         ALOGE("getFrameAtTime: create Bitmap failed!");
282         return NULL;
283     }
284
285     SkBitmap *bitmap =
286             (SkBitmap *) env->GetLongField(jBitmap, fields.nativeBitmap);
287
288     bitmap->lockPixels();
289     rotate((uint16_t*)bitmap->getPixels(),
290            (uint16_t*)((char*)videoFrame + sizeof(VideoFrame)),
291            videoFrame->mWidth,
292            videoFrame->mHeight,
293            videoFrame->mRotationAngle);
294     bitmap->unlockPixels();
295
296     if (videoFrame->mDisplayWidth  != videoFrame->mWidth ||
297         videoFrame->mDisplayHeight != videoFrame->mHeight) {
298         uint32_t displayWidth = videoFrame->mDisplayWidth;
299         uint32_t displayHeight = videoFrame->mDisplayHeight;
300         if (swapWidthAndHeight) {
301             displayWidth = videoFrame->mDisplayHeight;
302             displayHeight = videoFrame->mDisplayWidth;
303         }
304         ALOGV("Bitmap dimension is scaled from %dx%d to %dx%d",
305                 width, height, displayWidth, displayHeight);
306         jobject scaledBitmap = env->CallStaticObjectMethod(fields.bitmapClazz,
307                                     fields.createScaledBitmapMethod,
308                                     jBitmap,
309                                     displayWidth,
310                                     displayHeight,
311                                     true);
312         return scaledBitmap;
313     }
314
315     return jBitmap;
316 }
317
318 static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
319         JNIEnv *env, jobject thiz, jint pictureType)
320 {
321     ALOGV("getEmbeddedPicture: %d", pictureType);
322     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
323     if (retriever == 0) {
324         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
325         return NULL;
326     }
327     MediaAlbumArt* mediaAlbumArt = NULL;
328
329     // FIXME:
330     // Use pictureType to retrieve the intended embedded picture and also change
331     // the method name to getEmbeddedPicture().
332     sp<IMemory> albumArtMemory = retriever->extractAlbumArt();
333     if (albumArtMemory != 0) {  // cast the shared structure to a MediaAlbumArt object
334         mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer());
335     }
336     if (mediaAlbumArt == NULL) {
337         ALOGE("getEmbeddedPicture: Call to getEmbeddedPicture failed.");
338         return NULL;
339     }
340
341     jbyteArray array = env->NewByteArray(mediaAlbumArt->size());
342     if (!array) {  // OutOfMemoryError exception has already been thrown.
343         ALOGE("getEmbeddedPicture: OutOfMemoryError is thrown.");
344     } else {
345         const jbyte* data =
346                 reinterpret_cast<const jbyte*>(mediaAlbumArt->data());
347         env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data);
348     }
349
350     // No need to delete mediaAlbumArt here
351     return array;
352 }
353
354 static jobject android_media_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jobject thiz, jint keyCode)
355 {
356     ALOGV("extractMetadata");
357     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
358     if (retriever == 0) {
359         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
360         return NULL;
361     }
362     const char* value = retriever->extractMetadata(keyCode);
363     if (!value) {
364         ALOGV("extractMetadata: Metadata is not found");
365         return NULL;
366     }
367     ALOGV("extractMetadata: value (%s) for keyCode(%d)", value, keyCode);
368     return env->NewStringUTF(value);
369 }
370
371 static void android_media_MediaMetadataRetriever_release(JNIEnv *env, jobject thiz)
372 {
373     ALOGV("release");
374     Mutex::Autolock lock(sLock);
375     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
376     delete retriever;
377     setRetriever(env, thiz, (MediaMetadataRetriever*) 0);
378 }
379
380 static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz)
381 {
382     ALOGV("native_finalize");
383     // No lock is needed, since android_media_MediaMetadataRetriever_release() is protected
384     android_media_MediaMetadataRetriever_release(env, thiz);
385 }
386
387 // This function gets a field ID, which in turn causes class initialization.
388 // It is called from a static block in MediaMetadataRetriever, which won't run until the
389 // first time an instance of this class is used.
390 static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env)
391 {
392     jclass clazz = env->FindClass(kClassPathName);
393     if (clazz == NULL) {
394         return;
395     }
396
397     fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
398     if (fields.context == NULL) {
399         return;
400     }
401
402     jclass bitmapClazz = env->FindClass("android/graphics/Bitmap");
403     if (bitmapClazz == NULL) {
404         return;
405     }
406     fields.bitmapClazz = (jclass) env->NewGlobalRef(bitmapClazz);
407     if (fields.bitmapClazz == NULL) {
408         return;
409     }
410     fields.createBitmapMethod =
411             env->GetStaticMethodID(fields.bitmapClazz, "createBitmap",
412                     "(IILandroid/graphics/Bitmap$Config;)"
413                     "Landroid/graphics/Bitmap;");
414     if (fields.createBitmapMethod == NULL) {
415         return;
416     }
417     fields.createScaledBitmapMethod =
418             env->GetStaticMethodID(fields.bitmapClazz, "createScaledBitmap",
419                     "(Landroid/graphics/Bitmap;IIZ)"
420                     "Landroid/graphics/Bitmap;");
421     if (fields.createScaledBitmapMethod == NULL) {
422         return;
423     }
424     fields.nativeBitmap = env->GetFieldID(fields.bitmapClazz, "mNativeBitmap", "J");
425     if (fields.nativeBitmap == NULL) {
426         return;
427     }
428
429     jclass configClazz = env->FindClass("android/graphics/Bitmap$Config");
430     if (configClazz == NULL) {
431         return;
432     }
433     fields.configClazz = (jclass) env->NewGlobalRef(configClazz);
434     if (fields.configClazz == NULL) {
435         return;
436     }
437     fields.createConfigMethod =
438             env->GetStaticMethodID(fields.configClazz, "nativeToConfig",
439                     "(I)Landroid/graphics/Bitmap$Config;");
440     if (fields.createConfigMethod == NULL) {
441         return;
442     }
443 }
444
445 static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
446 {
447     ALOGV("native_setup");
448     MediaMetadataRetriever* retriever = new MediaMetadataRetriever();
449     if (retriever == 0) {
450         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
451         return;
452     }
453     setRetriever(env, thiz, retriever);
454 }
455
456 // JNI mapping between Java methods and native methods
457 static JNINativeMethod nativeMethods[] = {
458         {
459             "_setDataSource",
460             "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
461             (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders
462         },
463
464         {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
465         {"_getFrameAtTime", "(JI)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
466         {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
467         {"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
468         {"release",         "()V", (void *)android_media_MediaMetadataRetriever_release},
469         {"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize},
470         {"native_setup",    "()V", (void *)android_media_MediaMetadataRetriever_native_setup},
471         {"native_init",     "()V", (void *)android_media_MediaMetadataRetriever_native_init},
472 };
473
474 // This function only registers the native methods, and is called from
475 // JNI_OnLoad in android_media_MediaPlayer.cpp
476 int register_android_media_MediaMetadataRetriever(JNIEnv *env)
477 {
478     return AndroidRuntime::registerNativeMethods
479         (env, kClassPathName, nativeMethods, NELEM(nativeMethods));
480 }