blob: ecbd288db3cf6b7ce3421120a45b27ebc8fe9cdf [file] [log] [blame]
James Dongc371a022011-04-06 12:16:07 -07001/*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002**
3** Copyright 2007, 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 "MediaPlayer-JNI"
20#include "utils/Log.h"
21
22#include <media/mediaplayer.h>
Nicolas Catania20cb94e2009-05-12 23:25:55 -070023#include <media/MediaPlayerInterface.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024#include <stdio.h>
25#include <assert.h>
26#include <limits.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include <utils/threads.h>
30#include "jni.h"
31#include "JNIHelp.h"
32#include "android_runtime/AndroidRuntime.h"
The Android Open Source Project4df24232009-03-05 14:34:35 -080033#include "utils/Errors.h" // for status_t
Andreas Huber25643002010-01-28 11:19:57 -080034#include "utils/KeyedVector.h"
35#include "utils/String8.h"
Nicolas Catania20cb94e2009-05-12 23:25:55 -070036#include "android_util_Binder.h"
37#include <binder/Parcel.h>
Glenn Kastencc562a32011-02-08 17:26:17 -080038#include <gui/SurfaceTexture.h>
39#include <gui/ISurfaceTexture.h>
Mathias Agopian000479f2010-02-09 17:46:37 -080040#include <surfaceflinger/Surface.h>
Gloria Wangd211f412011-02-19 18:37:57 -080041#include <binder/IPCThreadState.h>
42#include <binder/IServiceManager.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
44// ----------------------------------------------------------------------------
45
46using namespace android;
47
48// ----------------------------------------------------------------------------
49
50struct fields_t {
51 jfieldID context;
52 jfieldID surface;
Glenn Kastencc562a32011-02-08 17:26:17 -080053 jfieldID surfaceTexture;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 /* actually in android.view.Surface XXX */
55 jfieldID surface_native;
Glenn Kastencc562a32011-02-08 17:26:17 -080056 // actually in android.graphics.SurfaceTexture
57 jfieldID surfaceTexture_native;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
59 jmethodID post_event;
60};
61static fields_t fields;
62
63static Mutex sLock;
64
65// ----------------------------------------------------------------------------
66// ref-counted object for callbacks
67class JNIMediaPlayerListener: public MediaPlayerListener
68{
69public:
70 JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
71 ~JNIMediaPlayerListener();
72 void notify(int msg, int ext1, int ext2);
73private:
74 JNIMediaPlayerListener();
75 jclass mClass; // Reference to MediaPlayer class
76 jobject mObject; // Weak ref to MediaPlayer Java object to call on
77};
78
79JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
80{
81
82 // Hold onto the MediaPlayer class for use in calling the static method
83 // that posts events to the application thread.
84 jclass clazz = env->GetObjectClass(thiz);
85 if (clazz == NULL) {
86 LOGE("Can't find android/media/MediaPlayer");
87 jniThrowException(env, "java/lang/Exception", NULL);
88 return;
89 }
90 mClass = (jclass)env->NewGlobalRef(clazz);
91
92 // We use a weak reference so the MediaPlayer object can be garbage collected.
93 // The reference is only used as a proxy for callbacks.
94 mObject = env->NewGlobalRef(weak_thiz);
95}
96
97JNIMediaPlayerListener::~JNIMediaPlayerListener()
98{
99 // remove global references
100 JNIEnv *env = AndroidRuntime::getJNIEnv();
101 env->DeleteGlobalRef(mObject);
102 env->DeleteGlobalRef(mClass);
103}
104
105void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2)
106{
107 JNIEnv *env = AndroidRuntime::getJNIEnv();
108 env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, 0);
109}
110
111// ----------------------------------------------------------------------------
112
Marco Nelissen0fc736f322009-07-10 09:34:59 -0700113static Surface* get_surface(JNIEnv* env, jobject clazz)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114{
Marco Nelissen0fc736f322009-07-10 09:34:59 -0700115 return (Surface*)env->GetIntField(clazz, fields.surface_native);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116}
117
Glenn Kastencc562a32011-02-08 17:26:17 -0800118sp<ISurfaceTexture> getSurfaceTexture(JNIEnv* env, jobject clazz)
119{
120 sp<ISurfaceTexture> surfaceTexture(
121 (ISurfaceTexture*)env->GetIntField(clazz, fields.surfaceTexture_native));
122 return surfaceTexture;
123}
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
126{
127 Mutex::Autolock l(sLock);
128 MediaPlayer* const p = (MediaPlayer*)env->GetIntField(thiz, fields.context);
129 return sp<MediaPlayer>(p);
130}
131
132static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
133{
134 Mutex::Autolock l(sLock);
135 sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
136 if (player.get()) {
137 player->incStrong(thiz);
138 }
139 if (old != 0) {
140 old->decStrong(thiz);
141 }
142 env->SetIntField(thiz, fields.context, (int)player.get());
143 return old;
144}
145
Nicolas Catania32f82772009-06-11 16:33:49 -0700146// If exception is NULL and opStatus is not OK, this method sends an error
147// event to the client application; otherwise, if exception is not NULL and
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148// opStatus is not OK, this method throws the given exception to the client
149// application.
150static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
151{
152 if (exception == NULL) { // Don't throw exception. Instead, send an event.
153 if (opStatus != (status_t) OK) {
154 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
155 if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
156 }
157 } else { // Throw exception!
158 if ( opStatus == (status_t) INVALID_OPERATION ) {
159 jniThrowException(env, "java/lang/IllegalStateException", NULL);
160 } else if ( opStatus != (status_t) OK ) {
161 if (strlen(message) > 230) {
162 // if the message is too long, don't bother displaying the status code
163 jniThrowException( env, exception, message);
164 } else {
165 char msg[256];
166 // append the status code to the message
167 sprintf(msg, "%s: status=0x%X", message, opStatus);
168 jniThrowException( env, exception, msg);
169 }
170 }
171 }
172}
173
174static void
Andreas Huber25643002010-01-28 11:19:57 -0800175android_media_MediaPlayer_setDataSourceAndHeaders(
176 JNIEnv *env, jobject thiz, jstring path, jobject headers) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
178 if (mp == NULL ) {
179 jniThrowException(env, "java/lang/IllegalStateException", NULL);
180 return;
181 }
182
183 if (path == NULL) {
184 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
185 return;
186 }
187
James Dongc371a022011-04-06 12:16:07 -0700188 const char *tmp = env->GetStringUTFChars(path, NULL);
189 if (tmp == NULL) { // Out of memory
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 return;
191 }
Andreas Huber25643002010-01-28 11:19:57 -0800192
James Dongc371a022011-04-06 12:16:07 -0700193 String8 pathStr(tmp);
194 env->ReleaseStringUTFChars(path, tmp);
195 tmp = NULL;
196
Andreas Huber25643002010-01-28 11:19:57 -0800197 // headers is a Map<String, String>.
198 // We build a similar KeyedVector out of it.
199 KeyedVector<String8, String8> headersVector;
200 if (headers) {
201 // Get the Map's entry Set.
202 jclass mapClass = env->FindClass("java/util/Map");
James Dongc371a022011-04-06 12:16:07 -0700203 if (mapClass == NULL) {
204 return;
205 }
Andreas Huber25643002010-01-28 11:19:57 -0800206
207 jmethodID entrySet =
208 env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
James Dongc371a022011-04-06 12:16:07 -0700209 if (entrySet == NULL) {
210 return;
211 }
Andreas Huber25643002010-01-28 11:19:57 -0800212
213 jobject set = env->CallObjectMethod(headers, entrySet);
James Dongc371a022011-04-06 12:16:07 -0700214 if (set == NULL) {
215 return;
216 }
217
Andreas Huber25643002010-01-28 11:19:57 -0800218 // Obtain an iterator over the Set
219 jclass setClass = env->FindClass("java/util/Set");
James Dongc371a022011-04-06 12:16:07 -0700220 if (setClass == NULL) {
221 return;
222 }
Andreas Huber25643002010-01-28 11:19:57 -0800223
224 jmethodID iterator =
225 env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
James Dongc371a022011-04-06 12:16:07 -0700226 if (iterator == NULL) {
227 return;
228 }
Andreas Huber25643002010-01-28 11:19:57 -0800229
230 jobject iter = env->CallObjectMethod(set, iterator);
James Dongc371a022011-04-06 12:16:07 -0700231 if (iter == NULL) {
232 return;
233 }
234
Andreas Huber25643002010-01-28 11:19:57 -0800235 // Get the Iterator method IDs
236 jclass iteratorClass = env->FindClass("java/util/Iterator");
James Dongc371a022011-04-06 12:16:07 -0700237 if (iteratorClass == NULL) {
238 return;
239 }
240
Andreas Huber25643002010-01-28 11:19:57 -0800241 jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
James Dongc371a022011-04-06 12:16:07 -0700242 if (hasNext == NULL) {
243 return;
244 }
Andreas Huber25643002010-01-28 11:19:57 -0800245
246 jmethodID next =
247 env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
James Dongc371a022011-04-06 12:16:07 -0700248 if (next == NULL) {
249 return;
250 }
Andreas Huber25643002010-01-28 11:19:57 -0800251
252 // Get the Entry class method IDs
253 jclass entryClass = env->FindClass("java/util/Map$Entry");
James Dongc371a022011-04-06 12:16:07 -0700254 if (entryClass == NULL) {
255 return;
256 }
Andreas Huber25643002010-01-28 11:19:57 -0800257
258 jmethodID getKey =
259 env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
James Dongc371a022011-04-06 12:16:07 -0700260 if (getKey == NULL) {
261 return;
262 }
Andreas Huber25643002010-01-28 11:19:57 -0800263
264 jmethodID getValue =
265 env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
James Dongc371a022011-04-06 12:16:07 -0700266 if (getValue == NULL) {
267 return;
268 }
Andreas Huber25643002010-01-28 11:19:57 -0800269
270 // Iterate over the entry Set
271 while (env->CallBooleanMethod(iter, hasNext)) {
272 jobject entry = env->CallObjectMethod(iter, next);
273 jstring key = (jstring) env->CallObjectMethod(entry, getKey);
274 jstring value = (jstring) env->CallObjectMethod(entry, getValue);
275
276 const char* keyStr = env->GetStringUTFChars(key, NULL);
277 if (!keyStr) { // Out of memory
Andreas Huber25643002010-01-28 11:19:57 -0800278 return;
279 }
280
281 const char* valueStr = env->GetStringUTFChars(value, NULL);
282 if (!valueStr) { // Out of memory
James Dongc371a022011-04-06 12:16:07 -0700283 env->ReleaseStringUTFChars(key, keyStr);
Andreas Huber25643002010-01-28 11:19:57 -0800284 return;
285 }
286
287 headersVector.add(String8(keyStr), String8(valueStr));
288
289 env->DeleteLocalRef(entry);
290 env->ReleaseStringUTFChars(key, keyStr);
291 env->DeleteLocalRef(key);
292 env->ReleaseStringUTFChars(value, valueStr);
293 env->DeleteLocalRef(value);
James Dongc371a022011-04-06 12:16:07 -0700294 }
Andreas Huber25643002010-01-28 11:19:57 -0800295
Andreas Huber25643002010-01-28 11:19:57 -0800296 }
297
The Android Open Source Project4df24232009-03-05 14:34:35 -0800298 LOGV("setDataSource: path %s", pathStr);
Andreas Huber25643002010-01-28 11:19:57 -0800299 status_t opStatus =
300 mp->setDataSource(
James Dongc371a022011-04-06 12:16:07 -0700301 pathStr,
Andreas Huber25643002010-01-28 11:19:57 -0800302 headers ? &headersVector : NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303
Andreas Huber25643002010-01-28 11:19:57 -0800304 process_media_player_call(
305 env, thiz, opStatus, "java/io/IOException",
306 "setDataSource failed." );
307}
308
309static void
310android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path)
311{
312 android_media_MediaPlayer_setDataSourceAndHeaders(env, thiz, path, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313}
314
315static void
316android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
317{
318 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
319 if (mp == NULL ) {
320 jniThrowException(env, "java/lang/IllegalStateException", NULL);
321 return;
322 }
323
324 if (fileDescriptor == NULL) {
325 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
326 return;
327 }
328 int fd = getParcelFileDescriptorFD(env, fileDescriptor);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800329 LOGV("setDataSourceFD: fd %d", fd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
331}
332
Glenn Kastencc562a32011-02-08 17:26:17 -0800333static void setVideoSurfaceOrSurfaceTexture(
334 const sp<MediaPlayer>& mp, JNIEnv *env, jobject thiz, const char *prefix)
Dave Sparks8b0b1742009-05-29 09:01:20 -0700335{
Glenn Kastencc562a32011-02-08 17:26:17 -0800336 // The Java MediaPlayer class makes sure that at most one of mSurface and
337 // mSurfaceTexture is non-null. But just in case, we give priority to
338 // mSurface over mSurfaceTexture.
Dave Sparks8b0b1742009-05-29 09:01:20 -0700339 jobject surface = env->GetObjectField(thiz, fields.surface);
340 if (surface != NULL) {
Glenn Kastencc562a32011-02-08 17:26:17 -0800341 sp<Surface> native_surface(get_surface(env, surface));
342 LOGV("%s: surface=%p (id=%d)", prefix,
Eric Laurent619346f2010-06-21 09:27:30 -0700343 native_surface.get(), native_surface->getIdentity());
Dave Sparks8b0b1742009-05-29 09:01:20 -0700344 mp->setVideoSurface(native_surface);
Glenn Kastencc562a32011-02-08 17:26:17 -0800345 } else {
346 jobject surfaceTexture = env->GetObjectField(thiz, fields.surfaceTexture);
347 if (surfaceTexture != NULL) {
348 sp<ISurfaceTexture> native_surfaceTexture(
349 getSurfaceTexture(env, surfaceTexture));
James Donga93f84e2011-04-01 13:29:27 -0700350 LOGV("%s: texture=%p", prefix, native_surfaceTexture.get());
Glenn Kastencc562a32011-02-08 17:26:17 -0800351 mp->setVideoSurfaceTexture(native_surfaceTexture);
352 }
Dave Sparks8b0b1742009-05-29 09:01:20 -0700353 }
354}
355
356static void
Glenn Kastencc562a32011-02-08 17:26:17 -0800357android_media_MediaPlayer_setVideoSurfaceOrSurfaceTexture(JNIEnv *env, jobject thiz)
Dave Sparks8b0b1742009-05-29 09:01:20 -0700358{
359 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
360 if (mp == NULL ) {
361 jniThrowException(env, "java/lang/IllegalStateException", NULL);
362 return;
363 }
Glenn Kastencc562a32011-02-08 17:26:17 -0800364 setVideoSurfaceOrSurfaceTexture(mp, env, thiz,
365 "_setVideoSurfaceOrSurfaceTexture");
Dave Sparks8b0b1742009-05-29 09:01:20 -0700366}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367
368static void
369android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
370{
371 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
372 if (mp == NULL ) {
373 jniThrowException(env, "java/lang/IllegalStateException", NULL);
374 return;
375 }
Glenn Kastencc562a32011-02-08 17:26:17 -0800376 setVideoSurfaceOrSurfaceTexture(mp, env, thiz, "prepare");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
378}
379
380static void
381android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
382{
383 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
384 if (mp == NULL ) {
385 jniThrowException(env, "java/lang/IllegalStateException", NULL);
386 return;
387 }
Glenn Kastencc562a32011-02-08 17:26:17 -0800388 setVideoSurfaceOrSurfaceTexture(mp, env, thiz, "prepareAsync");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
390}
391
392static void
393android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
394{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800395 LOGV("start");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
397 if (mp == NULL ) {
398 jniThrowException(env, "java/lang/IllegalStateException", NULL);
399 return;
400 }
401 process_media_player_call( env, thiz, mp->start(), NULL, NULL );
402}
403
404static void
405android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz)
406{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800407 LOGV("stop");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
409 if (mp == NULL ) {
410 jniThrowException(env, "java/lang/IllegalStateException", NULL);
411 return;
412 }
Nicolas Catania32f82772009-06-11 16:33:49 -0700413 process_media_player_call( env, thiz, mp->stop(), NULL, NULL );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414}
415
416static void
417android_media_MediaPlayer_pause(JNIEnv *env, jobject thiz)
418{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800419 LOGV("pause");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
421 if (mp == NULL ) {
422 jniThrowException(env, "java/lang/IllegalStateException", NULL);
423 return;
424 }
425 process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
426}
427
428static jboolean
429android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz)
430{
431 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
432 if (mp == NULL ) {
433 jniThrowException(env, "java/lang/IllegalStateException", NULL);
434 return false;
435 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800436 const jboolean is_playing = mp->isPlaying();
437
438 LOGV("isPlaying: %d", is_playing);
439 return is_playing;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440}
441
442static void
443android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec)
444{
445 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
446 if (mp == NULL ) {
447 jniThrowException(env, "java/lang/IllegalStateException", NULL);
448 return;
449 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800450 LOGV("seekTo: %d(msec)", msec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 process_media_player_call( env, thiz, mp->seekTo(msec), NULL, NULL );
452}
453
454static int
455android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz)
456{
457 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
458 if (mp == NULL ) {
459 jniThrowException(env, "java/lang/IllegalStateException", NULL);
460 return 0;
461 }
462 int w;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800463 if (0 != mp->getVideoWidth(&w)) {
464 LOGE("getVideoWidth failed");
465 w = 0;
466 }
467 LOGV("getVideoWidth: %d", w);
468 return w;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469}
470
471static int
472android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz)
473{
474 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
475 if (mp == NULL ) {
476 jniThrowException(env, "java/lang/IllegalStateException", NULL);
477 return 0;
478 }
479 int h;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800480 if (0 != mp->getVideoHeight(&h)) {
481 LOGE("getVideoHeight failed");
482 h = 0;
483 }
484 LOGV("getVideoHeight: %d", h);
485 return h;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486}
487
488
489static int
490android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz)
491{
492 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
493 if (mp == NULL ) {
494 jniThrowException(env, "java/lang/IllegalStateException", NULL);
495 return 0;
496 }
497 int msec;
498 process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL );
The Android Open Source Project4df24232009-03-05 14:34:35 -0800499 LOGV("getCurrentPosition: %d (msec)", msec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 return msec;
501}
502
503static int
504android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz)
505{
506 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
507 if (mp == NULL ) {
508 jniThrowException(env, "java/lang/IllegalStateException", NULL);
509 return 0;
510 }
511 int msec;
512 process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL );
The Android Open Source Project4df24232009-03-05 14:34:35 -0800513 LOGV("getDuration: %d (msec)", msec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 return msec;
515}
516
517static void
518android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
519{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800520 LOGV("reset");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
522 if (mp == NULL ) {
523 jniThrowException(env, "java/lang/IllegalStateException", NULL);
524 return;
525 }
526 process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
527}
528
529static void
530android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, int streamtype)
531{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800532 LOGV("setAudioStreamType: %d", streamtype);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
534 if (mp == NULL ) {
535 jniThrowException(env, "java/lang/IllegalStateException", NULL);
536 return;
537 }
538 process_media_player_call( env, thiz, mp->setAudioStreamType(streamtype) , NULL, NULL );
539}
540
541static void
542android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
543{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800544 LOGV("setLooping: %d", looping);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
546 if (mp == NULL ) {
547 jniThrowException(env, "java/lang/IllegalStateException", NULL);
548 return;
549 }
550 process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL );
551}
552
553static jboolean
554android_media_MediaPlayer_isLooping(JNIEnv *env, jobject thiz)
555{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800556 LOGV("isLooping");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
558 if (mp == NULL ) {
559 jniThrowException(env, "java/lang/IllegalStateException", NULL);
560 return false;
561 }
562 return mp->isLooping();
563}
564
565static void
566android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, float leftVolume, float rightVolume)
567{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800568 LOGV("setVolume: left %f right %f", leftVolume, rightVolume);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
570 if (mp == NULL ) {
571 jniThrowException(env, "java/lang/IllegalStateException", NULL);
572 return;
573 }
574 process_media_player_call( env, thiz, mp->setVolume(leftVolume, rightVolume), NULL, NULL );
575}
576
577// FIXME: deprecated
578static jobject
579android_media_MediaPlayer_getFrameAt(JNIEnv *env, jobject thiz, jint msec)
580{
581 return NULL;
582}
583
Nicolas Catania20cb94e2009-05-12 23:25:55 -0700584
585// Sends the request and reply parcels to the media player via the
586// binder interface.
587static jint
588android_media_MediaPlayer_invoke(JNIEnv *env, jobject thiz,
589 jobject java_request, jobject java_reply)
590{
591 sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
592 if (media_player == NULL ) {
593 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Nicolas Cataniab2c69392009-07-08 08:57:42 -0700594 return UNKNOWN_ERROR;
Nicolas Catania20cb94e2009-05-12 23:25:55 -0700595 }
596
597
598 Parcel *request = parcelForJavaObject(env, java_request);
599 Parcel *reply = parcelForJavaObject(env, java_reply);
600
Nicolas Catania20cb94e2009-05-12 23:25:55 -0700601 // Don't use process_media_player_call which use the async loop to
602 // report errors, instead returns the status.
Nicolas Cataniab2c69392009-07-08 08:57:42 -0700603 return media_player->invoke(*request, reply);
Nicolas Catania20cb94e2009-05-12 23:25:55 -0700604}
605
Nicolas Cataniab2c69392009-07-08 08:57:42 -0700606// Sends the new filter to the client.
607static jint
608android_media_MediaPlayer_setMetadataFilter(JNIEnv *env, jobject thiz, jobject request)
609{
610 sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
611 if (media_player == NULL ) {
612 jniThrowException(env, "java/lang/IllegalStateException", NULL);
613 return UNKNOWN_ERROR;
614 }
615
616 Parcel *filter = parcelForJavaObject(env, request);
617
Nicolas Catania5d55c712009-07-09 09:21:33 -0700618 if (filter == NULL ) {
619 jniThrowException(env, "java/lang/RuntimeException", "Filter is null");
620 return UNKNOWN_ERROR;
621 }
622
Nicolas Cataniab2c69392009-07-08 08:57:42 -0700623 return media_player->setMetadataFilter(*filter);
624}
625
Nicolas Catania5d55c712009-07-09 09:21:33 -0700626static jboolean
627android_media_MediaPlayer_getMetadata(JNIEnv *env, jobject thiz, jboolean update_only,
628 jboolean apply_filter, jobject reply)
629{
630 sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
631 if (media_player == NULL ) {
632 jniThrowException(env, "java/lang/IllegalStateException", NULL);
633 return false;
634 }
635
636 Parcel *metadata = parcelForJavaObject(env, reply);
637
638 if (metadata == NULL ) {
639 jniThrowException(env, "java/lang/RuntimeException", "Reply parcel is null");
640 return false;
641 }
642
643 metadata->freeData();
644 // On return metadata is positioned at the beginning of the
645 // metadata. Note however that the parcel actually starts with the
646 // return code so you should not rewind the parcel using
647 // setDataPosition(0).
648 return media_player->getMetadata(update_only, apply_filter, metadata) == OK;
649}
650
Marco Nelissen4935d052009-08-03 11:12:58 -0700651// This function gets some field IDs, which in turn causes class initialization.
652// It is called from a static block in MediaPlayer, which won't run until the
653// first time an instance of this class is used.
654static void
655android_media_MediaPlayer_native_init(JNIEnv *env)
656{
657 jclass clazz;
658
659 clazz = env->FindClass("android/media/MediaPlayer");
660 if (clazz == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700661 return;
662 }
663
664 fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
665 if (fields.context == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700666 return;
667 }
668
669 fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
670 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
671 if (fields.post_event == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700672 return;
673 }
674
675 fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
676 if (fields.surface == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700677 return;
678 }
679
680 jclass surface = env->FindClass("android/view/Surface");
681 if (surface == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700682 return;
683 }
684
Mathias Agopian8b138322010-04-12 16:22:15 -0700685 fields.surface_native = env->GetFieldID(surface, ANDROID_VIEW_SURFACE_JNI_ID, "I");
Marco Nelissen4935d052009-08-03 11:12:58 -0700686 if (fields.surface_native == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700687 return;
688 }
Glenn Kastencc562a32011-02-08 17:26:17 -0800689
690 fields.surfaceTexture = env->GetFieldID(clazz, "mSurfaceTexture",
691 "Landroid/graphics/SurfaceTexture;");
692 if (fields.surfaceTexture == NULL) {
Glenn Kastencc562a32011-02-08 17:26:17 -0800693 return;
694 }
695
696 jclass surfaceTexture = env->FindClass("android/graphics/SurfaceTexture");
697 if (surfaceTexture == NULL) {
Glenn Kastencc562a32011-02-08 17:26:17 -0800698 return;
699 }
700
701 fields.surfaceTexture_native = env->GetFieldID(surfaceTexture,
702 ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "I");
703 if (fields.surfaceTexture_native == NULL) {
Glenn Kastencc562a32011-02-08 17:26:17 -0800704 return;
705 }
706
Marco Nelissen4935d052009-08-03 11:12:58 -0700707}
Nicolas Cataniab2c69392009-07-08 08:57:42 -0700708
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709static void
710android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
711{
712 LOGV("native_setup");
713 sp<MediaPlayer> mp = new MediaPlayer();
714 if (mp == NULL) {
715 jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
716 return;
717 }
718
719 // create new listener and give it to MediaPlayer
720 sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
721 mp->setListener(listener);
722
723 // Stow our new C++ MediaPlayer in an opaque field in the Java object.
724 setMediaPlayer(env, thiz, mp);
725}
726
727static void
728android_media_MediaPlayer_release(JNIEnv *env, jobject thiz)
729{
730 LOGV("release");
731 sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0);
732 if (mp != NULL) {
733 // this prevents native callbacks after the object is released
734 mp->setListener(0);
735 mp->disconnect();
736 }
737}
738
739static void
740android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz)
741{
742 LOGV("native_finalize");
743 android_media_MediaPlayer_release(env, thiz);
744}
745
Eric Laurent619346f2010-06-21 09:27:30 -0700746static void android_media_MediaPlayer_set_audio_session_id(JNIEnv *env, jobject thiz, jint sessionId) {
747 LOGV("set_session_id(): %d", sessionId);
748 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
749 if (mp == NULL ) {
750 jniThrowException(env, "java/lang/IllegalStateException", NULL);
751 return;
752 }
753 process_media_player_call( env, thiz, mp->setAudioSessionId(sessionId), NULL, NULL );
754}
755
756static jint android_media_MediaPlayer_get_audio_session_id(JNIEnv *env, jobject thiz) {
757 LOGV("get_session_id()");
758 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
759 if (mp == NULL ) {
760 jniThrowException(env, "java/lang/IllegalStateException", NULL);
761 return 0;
762 }
763
764 return mp->getAudioSessionId();
765}
766
Eric Laurent7070b362010-07-16 07:43:46 -0700767static void
768android_media_MediaPlayer_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level)
769{
770 LOGV("setAuxEffectSendLevel: level %f", level);
771 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
772 if (mp == NULL ) {
773 jniThrowException(env, "java/lang/IllegalStateException", NULL);
774 return;
775 }
776 process_media_player_call( env, thiz, mp->setAuxEffectSendLevel(level), NULL, NULL );
777}
778
779static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env, jobject thiz, jint effectId) {
Eric Laurentb3bdf3f2010-10-07 18:23:03 -0700780 LOGV("attachAuxEffect(): %d", effectId);
Eric Laurent7070b362010-07-16 07:43:46 -0700781 sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
782 if (mp == NULL ) {
783 jniThrowException(env, "java/lang/IllegalStateException", NULL);
784 return;
785 }
786 process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
787}
788
Gloria Wangd211f412011-02-19 18:37:57 -0800789static jint
790android_media_MediaPlayer_pullBatteryData(JNIEnv *env, jobject thiz, jobject java_reply)
791{
792 sp<IBinder> binder = defaultServiceManager()->getService(String16("media.player"));
793 sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
794 if (service.get() == NULL) {
795 jniThrowException(env, "java/lang/RuntimeException", "cannot get MediaPlayerService");
796 return UNKNOWN_ERROR;
797 }
798
799 Parcel *reply = parcelForJavaObject(env, java_reply);
800
801 return service->pullBatteryData(reply);
802}
803
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804// ----------------------------------------------------------------------------
805
806static JNINativeMethod gMethods[] = {
807 {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource},
Andreas Huber25643002010-01-28 11:19:57 -0800808 {"setDataSource", "(Ljava/lang/String;Ljava/util/Map;)V",(void *)android_media_MediaPlayer_setDataSourceAndHeaders},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
Glenn Kastencc562a32011-02-08 17:26:17 -0800810 {"_setVideoSurfaceOrSurfaceTexture", "()V", (void *)android_media_MediaPlayer_setVideoSurfaceOrSurfaceTexture},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 {"prepare", "()V", (void *)android_media_MediaPlayer_prepare},
812 {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
813 {"_start", "()V", (void *)android_media_MediaPlayer_start},
814 {"_stop", "()V", (void *)android_media_MediaPlayer_stop},
815 {"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
816 {"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},
817 {"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo},
818 {"_pause", "()V", (void *)android_media_MediaPlayer_pause},
819 {"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying},
820 {"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition},
821 {"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},
822 {"_release", "()V", (void *)android_media_MediaPlayer_release},
823 {"_reset", "()V", (void *)android_media_MediaPlayer_reset},
824 {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
825 {"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
826 {"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping},
827 {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
828 {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
Nicolas Catania20cb94e2009-05-12 23:25:55 -0700829 {"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
Nicolas Cataniab2c69392009-07-08 08:57:42 -0700830 {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
Nicolas Catania5d55c712009-07-09 09:21:33 -0700831 {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
Marco Nelissen4935d052009-08-03 11:12:58 -0700832 {"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
834 {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
Eric Laurent619346f2010-06-21 09:27:30 -0700835 {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
836 {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
Eric Laurent7070b362010-07-16 07:43:46 -0700837 {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
838 {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
Gloria Wangd211f412011-02-19 18:37:57 -0800839 {"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840};
841
842static const char* const kClassPathName = "android/media/MediaPlayer";
843
Marco Nelissen4935d052009-08-03 11:12:58 -0700844// This function only registers the native methods
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845static int register_android_media_MediaPlayer(JNIEnv *env)
846{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 return AndroidRuntime::registerNativeMethods(env,
848 "android/media/MediaPlayer", gMethods, NELEM(gMethods));
849}
850
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800851extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852extern int register_android_media_MediaRecorder(JNIEnv *env);
853extern int register_android_media_MediaScanner(JNIEnv *env);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854extern int register_android_media_ResampleInputStream(JNIEnv *env);
James Dongc3711942010-01-19 17:45:38 -0800855extern int register_android_media_MediaProfiles(JNIEnv *env);
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800856extern int register_android_media_AmrInputStream(JNIEnv *env);
Mike Lockwood0cd01362010-12-30 11:54:33 -0500857extern int register_android_mtp_MtpDatabase(JNIEnv *env);
Mike Lockwood8182e722010-12-30 15:38:45 -0500858extern int register_android_mtp_MtpDevice(JNIEnv *env);
Mike Lockwood0cd01362010-12-30 11:54:33 -0500859extern int register_android_mtp_MtpServer(JNIEnv *env);
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800860
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861jint JNI_OnLoad(JavaVM* vm, void* reserved)
862{
863 JNIEnv* env = NULL;
864 jint result = -1;
865
866 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
867 LOGE("ERROR: GetEnv failed\n");
868 goto bail;
869 }
870 assert(env != NULL);
871
872 if (register_android_media_MediaPlayer(env) < 0) {
873 LOGE("ERROR: MediaPlayer native registration failed\n");
874 goto bail;
875 }
876
877 if (register_android_media_MediaRecorder(env) < 0) {
878 LOGE("ERROR: MediaRecorder native registration failed\n");
879 goto bail;
880 }
881
882 if (register_android_media_MediaScanner(env) < 0) {
883 LOGE("ERROR: MediaScanner native registration failed\n");
884 goto bail;
885 }
886
887 if (register_android_media_MediaMetadataRetriever(env) < 0) {
888 LOGE("ERROR: MediaMetadataRetriever native registration failed\n");
889 goto bail;
890 }
891
892 if (register_android_media_AmrInputStream(env) < 0) {
893 LOGE("ERROR: AmrInputStream native registration failed\n");
894 goto bail;
895 }
896
897 if (register_android_media_ResampleInputStream(env) < 0) {
898 LOGE("ERROR: ResampleInputStream native registration failed\n");
899 goto bail;
900 }
901
James Dongc3711942010-01-19 17:45:38 -0800902 if (register_android_media_MediaProfiles(env) < 0) {
903 LOGE("ERROR: MediaProfiles native registration failed");
904 goto bail;
905 }
906
Mike Lockwood0cd01362010-12-30 11:54:33 -0500907 if (register_android_mtp_MtpDatabase(env) < 0) {
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400908 LOGE("ERROR: MtpDatabase native registration failed");
909 goto bail;
910 }
911
Mike Lockwood8182e722010-12-30 15:38:45 -0500912 if (register_android_mtp_MtpDevice(env) < 0) {
913 LOGE("ERROR: MtpDevice native registration failed");
914 goto bail;
915 }
916
Mike Lockwood0cd01362010-12-30 11:54:33 -0500917 if (register_android_mtp_MtpServer(env) < 0) {
Mike Lockwood81ea83d2010-06-30 17:49:41 -0400918 LOGE("ERROR: MtpServer native registration failed");
919 goto bail;
920 }
921
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 /* success -- return valid version number */
923 result = JNI_VERSION_1_4;
924
925bail:
926 return result;
927}
928
929// KTHXBYE