diff options
Diffstat (limited to 'media/jni')
-rw-r--r-- | media/jni/android_media_MediaPlayer.cpp | 46 | ||||
-rw-r--r-- | media/jni/android_media_MediaRecorder.cpp | 228 | ||||
-rw-r--r-- | media/jni/soundpool/SoundPool.cpp | 51 | ||||
-rw-r--r-- | media/jni/soundpool/SoundPool.h | 5 |
4 files changed, 235 insertions, 95 deletions
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 5562254..707db02 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -29,6 +29,7 @@ #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include "utils/Errors.h" // for status_t // ---------------------------------------------------------------------------- @@ -171,6 +172,7 @@ android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path) jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; } + LOGV("setDataSource: path %s", pathStr); status_t opStatus = mp->setDataSource(pathStr); // Make sure that local ref is released before a potential exception @@ -192,6 +194,7 @@ android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fil return; } int fd = getParcelFileDescriptorFD(env, fileDescriptor); + LOGV("setDataSourceFD: fd %d", fd); process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); } @@ -207,8 +210,8 @@ android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) jobject surface = env->GetObjectField(thiz, fields.surface); if (surface != NULL) { const sp<Surface>& native_surface = get_surface(env, surface); - //LOGI("prepare: surface=%p (id=%d)", - // native_surface.get(), native_surface->ID()); + LOGV("prepare: surface=%p (id=%d)", + native_surface.get(), native_surface->ID()); mp->setVideoSurface(native_surface); } process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." ); @@ -225,8 +228,8 @@ android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz) jobject surface = env->GetObjectField(thiz, fields.surface); if (surface != NULL) { const sp<Surface>& native_surface = get_surface(env, surface); - LOGI("prepareAsync: surface=%p (id=%d)", - native_surface.get(), native_surface->ID()); + LOGV("prepareAsync: surface=%p (id=%d)", + native_surface.get(), native_surface->ID()); mp->setVideoSurface(native_surface); } process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." ); @@ -235,6 +238,7 @@ android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz) static void android_media_MediaPlayer_start(JNIEnv *env, jobject thiz) { + LOGV("start"); sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); @@ -246,6 +250,7 @@ android_media_MediaPlayer_start(JNIEnv *env, jobject thiz) static void android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz) { + LOGV("stop"); sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); @@ -257,6 +262,7 @@ android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz) static void android_media_MediaPlayer_pause(JNIEnv *env, jobject thiz) { + LOGV("pause"); sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); @@ -273,7 +279,10 @@ android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz) jniThrowException(env, "java/lang/IllegalStateException", NULL); return false; } - return mp->isPlaying(); + const jboolean is_playing = mp->isPlaying(); + + LOGV("isPlaying: %d", is_playing); + return is_playing; } static void @@ -284,6 +293,7 @@ android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec) jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } + LOGV("seekTo: %d(msec)", msec); process_media_player_call( env, thiz, mp->seekTo(msec), NULL, NULL ); } @@ -296,9 +306,12 @@ android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz) return 0; } int w; - if (0 == mp->getVideoWidth(&w)) - return w; - return 0; + if (0 != mp->getVideoWidth(&w)) { + LOGE("getVideoWidth failed"); + w = 0; + } + LOGV("getVideoWidth: %d", w); + return w; } static int @@ -310,9 +323,12 @@ android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz) return 0; } int h; - if (0 == mp->getVideoHeight(&h)) - return h; - return 0; + if (0 != mp->getVideoHeight(&h)) { + LOGE("getVideoHeight failed"); + h = 0; + } + LOGV("getVideoHeight: %d", h); + return h; } @@ -326,6 +342,7 @@ android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz) } int msec; process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL ); + LOGV("getCurrentPosition: %d (msec)", msec); return msec; } @@ -339,12 +356,14 @@ android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz) } int msec; process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL ); + LOGV("getDuration: %d (msec)", msec); return msec; } static void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz) { + LOGV("reset"); sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); @@ -356,6 +375,7 @@ android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz) static void android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, int streamtype) { + LOGV("setAudioStreamType: %d", streamtype); sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); @@ -367,6 +387,7 @@ android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, int stre static void android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping) { + LOGV("setLooping: %d", looping); sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); @@ -378,6 +399,7 @@ android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping static jboolean android_media_MediaPlayer_isLooping(JNIEnv *env, jobject thiz) { + LOGV("isLooping"); sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); @@ -389,6 +411,7 @@ android_media_MediaPlayer_isLooping(JNIEnv *env, jobject thiz) static void android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, float leftVolume, float rightVolume) { + LOGV("setVolume: left %f right %f", leftVolume, rightVolume); sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); @@ -570,4 +593,3 @@ bail: } // KTHXBYE - diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 2810a9c..1e508d2 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -41,16 +41,68 @@ using namespace android; // ---------------------------------------------------------------------------- // helper function to extract a native Camera object from a Camera Java object -extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz); +extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct camera_context_t** context); struct fields_t { jfieldID context; jfieldID surface; /* actually in android.view.Surface XXX */ jfieldID surface_native; + + jmethodID post_event; }; static fields_t fields; +static Mutex sLock; + +// ---------------------------------------------------------------------------- +// ref-counted object for callbacks +class JNIMediaRecorderListener: public MediaRecorderListener +{ +public: + JNIMediaRecorderListener(JNIEnv* env, jobject thiz, jobject weak_thiz); + ~JNIMediaRecorderListener(); + void notify(int msg, int ext1, int ext2); +private: + JNIMediaRecorderListener(); + jclass mClass; // Reference to MediaRecorder class + jobject mObject; // Weak ref to MediaRecorder Java object to call on +}; + +JNIMediaRecorderListener::JNIMediaRecorderListener(JNIEnv* env, jobject thiz, jobject weak_thiz) +{ + + // Hold onto the MediaRecorder class for use in calling the static method + // that posts events to the application thread. + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + LOGE("Can't find android/media/MediaRecorder"); + jniThrowException(env, "java/lang/Exception", NULL); + return; + } + mClass = (jclass)env->NewGlobalRef(clazz); + + // We use a weak reference so the MediaRecorder object can be garbage collected. + // The reference is only used as a proxy for callbacks. + mObject = env->NewGlobalRef(weak_thiz); +} + +JNIMediaRecorderListener::~JNIMediaRecorderListener() +{ + // remove global references + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mObject); + env->DeleteGlobalRef(mClass); +} + +void JNIMediaRecorderListener::notify(int msg, int ext1, int ext2) +{ + LOGV("JNIMediaRecorderListener::notify"); + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, 0); +} + // ---------------------------------------------------------------------------- static sp<Surface> get_surface(JNIEnv* env, jobject clazz) @@ -60,21 +112,46 @@ static sp<Surface> get_surface(JNIEnv* env, jobject clazz) return sp<Surface>(p); } -static void process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message) +// Returns true if it throws an exception. +static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message) { LOGV("process_media_recorder_call"); if (opStatus == (status_t)INVALID_OPERATION) { jniThrowException(env, "java/lang/IllegalStateException", NULL); + return true; } else if (opStatus != (status_t)OK) { jniThrowException(env, exception, message); + return true; + } + return false; +} + +static sp<MediaRecorder> getMediaRecorder(JNIEnv* env, jobject thiz) +{ + Mutex::Autolock l(sLock); + MediaRecorder* const p = (MediaRecorder*)env->GetIntField(thiz, fields.context); + return sp<MediaRecorder>(p); +} + +static sp<MediaRecorder> setMediaRecorder(JNIEnv* env, jobject thiz, const sp<MediaRecorder>& recorder) +{ + Mutex::Autolock l(sLock); + sp<MediaRecorder> old = (MediaRecorder*)env->GetIntField(thiz, fields.context); + if (recorder.get()) { + recorder->incStrong(thiz); + } + if (old != 0) { + old->decStrong(thiz); } - return; + env->SetIntField(thiz, fields.context, (int)recorder.get()); + return old; } + static void android_media_MediaRecorder_setCamera(JNIEnv* env, jobject thiz, jobject camera) { - sp<Camera> c = get_native_camera(env, camera); - MediaRecorder *mr = (MediaRecorder*)env->GetIntField(thiz, fields.context); + sp<Camera> c = get_native_camera(env, camera, NULL); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->setCamera(c->remote()), "java/lang/RuntimeException", "setCamera failed."); } @@ -87,7 +164,7 @@ android_media_MediaRecorder_setVideoSource(JNIEnv *env, jobject thiz, jint vs) jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid video source"); return; } - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->setVideoSource(vs), "java/lang/RuntimeException", "setVideoSource failed."); } @@ -99,7 +176,7 @@ android_media_MediaRecorder_setAudioSource(JNIEnv *env, jobject thiz, jint as) jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio source"); return; } - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->setAudioSource(as), "java/lang/RuntimeException", "setAudioSource failed."); } @@ -111,7 +188,7 @@ android_media_MediaRecorder_setOutputFormat(JNIEnv *env, jobject thiz, jint of) jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid output format"); return; } - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->setOutputFormat(of), "java/lang/RuntimeException", "setOutputFormat failed."); } @@ -123,7 +200,7 @@ android_media_MediaRecorder_setVideoEncoder(JNIEnv *env, jobject thiz, jint ve) jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid video encoder"); return; } - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->setVideoEncoder(ve), "java/lang/RuntimeException", "setVideoEncoder failed."); } @@ -135,37 +212,29 @@ android_media_MediaRecorder_setAudioEncoder(JNIEnv *env, jobject thiz, jint ae) jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio encoder"); return; } - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->setAudioEncoder(ae), "java/lang/RuntimeException", "setAudioEncoder failed."); } static void -android_media_MediaRecorder_setOutputFile(JNIEnv *env, jobject thiz, jstring path) +android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) { LOGV("setOutputFile"); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); - - if (path == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Path is a NULL pointer"); + if (fileDescriptor == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } - const char *pathStr = env->GetStringUTFChars(path, NULL); - if (pathStr == NULL) { // Out of memory - jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); - return; - } - status_t opStatus = mr->setOutputFile(pathStr); - - // Make sure that local ref is released before a potential exception - env->ReleaseStringUTFChars(path, pathStr); - process_media_recorder_call(env, opStatus, "java/lang/RuntimeException", "setOutputFile failed."); + int fd = getParcelFileDescriptorFD(env, fileDescriptor); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); + status_t opStatus = mr->setOutputFile(fd, offset, length); + process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed."); } static void android_media_MediaRecorder_setVideoSize(JNIEnv *env, jobject thiz, jint width, jint height) { LOGV("setVideoSize(%d, %d)", width, height); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); if (width <= 0 || height <= 0) { jniThrowException(env, "java/lang/IllegalArgumentException", "invalid video size"); @@ -182,21 +251,35 @@ android_media_MediaRecorder_setVideoFrameRate(JNIEnv *env, jobject thiz, jint ra jniThrowException(env, "java/lang/IllegalArgumentException", "invalid frame rate"); return; } - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->setVideoFrameRate(rate), "java/lang/RuntimeException", "setVideoFrameRate failed."); } static void +android_media_MediaRecorder_setMaxDuration(JNIEnv *env, jobject thiz, jint max_duration_ms) +{ + LOGV("setMaxDuration(%d)", max_duration_ms); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); + + char params[64]; + sprintf(params, "max-duration=%d", max_duration_ms); + + process_media_recorder_call(env, mr->setParameters(String8(params)), "java/lang/RuntimeException", "setMaxDuration failed."); +} + +static void android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz) { LOGV("prepare"); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); jobject surface = env->GetObjectField(thiz, fields.surface); if (surface != NULL) { const sp<Surface>& native_surface = get_surface(env, surface); LOGI("prepare: surface=%p (id=%d)", native_surface.get(), native_surface->ID()); - process_media_recorder_call(env, mr->setPreviewSurface(native_surface), "java/lang/RuntimeException", "setPreviewSurface failed."); + if (process_media_recorder_call(env, mr->setPreviewSurface(native_surface), "java/lang/RuntimeException", "setPreviewSurface failed.")) { + return; + } } process_media_recorder_call(env, mr->prepare(), "java/io/IOException", "prepare failed."); } @@ -205,7 +288,7 @@ static int android_media_MediaRecorder_native_getMaxAmplitude(JNIEnv *env, jobject thiz) { LOGV("getMaxAmplitude"); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); int result = 0; process_media_recorder_call(env, mr->getMaxAmplitude(&result), "java/lang/RuntimeException", "getMaxAmplitude failed."); return result; @@ -215,7 +298,7 @@ static void android_media_MediaRecorder_start(JNIEnv *env, jobject thiz) { LOGV("start"); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->start(), "java/lang/RuntimeException", "start failed."); } @@ -223,68 +306,78 @@ static void android_media_MediaRecorder_stop(JNIEnv *env, jobject thiz) { LOGV("stop"); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->stop(), "java/lang/RuntimeException", "stop failed."); } static void -android_media_MediaRecorder_reset(JNIEnv *env, jobject thiz) +android_media_MediaRecorder_native_reset(JNIEnv *env, jobject thiz) { - LOGV("reset"); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); - process_media_recorder_call(env, mr->reset(), "java/lang/RuntimeException", "reset failed."); + LOGV("native_reset"); + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); + process_media_recorder_call(env, mr->reset(), "java/lang/RuntimeException", "native_reset failed."); } static void android_media_MediaRecorder_release(JNIEnv *env, jobject thiz) { LOGV("release"); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); - env->SetIntField(thiz, fields.context, 0); - delete mr; + sp<MediaRecorder> mr = setMediaRecorder(env, thiz, 0); + if (mr != NULL) { + mr->setListener(NULL); + mr->release(); + } } static void -android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz) +android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) { LOGV("setup"); - MediaRecorder *mr = new MediaRecorder(); - if (mr->initCheck() == NO_ERROR) { - env->SetIntField(thiz, fields.context, (int)mr); - } else { - delete mr; + sp<MediaRecorder> mr = new MediaRecorder(); + if (mr == NULL) { + jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); + return; + } + if (mr->initCheck() != NO_ERROR) { jniThrowException(env, "java/lang/IOException", "Unable to initialize camera"); + return; } + + // create new listener and give it to MediaRecorder + sp<JNIMediaRecorderListener> listener = new JNIMediaRecorderListener(env, thiz, weak_this); + mr->setListener(listener); + + setMediaRecorder(env, thiz, mr); } static void android_media_MediaRecorder_native_finalize(JNIEnv *env, jobject thiz) { LOGV("finalize"); - MediaRecorder *mr = (MediaRecorder *)env->GetIntField(thiz, fields.context); - delete mr; + android_media_MediaRecorder_release(env, thiz); } // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { - {"setCamera", "(Landroid/hardware/Camera;)V",(void *)android_media_MediaRecorder_setCamera}, - {"setVideoSource", "(I)V", (void *)android_media_MediaRecorder_setVideoSource}, - {"setAudioSource", "(I)V", (void *)android_media_MediaRecorder_setAudioSource}, - {"setOutputFormat", "(I)V", (void *)android_media_MediaRecorder_setOutputFormat}, - {"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder}, - {"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder}, - {"setOutputFile", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setOutputFile}, - {"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize}, - {"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate}, - {"prepare", "()V", (void *)android_media_MediaRecorder_prepare}, - {"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude}, - {"start", "()V", (void *)android_media_MediaRecorder_start}, - {"stop", "()V", (void *)android_media_MediaRecorder_stop}, - {"reset", "()V", (void *)android_media_MediaRecorder_reset}, - {"release", "()V", (void *)android_media_MediaRecorder_release}, - {"native_setup", "()V", (void *)android_media_MediaRecorder_native_setup}, - {"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize}, + {"setCamera", "(Landroid/hardware/Camera;)V", (void *)android_media_MediaRecorder_setCamera}, + {"setVideoSource", "(I)V", (void *)android_media_MediaRecorder_setVideoSource}, + {"setAudioSource", "(I)V", (void *)android_media_MediaRecorder_setAudioSource}, + {"setOutputFormat", "(I)V", (void *)android_media_MediaRecorder_setOutputFormat}, + {"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder}, + {"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder}, + {"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD}, + {"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize}, + {"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate}, + {"setMaxDuration", "(I)V", (void *)android_media_MediaRecorder_setMaxDuration}, + {"_prepare", "()V", (void *)android_media_MediaRecorder_prepare}, + {"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude}, + {"start", "()V", (void *)android_media_MediaRecorder_start}, + {"stop", "()V", (void *)android_media_MediaRecorder_stop}, + {"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset}, + {"release", "()V", (void *)android_media_MediaRecorder_release}, + {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaRecorder_native_setup}, + {"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize}, }; static const char* const kClassPathName = "android/media/MediaRecorder"; @@ -323,6 +416,13 @@ int register_android_media_MediaRecorder(JNIEnv *env) return -1; } + fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", + "(Ljava/lang/Object;IIILjava/lang/Object;)V"); + if (fields.post_event == NULL) { + LOGE("Can't find MediaRecorder.postEventFromNative"); + return -1; + } + return AndroidRuntime::registerNativeMethods(env, "android/media/MediaRecorder", gMethods, NELEM(gMethods)); } diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 7872a8d..02731825 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -64,13 +64,6 @@ SoundPool::SoundPool(jobject soundPoolRef, int maxChannels, int streamType, int mChannels.push_back(&mChannelPool[i]); } - if (AudioSystem::getOutputFrameCount(&mFrameCount) != NO_ERROR) { - mFrameCount = kDefaultFrameCount; - } - if (AudioSystem::getOutputSamplingRate(&mSampleRate) != NO_ERROR) { - mSampleRate = kDefaultSampleRate; - } - // start decode thread startThreads(); } @@ -481,8 +474,8 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV { AudioTrack* oldTrack; - LOGV("play: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f", - sample->sampleID(), nextChannelID, leftVolume, rightVolume, priority, loop, rate); + LOGV("play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f", + this, sample->sampleID(), nextChannelID, leftVolume, rightVolume, priority, loop, rate); // if not idle, this voice is being stolen if (mState != IDLE) { @@ -496,9 +489,18 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV } // initialize track + int afFrameCount; + int afSampleRate; + int streamType = mSoundPool->streamType(); + if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { + afFrameCount = kDefaultFrameCount; + } + if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { + afSampleRate = kDefaultSampleRate; + } int numChannels = sample->numChannels(); uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5); - uint32_t bufferFrames = (mSoundPool->mFrameCount * sampleRate) / mSoundPool->mSampleRate; + uint32_t bufferFrames = (afFrameCount * sampleRate) / afSampleRate; uint32_t frameCount = 0; if (loop) { @@ -511,12 +513,21 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV } AudioTrack* newTrack; + + // mToggle toggles each time a track is started on a given channel. + // The toggle is concatenated with the SoundChannel address and passed to AudioTrack + // as callback user data. This enables the detection of callbacks received from the old + // audio track while the new one is being started and avoids processing them with + // wrong audio audio buffer size (mAudioBufferSize) + unsigned long toggle = mToggle ^ 1; + void *userData = (void *)((unsigned long)this | toggle); + #ifdef USE_SHARED_MEM_BUFFER - newTrack = new AudioTrack(mSoundPool->streamType(), sampleRate, sample->format(), - numChannels, sample->getIMemory(), 0, callback, this); + newTrack = new AudioTrack(streamType, sampleRate, sample->format(), + numChannels, sample->getIMemory(), 0, callback, userData); #else - newTrack = new AudioTrack(mSoundPool->streamType(), sampleRate, sample->format(), - numChannels, frameCount, 0, callback, this, bufferFrames); + newTrack = new AudioTrack(streamType, sampleRate, sample->format(), + numChannels, frameCount, 0, callback, userData, bufferFrames); #endif if (newTrack->initCheck() != NO_ERROR) { LOGE("Error creating AudioTrack"); @@ -529,6 +540,8 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV { Mutex::Autolock lock(&mLock); + // From now on, AudioTrack callbacks recevieved with previous toggle value will be ignored. + mToggle = toggle; oldTrack = mAudioTrack; mAudioTrack = newTrack; mPos = 0; @@ -583,7 +596,13 @@ void SoundChannel::nextEvent() void SoundChannel::callback(int event, void* user, void *info) { - SoundChannel* channel = static_cast<SoundChannel*>(user); + unsigned long toggle = (unsigned long)user & 1; + SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1)); + + if (channel->mToggle != toggle) { + LOGV("callback with wrong toggle"); + return; + } channel->process(event, info); } @@ -592,7 +611,7 @@ void SoundChannel::process(int event, void *info) //LOGV("process(%d)", mChannelID); sp<Sample> sample = mSample; - LOGV("SoundChannel::process event %d", event); +// LOGV("SoundChannel::process event %d", event); if (event == AudioTrack::EVENT_MORE_DATA) { AudioTrack::Buffer* b = static_cast<AudioTrack::Buffer *>(info); diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h index d02ae8b..7802781 100644 --- a/media/jni/soundpool/SoundPool.h +++ b/media/jni/soundpool/SoundPool.h @@ -118,7 +118,7 @@ protected: class SoundChannel : public SoundEvent { public: enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING }; - SoundChannel() : mAudioTrack(0), mState(IDLE), mNumChannels(1), mPos(0) {} + SoundChannel() : mAudioTrack(0), mState(IDLE), mNumChannels(1), mPos(0), mToggle(0) {} ~SoundChannel(); void init(SoundPool* soundPool); void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume, @@ -151,6 +151,7 @@ private: int mNumChannels; int mPos; int mAudioBufferSize; + unsigned long mToggle; }; // application object for managing a pool of sounds @@ -215,8 +216,6 @@ private: int mAllocated; int mNextSampleID; int mNextChannelID; - int mFrameCount; - int mSampleRate; bool mQuit; }; |