android_media_MediaPlayer.cpp revision 4df2423a947bcd3f024cc3d3a1a315a8dc428598
1/* //device/libs/android_runtime/android_media_MediaPlayer.cpp
2**
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>
23#include <stdio.h>
24#include <assert.h>
25#include <limits.h>
26#include <unistd.h>
27#include <fcntl.h>
28#include <utils/threads.h>
29#include "jni.h"
30#include "JNIHelp.h"
31#include "android_runtime/AndroidRuntime.h"
32#include "utils/Errors.h"  // for status_t
33
34
35// ----------------------------------------------------------------------------
36
37using namespace android;
38
39// ----------------------------------------------------------------------------
40
41struct fields_t {
42    jfieldID    context;
43    jfieldID    surface;
44    /* actually in android.view.Surface XXX */
45    jfieldID    surface_native;
46
47    jmethodID   post_event;
48};
49static fields_t fields;
50
51static Mutex sLock;
52
53// ----------------------------------------------------------------------------
54// ref-counted object for callbacks
55class JNIMediaPlayerListener: public MediaPlayerListener
56{
57public:
58    JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
59    ~JNIMediaPlayerListener();
60    void notify(int msg, int ext1, int ext2);
61private:
62    JNIMediaPlayerListener();
63    jclass      mClass;     // Reference to MediaPlayer class
64    jobject     mObject;    // Weak ref to MediaPlayer Java object to call on
65};
66
67JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
68{
69
70    // Hold onto the MediaPlayer class for use in calling the static method
71    // that posts events to the application thread.
72    jclass clazz = env->GetObjectClass(thiz);
73    if (clazz == NULL) {
74        LOGE("Can't find android/media/MediaPlayer");
75        jniThrowException(env, "java/lang/Exception", NULL);
76        return;
77    }
78    mClass = (jclass)env->NewGlobalRef(clazz);
79
80    // We use a weak reference so the MediaPlayer object can be garbage collected.
81    // The reference is only used as a proxy for callbacks.
82    mObject  = env->NewGlobalRef(weak_thiz);
83}
84
85JNIMediaPlayerListener::~JNIMediaPlayerListener()
86{
87    // remove global references
88    JNIEnv *env = AndroidRuntime::getJNIEnv();
89    env->DeleteGlobalRef(mObject);
90    env->DeleteGlobalRef(mClass);
91}
92
93void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2)
94{
95    JNIEnv *env = AndroidRuntime::getJNIEnv();
96    env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, 0);
97}
98
99// ----------------------------------------------------------------------------
100
101static sp<Surface> get_surface(JNIEnv* env, jobject clazz)
102{
103    Surface* const p = (Surface*)env->GetIntField(clazz, fields.surface_native);
104    return sp<Surface>(p);
105}
106
107static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
108{
109    Mutex::Autolock l(sLock);
110    MediaPlayer* const p = (MediaPlayer*)env->GetIntField(thiz, fields.context);
111    return sp<MediaPlayer>(p);
112}
113
114static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
115{
116    Mutex::Autolock l(sLock);
117    sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
118    if (player.get()) {
119        player->incStrong(thiz);
120    }
121    if (old != 0) {
122        old->decStrong(thiz);
123    }
124    env->SetIntField(thiz, fields.context, (int)player.get());
125    return old;
126}
127
128// If exception is NULL and opStatus is not OK, this method sends an error
129// event to the client application; otherwise, if exception is not NULL and
130// opStatus is not OK, this method throws the given exception to the client
131// application.
132static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
133{
134    if (exception == NULL) {  // Don't throw exception. Instead, send an event.
135        if (opStatus != (status_t) OK) {
136            sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
137            if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
138        }
139    } else {  // Throw exception!
140        if ( opStatus == (status_t) INVALID_OPERATION ) {
141            jniThrowException(env, "java/lang/IllegalStateException", NULL);
142        } else if ( opStatus != (status_t) OK ) {
143            if (strlen(message) > 230) {
144               // if the message is too long, don't bother displaying the status code
145               jniThrowException( env, exception, message);
146            } else {
147               char msg[256];
148                // append the status code to the message
149               sprintf(msg, "%s: status=0x%X", message, opStatus);
150               jniThrowException( env, exception, msg);
151            }
152        }
153    }
154}
155
156static void
157android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path)
158{
159    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
160    if (mp == NULL ) {
161        jniThrowException(env, "java/lang/IllegalStateException", NULL);
162        return;
163    }
164
165    if (path == NULL) {
166        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
167        return;
168    }
169
170    const char *pathStr = env->GetStringUTFChars(path, NULL);
171    if (pathStr == NULL) {  // Out of memory
172        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
173        return;
174    }
175    LOGV("setDataSource: path %s", pathStr);
176    status_t opStatus = mp->setDataSource(pathStr);
177
178    // Make sure that local ref is released before a potential exception
179    env->ReleaseStringUTFChars(path, pathStr);
180    process_media_player_call( env, thiz, opStatus, "java/io/IOException", "setDataSource failed." );
181}
182
183static void
184android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
185{
186    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
187    if (mp == NULL ) {
188        jniThrowException(env, "java/lang/IllegalStateException", NULL);
189        return;
190    }
191
192    if (fileDescriptor == NULL) {
193        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
194        return;
195    }
196    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
197    LOGV("setDataSourceFD: fd %d", fd);
198    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
199}
200
201
202static void
203android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
204{
205    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
206    if (mp == NULL ) {
207        jniThrowException(env, "java/lang/IllegalStateException", NULL);
208        return;
209    }
210    jobject surface = env->GetObjectField(thiz, fields.surface);
211    if (surface != NULL) {
212        const sp<Surface>& native_surface = get_surface(env, surface);
213        LOGV("prepare: surface=%p (id=%d)",
214             native_surface.get(), native_surface->ID());
215        mp->setVideoSurface(native_surface);
216    }
217    process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
218}
219
220static void
221android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
222{
223    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
224    if (mp == NULL ) {
225        jniThrowException(env, "java/lang/IllegalStateException", NULL);
226        return;
227    }
228    jobject surface = env->GetObjectField(thiz, fields.surface);
229    if (surface != NULL) {
230        const sp<Surface>& native_surface = get_surface(env, surface);
231        LOGV("prepareAsync: surface=%p (id=%d)",
232             native_surface.get(), native_surface->ID());
233        mp->setVideoSurface(native_surface);
234    }
235    process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
236}
237
238static void
239android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
240{
241    LOGV("start");
242    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
243    if (mp == NULL ) {
244        jniThrowException(env, "java/lang/IllegalStateException", NULL);
245        return;
246    }
247    process_media_player_call( env, thiz, mp->start(), NULL, NULL );
248}
249
250static void
251android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz)
252{
253    LOGV("stop");
254    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
255    if (mp == NULL ) {
256        jniThrowException(env, "java/lang/IllegalStateException", NULL);
257        return;
258    }
259    process_media_player_call( env, thiz, mp->stop(), NULL, NULL );
260}
261
262static void
263android_media_MediaPlayer_pause(JNIEnv *env, jobject thiz)
264{
265    LOGV("pause");
266    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
267    if (mp == NULL ) {
268        jniThrowException(env, "java/lang/IllegalStateException", NULL);
269        return;
270    }
271    process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
272}
273
274static jboolean
275android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz)
276{
277    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
278    if (mp == NULL ) {
279        jniThrowException(env, "java/lang/IllegalStateException", NULL);
280        return false;
281    }
282    const jboolean is_playing = mp->isPlaying();
283
284    LOGV("isPlaying: %d", is_playing);
285    return is_playing;
286}
287
288static void
289android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec)
290{
291    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
292    if (mp == NULL ) {
293        jniThrowException(env, "java/lang/IllegalStateException", NULL);
294        return;
295    }
296    LOGV("seekTo: %d(msec)", msec);
297    process_media_player_call( env, thiz, mp->seekTo(msec), NULL, NULL );
298}
299
300static int
301android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz)
302{
303    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
304    if (mp == NULL ) {
305        jniThrowException(env, "java/lang/IllegalStateException", NULL);
306        return 0;
307    }
308    int w;
309    if (0 != mp->getVideoWidth(&w)) {
310        LOGE("getVideoWidth failed");
311        w = 0;
312    }
313    LOGV("getVideoWidth: %d", w);
314    return w;
315}
316
317static int
318android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz)
319{
320    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
321    if (mp == NULL ) {
322        jniThrowException(env, "java/lang/IllegalStateException", NULL);
323        return 0;
324    }
325    int h;
326    if (0 != mp->getVideoHeight(&h)) {
327        LOGE("getVideoHeight failed");
328        h = 0;
329    }
330    LOGV("getVideoHeight: %d", h);
331    return h;
332}
333
334
335static int
336android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz)
337{
338    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
339    if (mp == NULL ) {
340        jniThrowException(env, "java/lang/IllegalStateException", NULL);
341        return 0;
342    }
343    int msec;
344    process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL );
345    LOGV("getCurrentPosition: %d (msec)", msec);
346    return msec;
347}
348
349static int
350android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz)
351{
352    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
353    if (mp == NULL ) {
354        jniThrowException(env, "java/lang/IllegalStateException", NULL);
355        return 0;
356    }
357    int msec;
358    process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL );
359    LOGV("getDuration: %d (msec)", msec);
360    return msec;
361}
362
363static void
364android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
365{
366    LOGV("reset");
367    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
368    if (mp == NULL ) {
369        jniThrowException(env, "java/lang/IllegalStateException", NULL);
370        return;
371    }
372    process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
373}
374
375static void
376android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, int streamtype)
377{
378    LOGV("setAudioStreamType: %d", streamtype);
379    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
380    if (mp == NULL ) {
381        jniThrowException(env, "java/lang/IllegalStateException", NULL);
382        return;
383    }
384    process_media_player_call( env, thiz, mp->setAudioStreamType(streamtype) , NULL, NULL );
385}
386
387static void
388android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
389{
390    LOGV("setLooping: %d", looping);
391    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
392    if (mp == NULL ) {
393        jniThrowException(env, "java/lang/IllegalStateException", NULL);
394        return;
395    }
396    process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL );
397}
398
399static jboolean
400android_media_MediaPlayer_isLooping(JNIEnv *env, jobject thiz)
401{
402    LOGV("isLooping");
403    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
404    if (mp == NULL ) {
405        jniThrowException(env, "java/lang/IllegalStateException", NULL);
406        return false;
407    }
408    return mp->isLooping();
409}
410
411static void
412android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, float leftVolume, float rightVolume)
413{
414    LOGV("setVolume: left %f  right %f", leftVolume, rightVolume);
415    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
416    if (mp == NULL ) {
417        jniThrowException(env, "java/lang/IllegalStateException", NULL);
418        return;
419    }
420    process_media_player_call( env, thiz, mp->setVolume(leftVolume, rightVolume), NULL, NULL );
421}
422
423// FIXME: deprecated
424static jobject
425android_media_MediaPlayer_getFrameAt(JNIEnv *env, jobject thiz, jint msec)
426{
427    return NULL;
428}
429
430static void
431android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
432{
433    LOGV("native_setup");
434    sp<MediaPlayer> mp = new MediaPlayer();
435    if (mp == NULL) {
436        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
437        return;
438    }
439
440    // create new listener and give it to MediaPlayer
441    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
442    mp->setListener(listener);
443
444    // Stow our new C++ MediaPlayer in an opaque field in the Java object.
445    setMediaPlayer(env, thiz, mp);
446}
447
448static void
449android_media_MediaPlayer_release(JNIEnv *env, jobject thiz)
450{
451    LOGV("release");
452    sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0);
453    if (mp != NULL) {
454        // this prevents native callbacks after the object is released
455        mp->setListener(0);
456        mp->disconnect();
457    }
458}
459
460static void
461android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz)
462{
463    LOGV("native_finalize");
464    android_media_MediaPlayer_release(env, thiz);
465}
466
467// ----------------------------------------------------------------------------
468
469static JNINativeMethod gMethods[] = {
470    {"setDataSource",       "(Ljava/lang/String;)V",            (void *)android_media_MediaPlayer_setDataSource},
471    {"setDataSource",       "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
472    {"prepare",             "()V",                              (void *)android_media_MediaPlayer_prepare},
473    {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
474    {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
475    {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
476    {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},
477    {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
478    {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},
479    {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},
480    {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},
481    {"getCurrentPosition",  "()I",                              (void *)android_media_MediaPlayer_getCurrentPosition},
482    {"getDuration",         "()I",                              (void *)android_media_MediaPlayer_getDuration},
483    {"_release",            "()V",                              (void *)android_media_MediaPlayer_release},
484    {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},
485    {"setAudioStreamType",  "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},
486    {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},
487    {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},
488    {"setVolume",           "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
489    {"getFrameAt",          "(I)Landroid/graphics/Bitmap;",     (void *)android_media_MediaPlayer_getFrameAt},
490    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
491    {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
492};
493
494static const char* const kClassPathName = "android/media/MediaPlayer";
495
496static int register_android_media_MediaPlayer(JNIEnv *env)
497{
498    jclass clazz;
499
500    clazz = env->FindClass("android/media/MediaPlayer");
501    if (clazz == NULL) {
502        LOGE("Can't find android/media/MediaPlayer");
503        return -1;
504    }
505
506    fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
507    if (fields.context == NULL) {
508        LOGE("Can't find MediaPlayer.mNativeContext");
509        return -1;
510    }
511
512    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
513                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
514    if (fields.post_event == NULL) {
515        LOGE("Can't find MediaPlayer.postEventFromNative");
516        return -1;
517    }
518
519    fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
520    if (fields.surface == NULL) {
521        LOGE("Can't find MediaPlayer.mSurface");
522        return -1;
523    }
524
525    jclass surface = env->FindClass("android/view/Surface");
526    if (surface == NULL) {
527        LOGE("Can't find android/view/Surface");
528        return -1;
529    }
530
531    fields.surface_native = env->GetFieldID(surface, "mSurface", "I");
532    if (fields.surface_native == NULL) {
533        LOGE("Can't find Surface fields");
534        return -1;
535    }
536
537    return AndroidRuntime::registerNativeMethods(env,
538                "android/media/MediaPlayer", gMethods, NELEM(gMethods));
539}
540
541extern int register_android_media_MediaRecorder(JNIEnv *env);
542extern int register_android_media_MediaScanner(JNIEnv *env);
543extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
544extern int register_android_media_AmrInputStream(JNIEnv *env);
545extern int register_android_media_ResampleInputStream(JNIEnv *env);
546
547jint JNI_OnLoad(JavaVM* vm, void* reserved)
548{
549    JNIEnv* env = NULL;
550    jint result = -1;
551
552    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
553        LOGE("ERROR: GetEnv failed\n");
554        goto bail;
555    }
556    assert(env != NULL);
557
558    if (register_android_media_MediaPlayer(env) < 0) {
559        LOGE("ERROR: MediaPlayer native registration failed\n");
560        goto bail;
561    }
562
563    if (register_android_media_MediaRecorder(env) < 0) {
564        LOGE("ERROR: MediaRecorder native registration failed\n");
565        goto bail;
566    }
567
568    if (register_android_media_MediaScanner(env) < 0) {
569        LOGE("ERROR: MediaScanner native registration failed\n");
570        goto bail;
571    }
572
573    if (register_android_media_MediaMetadataRetriever(env) < 0) {
574        LOGE("ERROR: MediaMetadataRetriever native registration failed\n");
575        goto bail;
576    }
577
578    if (register_android_media_AmrInputStream(env) < 0) {
579        LOGE("ERROR: AmrInputStream native registration failed\n");
580        goto bail;
581    }
582
583    if (register_android_media_ResampleInputStream(env) < 0) {
584        LOGE("ERROR: ResampleInputStream native registration failed\n");
585        goto bail;
586    }
587
588    /* success -- return valid version number */
589    result = JNI_VERSION_1_4;
590
591bail:
592    return result;
593}
594
595// KTHXBYE
596