summaryrefslogtreecommitdiffstats
path: root/media/jni
diff options
context:
space:
mode:
Diffstat (limited to 'media/jni')
-rw-r--r--media/jni/android_media_MediaPlayer.cpp46
-rw-r--r--media/jni/android_media_MediaRecorder.cpp228
-rw-r--r--media/jni/soundpool/SoundPool.cpp51
-rw-r--r--media/jni/soundpool/SoundPool.h5
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;
};