blob: 73aea2a64dd853cde3d0c3d157d764ac5ae2466f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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"
James Dong79f407c2011-05-05 12:50:04 -070031#include "android_media_Utils.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
33
34using namespace android;
35
36struct fields_t {
37 jfieldID context;
James Dongc371a022011-04-06 12:16:07 -070038 jclass bitmapClazz; // Must be a global ref
James Dong0e4b5352010-12-19 13:05:33 -080039 jfieldID nativeBitmap;
James Dong53ebc722010-11-08 16:04:27 -080040 jmethodID createBitmapMethod;
James Dong9f2cde32011-03-18 17:55:06 -070041 jmethodID createScaledBitmapMethod;
James Dongc371a022011-04-06 12:16:07 -070042 jclass configClazz; // Must be a global ref
James Dong0e4b5352010-12-19 13:05:33 -080043 jmethodID createConfigMethod;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044};
45
46static fields_t fields;
47static Mutex sLock;
Marco Nelissen4935d052009-08-03 11:12:58 -070048static const char* const kClassPathName = "android/media/MediaMetadataRetriever";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
50static void process_media_retriever_call(JNIEnv *env, status_t opStatus, const char* exception, const char *message)
51{
52 if (opStatus == (status_t) INVALID_OPERATION) {
53 jniThrowException(env, "java/lang/IllegalStateException", NULL);
54 } else if (opStatus != (status_t) OK) {
55 if (strlen(message) > 230) {
56 // If the message is too long, don't bother displaying the status code.
57 jniThrowException( env, exception, message);
58 } else {
59 char msg[256];
60 // Append the status code to the message.
61 sprintf(msg, "%s: status = 0x%X", message, opStatus);
62 jniThrowException( env, exception, msg);
63 }
64 }
65}
66
67static MediaMetadataRetriever* getRetriever(JNIEnv* env, jobject thiz)
68{
69 // No lock is needed, since it is called internally by other methods that are protected
70 MediaMetadataRetriever* retriever = (MediaMetadataRetriever*) env->GetIntField(thiz, fields.context);
71 return retriever;
72}
73
74static void setRetriever(JNIEnv* env, jobject thiz, int retriever)
75{
76 // No lock is needed, since it is called internally by other methods that are protected
77 MediaMetadataRetriever *old = (MediaMetadataRetriever*) env->GetIntField(thiz, fields.context);
78 env->SetIntField(thiz, fields.context, retriever);
79}
80
Andreas Huber5b7ced62011-03-21 10:25:44 -070081static void
82android_media_MediaMetadataRetriever_setDataSourceAndHeaders(
James Dong17524dc2011-05-04 13:41:58 -070083 JNIEnv *env, jobject thiz, jstring path,
84 jobjectArray keys, jobjectArray values) {
85
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 LOGV("setDataSource");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 MediaMetadataRetriever* retriever = getRetriever(env, thiz);
88 if (retriever == 0) {
Andreas Huber5b7ced62011-03-21 10:25:44 -070089 jniThrowException(
90 env,
91 "java/lang/IllegalStateException", "No retriever available");
92
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 return;
94 }
95
Andreas Huber5b7ced62011-03-21 10:25:44 -070096 if (!path) {
97 jniThrowException(
98 env, "java/lang/IllegalArgumentException", "Null pointer");
99
100 return;
101 }
102
103 const char *tmp = env->GetStringUTFChars(path, NULL);
Andreas Huber5bb357f2011-03-21 11:55:01 -0700104 if (!tmp) { // OutOfMemoryError exception already thrown
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 return;
106 }
107
Andreas Huber5bb357f2011-03-21 11:55:01 -0700108 String8 pathStr(tmp);
Andreas Huber5b7ced62011-03-21 10:25:44 -0700109 env->ReleaseStringUTFChars(path, tmp);
110 tmp = NULL;
111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 // Don't let somebody trick us in to reading some random block of memory
Andreas Huber5b7ced62011-03-21 10:25:44 -0700113 if (strncmp("mem://", pathStr.string(), 6) == 0) {
114 jniThrowException(
115 env, "java/lang/IllegalArgumentException", "Invalid pathname");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 return;
117 }
118
Andreas Huber5b7ced62011-03-21 10:25:44 -0700119 // We build a similar KeyedVector out of it.
120 KeyedVector<String8, String8> headersVector;
James Dong79f407c2011-05-05 12:50:04 -0700121 if (!ConvertKeyValueArraysToKeyedVector(
122 env, keys, values, &headersVector)) {
123 return;
Andreas Huber5b7ced62011-03-21 10:25:44 -0700124 }
Andreas Huber5b7ced62011-03-21 10:25:44 -0700125 process_media_retriever_call(
126 env,
127 retriever->setDataSource(
James Dong79f407c2011-05-05 12:50:04 -0700128 pathStr.string(), headersVector.size() > 0 ? &headersVector : NULL),
Andreas Huber5b7ced62011-03-21 10:25:44 -0700129
130 "java/lang/RuntimeException",
131 "setDataSource failed");
132}
133
James Dong79f407c2011-05-05 12:50:04 -0700134
Andreas Huber5b7ced62011-03-21 10:25:44 -0700135static void android_media_MediaMetadataRetriever_setDataSource(
136 JNIEnv *env, jobject thiz, jstring path) {
137 android_media_MediaMetadataRetriever_setDataSourceAndHeaders(
James Dong17524dc2011-05-04 13:41:58 -0700138 env, thiz, path, NULL, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139}
140
141static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
142{
143 LOGV("setDataSource");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 MediaMetadataRetriever* retriever = getRetriever(env, thiz);
145 if (retriever == 0) {
146 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
147 return;
148 }
149 if (!fileDescriptor) {
150 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
151 return;
152 }
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700153 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 if (offset < 0 || length < 0 || fd < 0) {
155 if (offset < 0) {
156 LOGE("negative offset (%lld)", offset);
157 }
158 if (length < 0) {
159 LOGE("negative length (%lld)", length);
160 }
161 if (fd < 0) {
162 LOGE("invalid file descriptor");
163 }
164 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
165 return;
166 }
167 process_media_retriever_call(env, retriever->setDataSource(fd, offset, length), "java/lang/RuntimeException", "setDataSource failed");
168}
169
Carl Shapiroae12a502011-01-20 23:13:09 -0800170template<typename T>
171static void rotate0(T* dst, const T* src, size_t width, size_t height)
172{
173 memcpy(dst, src, width * height * sizeof(T));
174}
175
176template<typename T>
177static void rotate90(T* dst, const T* src, size_t width, size_t height)
178{
179 for (size_t i = 0; i < height; ++i) {
180 for (size_t j = 0; j < width; ++j) {
181 dst[j * height + height - 1 - i] = src[i * width + j];
182 }
183 }
184}
185
186template<typename T>
187static void rotate180(T* dst, const T* src, size_t width, size_t height)
188{
189 for (size_t i = 0; i < height; ++i) {
190 for (size_t j = 0; j < width; ++j) {
191 dst[(height - 1 - i) * width + width - 1 - j] = src[i * width + j];
192 }
193 }
194}
195
196template<typename T>
197static void rotate270(T* dst, const T* src, size_t width, size_t height)
198{
199 for (size_t i = 0; i < height; ++i) {
200 for (size_t j = 0; j < width; ++j) {
201 dst[(width - 1 - j) * height + i] = src[i * width + j];
202 }
203 }
204}
205
206template<typename T>
207static void rotate(T *dst, const T *src, size_t width, size_t height, int angle)
208{
209 switch (angle) {
210 case 0:
211 rotate0(dst, src, width, height);
212 break;
213 case 90:
214 rotate90(dst, src, width, height);
215 break;
216 case 180:
217 rotate180(dst, src, width, height);
218 break;
219 case 270:
220 rotate270(dst, src, width, height);
221 break;
222 }
223}
224
James Dongfaf09ba2010-12-02 17:42:08 -0800225static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, jobject thiz, jlong timeUs, jint option)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226{
James Dongfaf09ba2010-12-02 17:42:08 -0800227 LOGV("getFrameAtTime: %lld us option: %d", timeUs, option);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 MediaMetadataRetriever* retriever = getRetriever(env, thiz);
229 if (retriever == 0) {
230 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
231 return NULL;
232 }
233
234 // Call native method to retrieve a video frame
235 VideoFrame *videoFrame = NULL;
James Dongfaf09ba2010-12-02 17:42:08 -0800236 sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 if (frameMemory != 0) { // cast the shared structure to a VideoFrame object
238 videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
239 }
240 if (videoFrame == NULL) {
James Dongfaf09ba2010-12-02 17:42:08 -0800241 LOGE("getFrameAtTime: videoFrame is a NULL pointer");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 return NULL;
243 }
244
James Dong0e4b5352010-12-19 13:05:33 -0800245 LOGV("Dimension = %dx%d and bytes = %d",
246 videoFrame->mDisplayWidth,
247 videoFrame->mDisplayHeight,
248 videoFrame->mSize);
249
James Dong0e4b5352010-12-19 13:05:33 -0800250 jobject config = env->CallStaticObjectMethod(
251 fields.configClazz,
252 fields.createConfigMethod,
253 SkBitmap::kRGB_565_Config);
254
Carl Shapiroae12a502011-01-20 23:13:09 -0800255 size_t width, height;
James Dong9f2cde32011-03-18 17:55:06 -0700256 bool swapWidthAndHeight = false;
Carl Shapiroae12a502011-01-20 23:13:09 -0800257 if (videoFrame->mRotationAngle == 90 || videoFrame->mRotationAngle == 270) {
James Dong9f2cde32011-03-18 17:55:06 -0700258 width = videoFrame->mHeight;
259 height = videoFrame->mWidth;
260 swapWidthAndHeight = true;
Carl Shapiroae12a502011-01-20 23:13:09 -0800261 } else {
James Dong9f2cde32011-03-18 17:55:06 -0700262 width = videoFrame->mWidth;
263 height = videoFrame->mHeight;
Carl Shapiroae12a502011-01-20 23:13:09 -0800264 }
265
James Dong0e4b5352010-12-19 13:05:33 -0800266 jobject jBitmap = env->CallStaticObjectMethod(
267 fields.bitmapClazz,
268 fields.createBitmapMethod,
Carl Shapiroae12a502011-01-20 23:13:09 -0800269 width,
270 height,
James Dong0e4b5352010-12-19 13:05:33 -0800271 config);
Carl Shapiroae12a502011-01-20 23:13:09 -0800272
James Dong0e4b5352010-12-19 13:05:33 -0800273 SkBitmap *bitmap =
274 (SkBitmap *) env->GetIntField(jBitmap, fields.nativeBitmap);
275
276 bitmap->lockPixels();
Carl Shapiroae12a502011-01-20 23:13:09 -0800277 rotate((uint16_t*)bitmap->getPixels(),
278 (uint16_t*)((char*)videoFrame + sizeof(VideoFrame)),
James Dong9f2cde32011-03-18 17:55:06 -0700279 videoFrame->mWidth,
280 videoFrame->mHeight,
Carl Shapiroae12a502011-01-20 23:13:09 -0800281 videoFrame->mRotationAngle);
James Dong0e4b5352010-12-19 13:05:33 -0800282 bitmap->unlockPixels();
283
James Dong9f2cde32011-03-18 17:55:06 -0700284 if (videoFrame->mDisplayWidth != videoFrame->mWidth ||
285 videoFrame->mDisplayHeight != videoFrame->mHeight) {
286 size_t displayWidth = videoFrame->mDisplayWidth;
287 size_t displayHeight = videoFrame->mDisplayHeight;
288 if (swapWidthAndHeight) {
289 displayWidth = videoFrame->mDisplayHeight;
290 displayHeight = videoFrame->mDisplayWidth;
291 }
292 LOGV("Bitmap dimension is scaled from %dx%d to %dx%d",
293 width, height, displayWidth, displayHeight);
294 jobject scaledBitmap = env->CallStaticObjectMethod(fields.bitmapClazz,
295 fields.createScaledBitmapMethod,
296 jBitmap,
297 displayWidth,
298 displayHeight,
299 true);
300 return scaledBitmap;
301 }
302
James Dong0e4b5352010-12-19 13:05:33 -0800303 return jBitmap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304}
305
James Dongdf9b3492011-01-04 15:03:48 -0800306static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
307 JNIEnv *env, jobject thiz, jint pictureType)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308{
James Dongdf9b3492011-01-04 15:03:48 -0800309 LOGV("getEmbeddedPicture: %d", pictureType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 MediaMetadataRetriever* retriever = getRetriever(env, thiz);
311 if (retriever == 0) {
312 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
313 return NULL;
314 }
315 MediaAlbumArt* mediaAlbumArt = NULL;
James Dongdf9b3492011-01-04 15:03:48 -0800316
317 // FIXME:
318 // Use pictureType to retrieve the intended embedded picture and also change
319 // the method name to getEmbeddedPicture().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 sp<IMemory> albumArtMemory = retriever->extractAlbumArt();
321 if (albumArtMemory != 0) { // cast the shared structure to a MediaAlbumArt object
322 mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer());
323 }
324 if (mediaAlbumArt == NULL) {
James Dongdf9b3492011-01-04 15:03:48 -0800325 LOGE("getEmbeddedPicture: Call to getEmbeddedPicture failed.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 return NULL;
327 }
328
329 unsigned int len = mediaAlbumArt->mSize;
330 char* data = (char*) mediaAlbumArt + sizeof(MediaAlbumArt);
331 jbyteArray array = env->NewByteArray(len);
332 if (!array) { // OutOfMemoryError exception has already been thrown.
James Dongdf9b3492011-01-04 15:03:48 -0800333 LOGE("getEmbeddedPicture: OutOfMemoryError is thrown.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 } else {
335 jbyte* bytes = env->GetByteArrayElements(array, NULL);
336 if (bytes != NULL) {
337 memcpy(bytes, data, len);
338 env->ReleaseByteArrayElements(array, bytes, 0);
339 }
340 }
341
342 // No need to delete mediaAlbumArt here
343 return array;
344}
345
346static jobject android_media_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jobject thiz, jint keyCode)
347{
348 LOGV("extractMetadata");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 MediaMetadataRetriever* retriever = getRetriever(env, thiz);
350 if (retriever == 0) {
351 jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
352 return NULL;
353 }
354 const char* value = retriever->extractMetadata(keyCode);
355 if (!value) {
356 LOGV("extractMetadata: Metadata is not found");
357 return NULL;
358 }
359 LOGV("extractMetadata: value (%s) for keyCode(%d)", value, keyCode);
360 return env->NewStringUTF(value);
361}
362
363static void android_media_MediaMetadataRetriever_release(JNIEnv *env, jobject thiz)
364{
365 LOGV("release");
366 Mutex::Autolock lock(sLock);
367 MediaMetadataRetriever* retriever = getRetriever(env, thiz);
368 delete retriever;
369 setRetriever(env, thiz, 0);
370}
371
372static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz)
373{
374 LOGV("native_finalize");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 // No lock is needed, since android_media_MediaMetadataRetriever_release() is protected
376 android_media_MediaMetadataRetriever_release(env, thiz);
377}
378
Marco Nelissen4935d052009-08-03 11:12:58 -0700379// This function gets a field ID, which in turn causes class initialization.
380// It is called from a static block in MediaMetadataRetriever, which won't run until the
381// first time an instance of this class is used.
382static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env)
383{
384 jclass clazz = env->FindClass(kClassPathName);
385 if (clazz == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700386 return;
387 }
388
389 fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
390 if (fields.context == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700391 return;
392 }
393
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700394 jclass bitmapClazz = env->FindClass("android/graphics/Bitmap");
395 if (bitmapClazz == NULL) {
396 return;
397 }
398 fields.bitmapClazz = (jclass) env->NewGlobalRef(bitmapClazz);
Marco Nelissen4935d052009-08-03 11:12:58 -0700399 if (fields.bitmapClazz == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700400 return;
401 }
James Dong0e4b5352010-12-19 13:05:33 -0800402 fields.createBitmapMethod =
403 env->GetStaticMethodID(fields.bitmapClazz, "createBitmap",
404 "(IILandroid/graphics/Bitmap$Config;)"
405 "Landroid/graphics/Bitmap;");
406 if (fields.createBitmapMethod == NULL) {
James Dong0e4b5352010-12-19 13:05:33 -0800407 return;
408 }
James Dong9f2cde32011-03-18 17:55:06 -0700409 fields.createScaledBitmapMethod =
410 env->GetStaticMethodID(fields.bitmapClazz, "createScaledBitmap",
411 "(Landroid/graphics/Bitmap;IIZ)"
412 "Landroid/graphics/Bitmap;");
413 if (fields.createScaledBitmapMethod == NULL) {
James Dong9f2cde32011-03-18 17:55:06 -0700414 return;
415 }
James Dong0e4b5352010-12-19 13:05:33 -0800416 fields.nativeBitmap = env->GetFieldID(fields.bitmapClazz, "mNativeBitmap", "I");
417 if (fields.nativeBitmap == NULL) {
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700418 return;
James Dong0e4b5352010-12-19 13:05:33 -0800419 }
420
Brian Carlstrom46e18c112011-04-05 22:44:45 -0700421 jclass configClazz = env->FindClass("android/graphics/Bitmap$Config");
422 if (configClazz == NULL) {
423 return;
424 }
425 fields.configClazz = (jclass) env->NewGlobalRef(configClazz);
James Dong0e4b5352010-12-19 13:05:33 -0800426 if (fields.configClazz == NULL) {
James Dong0e4b5352010-12-19 13:05:33 -0800427 return;
428 }
429 fields.createConfigMethod =
430 env->GetStaticMethodID(fields.configClazz, "nativeToConfig",
431 "(I)Landroid/graphics/Bitmap$Config;");
432 if (fields.createConfigMethod == NULL) {
James Dong0e4b5352010-12-19 13:05:33 -0800433 return;
434 }
Marco Nelissen4935d052009-08-03 11:12:58 -0700435}
436
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
438{
439 LOGV("native_setup");
440 MediaMetadataRetriever* retriever = new MediaMetadataRetriever();
441 if (retriever == 0) {
442 jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
443 return;
444 }
445 setRetriever(env, thiz, (int)retriever);
446}
447
448// JNI mapping between Java methods and native methods
449static JNINativeMethod nativeMethods[] = {
450 {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSource},
James Dong17524dc2011-05-04 13:41:58 -0700451
452 {
453 "_setDataSource",
454 "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
455 (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders
456 },
457
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
James Dongfaf09ba2010-12-02 17:42:08 -0800459 {"_getFrameAtTime", "(JI)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
James Dongdf9b3492011-01-04 15:03:48 -0800461 {"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 {"release", "()V", (void *)android_media_MediaMetadataRetriever_release},
463 {"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize},
464 {"native_setup", "()V", (void *)android_media_MediaMetadataRetriever_native_setup},
Marco Nelissen4935d052009-08-03 11:12:58 -0700465 {"native_init", "()V", (void *)android_media_MediaMetadataRetriever_native_init},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466};
467
Marco Nelissen4935d052009-08-03 11:12:58 -0700468// This function only registers the native methods, and is called from
469// JNI_OnLoad in android_media_MediaPlayer.cpp
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470int register_android_media_MediaMetadataRetriever(JNIEnv *env)
471{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 return AndroidRuntime::registerNativeMethods
Marco Nelissen4935d052009-08-03 11:12:58 -0700473 (env, kClassPathName, nativeMethods, NELEM(nativeMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474}