M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

51
base/media/jni/Android.mk Normal file
View File

@ -0,0 +1,51 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
android_media_MediaPlayer.cpp \
android_media_MediaRecorder.cpp \
android_media_MediaScanner.cpp \
android_media_MediaMetadataRetriever.cpp \
android_media_ResampleInputStream.cpp \
android_media_MediaProfiles.cpp \
android_media_AmrInputStream.cpp
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libnativehelper \
libutils \
libbinder \
libmedia \
libskia \
libui \
libcutils \
libsurfaceflinger_client \
libstagefright \
libcamera_client \
libopencore_player
LOCAL_STATIC_LIBRARIES :=
LOCAL_C_INCLUDES += \
external/tremor/Tremor \
frameworks/base/core/jni \
frameworks/base/media/libmedia \
frameworks/base/media/libstagefright/codecs/amrnb/enc/src \
frameworks/base/media/libstagefright/codecs/amrnb/common \
frameworks/base/media/libstagefright/codecs/amrnb/common/include \
external/opencore/android \
$(PV_INCLUDES) \
$(JNI_H_INCLUDE) \
$(call include-path-for, corecg graphics)
LOCAL_CFLAGS +=
LOCAL_LDLIBS := -lpthread
LOCAL_MODULE:= libmedia_jni
include $(BUILD_SHARED_LIBRARY)
# build libsoundpool.so
# build libaudioeffect_jni.so
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

190
base/media/jni/NOTICE Normal file
View File

@ -0,0 +1,190 @@
Copyright (c) 2005-2008, The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,152 @@
/*
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#define LOG_TAG "AmrInputStream"
#include "utils/Log.h"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include "gsmamr_enc.h"
// ----------------------------------------------------------------------------
using namespace android;
// Corresponds to max bit rate of 12.2 kbps.
static const int MAX_OUTPUT_BUFFER_SIZE = 32;
static const int FRAME_DURATION_MS = 20;
static const int SAMPLING_RATE_HZ = 8000;
static const int SAMPLES_PER_FRAME = ((SAMPLING_RATE_HZ * FRAME_DURATION_MS) / 1000);
static const int BYTES_PER_SAMPLE = 2; // Assume 16-bit PCM samples
static const int BYTES_PER_FRAME = (SAMPLES_PER_FRAME * BYTES_PER_SAMPLE);
struct GsmAmrEncoderState {
GsmAmrEncoderState()
: mEncState(NULL),
mSidState(NULL),
mLastModeUsed(0) {
}
~GsmAmrEncoderState() {}
void* mEncState;
void* mSidState;
int32_t mLastModeUsed;
};
//
// helper function to throw an exception
//
static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) {
if (jclass cls = env->FindClass(ex)) {
char msg[128];
sprintf(msg, fmt, data);
env->ThrowNew(cls, msg);
env->DeleteLocalRef(cls);
}
}
static jint android_media_AmrInputStream_GsmAmrEncoderNew
(JNIEnv *env, jclass clazz) {
GsmAmrEncoderState* gae = new GsmAmrEncoderState();
if (gae == NULL) {
throwException(env, "java/lang/RuntimeException",
"Out of memory", 0);
}
return (jint)gae;
}
static void android_media_AmrInputStream_GsmAmrEncoderInitialize
(JNIEnv *env, jclass clazz, jint gae) {
GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
int32_t nResult = AMREncodeInit(&state->mEncState, &state->mSidState, false);
if (nResult != OK) {
throwException(env, "java/lang/IllegalArgumentException",
"GsmAmrEncoder initialization failed %d", nResult);
}
}
static jint android_media_AmrInputStream_GsmAmrEncoderEncode
(JNIEnv *env, jclass clazz,
jint gae, jbyteArray pcm, jint pcmOffset, jbyteArray amr, jint amrOffset) {
jbyte inBuf[BYTES_PER_FRAME];
jbyte outBuf[MAX_OUTPUT_BUFFER_SIZE];
env->GetByteArrayRegion(pcm, pcmOffset, sizeof(inBuf), inBuf);
GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
int32_t length = AMREncode(state->mEncState, state->mSidState,
(Mode) MR122,
(int16_t *) inBuf,
(unsigned char *) outBuf,
(Frame_Type_3GPP*) &state->mLastModeUsed,
AMR_TX_WMF);
if (length < 0) {
throwException(env, "java/io/IOException",
"Failed to encode a frame with error code: %d", length);
return -1;
}
// The 1st byte of PV AMR frames are WMF (Wireless Multimedia Forum)
// bitpacked, i.e.;
// [P(4) + FT(4)]. Q=1 for good frame, P=padding bit, 0
// Here we are converting the header to be as specified in Section 5.3 of
// RFC 3267 (AMR storage format) i.e.
// [P(1) + FT(4) + Q(1) + P(2)].
if (length > 0) {
outBuf[0] = (outBuf[0] << 3) | 0x4;
}
env->SetByteArrayRegion(amr, amrOffset, length, outBuf);
return length;
}
static void android_media_AmrInputStream_GsmAmrEncoderCleanup
(JNIEnv *env, jclass clazz, jint gae) {
GsmAmrEncoderState *state = (GsmAmrEncoderState *)gae;
AMREncodeExit(&state->mEncState, &state->mSidState);
state->mEncState = NULL;
state->mSidState = NULL;
}
static void android_media_AmrInputStream_GsmAmrEncoderDelete
(JNIEnv *env, jclass clazz, jint gae) {
delete (GsmAmrEncoderState*)gae;
}
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
{"GsmAmrEncoderNew", "()I", (void*)android_media_AmrInputStream_GsmAmrEncoderNew},
{"GsmAmrEncoderInitialize", "(I)V", (void*)android_media_AmrInputStream_GsmAmrEncoderInitialize},
{"GsmAmrEncoderEncode", "(I[BI[BI)I", (void*)android_media_AmrInputStream_GsmAmrEncoderEncode},
{"GsmAmrEncoderCleanup", "(I)V", (void*)android_media_AmrInputStream_GsmAmrEncoderCleanup},
{"GsmAmrEncoderDelete", "(I)V", (void*)android_media_AmrInputStream_GsmAmrEncoderDelete},
};
int register_android_media_AmrInputStream(JNIEnv *env)
{
const char* const kClassPathName = "android/media/AmrInputStream";
return AndroidRuntime::registerNativeMethods(env,
kClassPathName, gMethods, NELEM(gMethods));
}

View File

@ -0,0 +1,364 @@
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaMetadataRetrieverJNI"
#include <assert.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include <core/SkBitmap.h>
#include <media/mediametadataretriever.h>
#include <private/media/VideoFrame.h>
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
using namespace android;
struct fields_t {
jfieldID context;
jclass bitmapClazz;
jmethodID bitmapConstructor;
jmethodID createBitmapMethod;
};
static fields_t fields;
static Mutex sLock;
static const char* const kClassPathName = "android/media/MediaMetadataRetriever";
static void process_media_retriever_call(JNIEnv *env, status_t opStatus, const char* exception, const char *message)
{
if (opStatus == (status_t) INVALID_OPERATION) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
} else if (opStatus != (status_t) OK) {
if (strlen(message) > 230) {
// If the message is too long, don't bother displaying the status code.
jniThrowException( env, exception, message);
} else {
char msg[256];
// Append the status code to the message.
sprintf(msg, "%s: status = 0x%X", message, opStatus);
jniThrowException( env, exception, msg);
}
}
}
static MediaMetadataRetriever* getRetriever(JNIEnv* env, jobject thiz)
{
// No lock is needed, since it is called internally by other methods that are protected
MediaMetadataRetriever* retriever = (MediaMetadataRetriever*) env->GetIntField(thiz, fields.context);
return retriever;
}
static void setRetriever(JNIEnv* env, jobject thiz, int retriever)
{
// No lock is needed, since it is called internally by other methods that are protected
MediaMetadataRetriever *old = (MediaMetadataRetriever*) env->GetIntField(thiz, fields.context);
env->SetIntField(thiz, fields.context, retriever);
}
static void android_media_MediaMetadataRetriever_setDataSource(JNIEnv *env, jobject thiz, jstring path)
{
LOGV("setDataSource");
MediaMetadataRetriever* retriever = getRetriever(env, thiz);
if (retriever == 0) {
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
return;
}
if (!path) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Null pointer");
return;
}
const char *pathStr = env->GetStringUTFChars(path, NULL);
if (!pathStr) { // OutOfMemoryError exception already thrown
return;
}
// Don't let somebody trick us in to reading some random block of memory
if (strncmp("mem://", pathStr, 6) == 0) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid pathname");
return;
}
process_media_retriever_call(env, retriever->setDataSource(pathStr), "java/lang/RuntimeException", "setDataSource failed");
env->ReleaseStringUTFChars(path, pathStr);
}
static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
LOGV("setDataSource");
MediaMetadataRetriever* retriever = getRetriever(env, thiz);
if (retriever == 0) {
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
return;
}
if (!fileDescriptor) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
int fd = getParcelFileDescriptorFD(env, fileDescriptor);
if (offset < 0 || length < 0 || fd < 0) {
if (offset < 0) {
LOGE("negative offset (%lld)", offset);
}
if (length < 0) {
LOGE("negative length (%lld)", length);
}
if (fd < 0) {
LOGE("invalid file descriptor");
}
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
process_media_retriever_call(env, retriever->setDataSource(fd, offset, length), "java/lang/RuntimeException", "setDataSource failed");
}
static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, jobject thiz, jlong timeUs, jint option)
{
LOGV("getFrameAtTime: %lld us option: %d", timeUs, option);
MediaMetadataRetriever* retriever = getRetriever(env, thiz);
if (retriever == 0) {
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
return NULL;
}
// Call native method to retrieve a video frame
VideoFrame *videoFrame = NULL;
sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
if (frameMemory != 0) { // cast the shared structure to a VideoFrame object
videoFrame = static_cast<VideoFrame *>(frameMemory->pointer());
}
if (videoFrame == NULL) {
LOGE("getFrameAtTime: videoFrame is a NULL pointer");
return NULL;
}
jobject matrix = NULL;
if (videoFrame->mRotationAngle != 0) {
LOGD("Create a rotation matrix: %d degrees", videoFrame->mRotationAngle);
jclass matrixClazz = env->FindClass("android/graphics/Matrix");
if (matrixClazz == NULL) {
jniThrowException(env, "java/lang/RuntimeException",
"Can't find android/graphics/Matrix");
return NULL;
}
jmethodID matrixConstructor =
env->GetMethodID(matrixClazz, "<init>", "()V");
if (matrixConstructor == NULL) {
jniThrowException(env, "java/lang/RuntimeException",
"Can't find Matrix constructor");
return NULL;
}
matrix =
env->NewObject(matrixClazz, matrixConstructor);
if (matrix == NULL) {
LOGE("Could not create a Matrix object");
return NULL;
}
LOGV("Rotate the matrix: %d degrees", videoFrame->mRotationAngle);
jmethodID setRotateMethod =
env->GetMethodID(matrixClazz, "setRotate", "(F)V");
if (setRotateMethod == NULL) {
jniThrowException(env, "java/lang/RuntimeException",
"Can't find Matrix setRotate method");
return NULL;
}
env->CallVoidMethod(matrix, setRotateMethod, 1.0 * videoFrame->mRotationAngle);
env->DeleteLocalRef(matrixClazz);
}
// Create a SkBitmap to hold the pixels
SkBitmap *bitmap = new SkBitmap();
if (bitmap == NULL) {
LOGE("getFrameAtTime: cannot instantiate a SkBitmap object.");
return NULL;
}
bitmap->setConfig(SkBitmap::kRGB_565_Config, videoFrame->mDisplayWidth, videoFrame->mDisplayHeight);
if (!bitmap->allocPixels()) {
delete bitmap;
LOGE("failed to allocate pixel buffer");
return NULL;
}
memcpy((uint8_t*)bitmap->getPixels(), (uint8_t*)videoFrame + sizeof(VideoFrame), videoFrame->mSize);
// Since internally SkBitmap uses reference count to manage the reference to
// its pixels, it is important that the pixels (along with SkBitmap) be
// available after creating the Bitmap is returned to Java app.
jobject jSrcBitmap = env->NewObject(fields.bitmapClazz,
fields.bitmapConstructor, (int) bitmap, true, NULL, -1);
LOGV("Return a new bitmap constructed with the rotation matrix");
return env->CallStaticObjectMethod(
fields.bitmapClazz, fields.createBitmapMethod,
jSrcBitmap, // source Bitmap
0, // x
0, // y
videoFrame->mDisplayWidth, // width
videoFrame->mDisplayHeight, // height
matrix, // transform matrix
false); // filter
}
static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture(
JNIEnv *env, jobject thiz, jint pictureType)
{
LOGV("getEmbeddedPicture: %d", pictureType);
MediaMetadataRetriever* retriever = getRetriever(env, thiz);
if (retriever == 0) {
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
return NULL;
}
MediaAlbumArt* mediaAlbumArt = NULL;
// FIXME:
// Use pictureType to retrieve the intended embedded picture and also change
// the method name to getEmbeddedPicture().
sp<IMemory> albumArtMemory = retriever->extractAlbumArt();
if (albumArtMemory != 0) { // cast the shared structure to a MediaAlbumArt object
mediaAlbumArt = static_cast<MediaAlbumArt *>(albumArtMemory->pointer());
}
if (mediaAlbumArt == NULL) {
LOGE("getEmbeddedPicture: Call to getEmbeddedPicture failed.");
return NULL;
}
unsigned int len = mediaAlbumArt->mSize;
char* data = (char*) mediaAlbumArt + sizeof(MediaAlbumArt);
jbyteArray array = env->NewByteArray(len);
if (!array) { // OutOfMemoryError exception has already been thrown.
LOGE("getEmbeddedPicture: OutOfMemoryError is thrown.");
} else {
jbyte* bytes = env->GetByteArrayElements(array, NULL);
if (bytes != NULL) {
memcpy(bytes, data, len);
env->ReleaseByteArrayElements(array, bytes, 0);
}
}
// No need to delete mediaAlbumArt here
return array;
}
static jobject android_media_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jobject thiz, jint keyCode)
{
LOGV("extractMetadata");
MediaMetadataRetriever* retriever = getRetriever(env, thiz);
if (retriever == 0) {
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
return NULL;
}
const char* value = retriever->extractMetadata(keyCode);
if (!value) {
LOGV("extractMetadata: Metadata is not found");
return NULL;
}
LOGV("extractMetadata: value (%s) for keyCode(%d)", value, keyCode);
return env->NewStringUTF(value);
}
static void android_media_MediaMetadataRetriever_release(JNIEnv *env, jobject thiz)
{
LOGV("release");
Mutex::Autolock lock(sLock);
MediaMetadataRetriever* retriever = getRetriever(env, thiz);
delete retriever;
setRetriever(env, thiz, 0);
}
static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz)
{
LOGV("native_finalize");
// No lock is needed, since android_media_MediaMetadataRetriever_release() is protected
android_media_MediaMetadataRetriever_release(env, thiz);
}
// This function gets a field ID, which in turn causes class initialization.
// It is called from a static block in MediaMetadataRetriever, which won't run until the
// first time an instance of this class is used.
static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env)
{
jclass clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaMetadataRetriever");
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
if (fields.context == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaMetadataRetriever.mNativeContext");
return;
}
fields.bitmapClazz = env->FindClass("android/graphics/Bitmap");
if (fields.bitmapClazz == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find android/graphics/Bitmap");
return;
}
fields.bitmapConstructor = env->GetMethodID(fields.bitmapClazz, "<init>", "(IZ[BI)V");
if (fields.bitmapConstructor == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find Bitmap constructor");
return;
}
fields.createBitmapMethod =
env->GetStaticMethodID(fields.bitmapClazz, "createBitmap",
"(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)"
"Landroid/graphics/Bitmap;");
if (fields.createBitmapMethod == NULL) {
jniThrowException(env, "java/lang/RuntimeException",
"Can't find Bitmap.createBitmap method");
return;
}
}
static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
{
LOGV("native_setup");
MediaMetadataRetriever* retriever = new MediaMetadataRetriever();
if (retriever == 0) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
setRetriever(env, thiz, (int)retriever);
}
// JNI mapping between Java methods and native methods
static JNINativeMethod nativeMethods[] = {
{"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaMetadataRetriever_setDataSource},
{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
{"_getFrameAtTime", "(JI)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
{"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
{"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
{"release", "()V", (void *)android_media_MediaMetadataRetriever_release},
{"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize},
{"native_setup", "()V", (void *)android_media_MediaMetadataRetriever_native_setup},
{"native_init", "()V", (void *)android_media_MediaMetadataRetriever_native_init},
};
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaMetadataRetriever(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods
(env, kClassPathName, nativeMethods, NELEM(nativeMethods));
}

View File

@ -0,0 +1,868 @@
/* //device/libs/android_runtime/android_media_MediaPlayer.cpp
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer-JNI"
#include "utils/Log.h"
#include <media/mediaplayer.h>
#include <media/MediaPlayerInterface.h>
#include <stdio.h>
#include <assert.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <utils/threads.h>
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include "utils/Errors.h" // for status_t
#include "utils/KeyedVector.h"
#include "utils/String8.h"
#include "android_util_Binder.h"
#include <binder/Parcel.h>
#include <surfaceflinger/Surface.h>
// ----------------------------------------------------------------------------
using namespace android;
// ----------------------------------------------------------------------------
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 JNIMediaPlayerListener: public MediaPlayerListener
{
public:
JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
~JNIMediaPlayerListener();
void notify(int msg, int ext1, int ext2);
private:
JNIMediaPlayerListener();
jclass mClass; // Reference to MediaPlayer class
jobject mObject; // Weak ref to MediaPlayer Java object to call on
};
JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{
// Hold onto the MediaPlayer 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/MediaPlayer");
jniThrowException(env, "java/lang/Exception", NULL);
return;
}
mClass = (jclass)env->NewGlobalRef(clazz);
// We use a weak reference so the MediaPlayer object can be garbage collected.
// The reference is only used as a proxy for callbacks.
mObject = env->NewGlobalRef(weak_thiz);
}
JNIMediaPlayerListener::~JNIMediaPlayerListener()
{
// remove global references
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
}
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, 0);
}
// ----------------------------------------------------------------------------
static Surface* get_surface(JNIEnv* env, jobject clazz)
{
return (Surface*)env->GetIntField(clazz, fields.surface_native);
}
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
MediaPlayer* const p = (MediaPlayer*)env->GetIntField(thiz, fields.context);
return sp<MediaPlayer>(p);
}
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
Mutex::Autolock l(sLock);
sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
if (player.get()) {
player->incStrong(thiz);
}
if (old != 0) {
old->decStrong(thiz);
}
env->SetIntField(thiz, fields.context, (int)player.get());
return old;
}
// If exception is NULL and opStatus is not OK, this method sends an error
// event to the client application; otherwise, if exception is not NULL and
// opStatus is not OK, this method throws the given exception to the client
// application.
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) { // Don't throw exception. Instead, send an event.
if (opStatus != (status_t) OK) {
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
}
} else { // Throw exception!
if ( opStatus == (status_t) INVALID_OPERATION ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
} else if ( opStatus != (status_t) OK ) {
if (strlen(message) > 230) {
// if the message is too long, don't bother displaying the status code
jniThrowException( env, exception, message);
} else {
char msg[256];
// append the status code to the message
sprintf(msg, "%s: status=0x%X", message, opStatus);
jniThrowException( env, exception, msg);
}
}
}
}
static void
android_media_MediaPlayer_setDataSourceAndHeaders(
JNIEnv *env, jobject thiz, jstring path, jobject headers) {
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
if (path == 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;
}
// headers is a Map<String, String>.
// We build a similar KeyedVector out of it.
KeyedVector<String8, String8> headersVector;
if (headers) {
// Get the Map's entry Set.
jclass mapClass = env->FindClass("java/util/Map");
jmethodID entrySet =
env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
jobject set = env->CallObjectMethod(headers, entrySet);
// Obtain an iterator over the Set
jclass setClass = env->FindClass("java/util/Set");
jmethodID iterator =
env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
jobject iter = env->CallObjectMethod(set, iterator);
// Get the Iterator method IDs
jclass iteratorClass = env->FindClass("java/util/Iterator");
jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
jmethodID next =
env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
// Get the Entry class method IDs
jclass entryClass = env->FindClass("java/util/Map$Entry");
jmethodID getKey =
env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
jmethodID getValue =
env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
// Iterate over the entry Set
while (env->CallBooleanMethod(iter, hasNext)) {
jobject entry = env->CallObjectMethod(iter, next);
jstring key = (jstring) env->CallObjectMethod(entry, getKey);
jstring value = (jstring) env->CallObjectMethod(entry, getValue);
const char* keyStr = env->GetStringUTFChars(key, NULL);
if (!keyStr) { // Out of memory
jniThrowException(
env, "java/lang/RuntimeException", "Out of memory");
return;
}
const char* valueStr = env->GetStringUTFChars(value, NULL);
if (!valueStr) { // Out of memory
jniThrowException(
env, "java/lang/RuntimeException", "Out of memory");
return;
}
headersVector.add(String8(keyStr), String8(valueStr));
env->DeleteLocalRef(entry);
env->ReleaseStringUTFChars(key, keyStr);
env->DeleteLocalRef(key);
env->ReleaseStringUTFChars(value, valueStr);
env->DeleteLocalRef(value);
}
env->DeleteLocalRef(entryClass);
env->DeleteLocalRef(iteratorClass);
env->DeleteLocalRef(iter);
env->DeleteLocalRef(setClass);
env->DeleteLocalRef(set);
env->DeleteLocalRef(mapClass);
}
LOGV("setDataSource: path %s", pathStr);
status_t opStatus =
mp->setDataSource(
String8(pathStr),
headers ? &headersVector : NULL);
// Make sure that local ref is released before a potential exception
env->ReleaseStringUTFChars(path, pathStr);
process_media_player_call(
env, thiz, opStatus, "java/io/IOException",
"setDataSource failed." );
}
static void
android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path)
{
android_media_MediaPlayer_setDataSourceAndHeaders(env, thiz, path, 0);
}
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
if (fileDescriptor == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
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." );
}
static void setVideoSurface(const sp<MediaPlayer>& mp, JNIEnv *env, jobject thiz)
{
jobject surface = env->GetObjectField(thiz, fields.surface);
if (surface != NULL) {
const sp<Surface> native_surface = get_surface(env, surface);
LOGV("prepare: surface=%p (id=%d)",
native_surface.get(), native_surface->getIdentity());
mp->setVideoSurface(native_surface);
}
}
static void
android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
setVideoSurface(mp, env, thiz);
}
static void
android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
setVideoSurface(mp, env, thiz);
process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
}
static void
android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
jobject surface = env->GetObjectField(thiz, fields.surface);
if (surface != NULL) {
const sp<Surface> native_surface = get_surface(env, surface);
LOGV("prepareAsync: surface=%p (id=%d)",
native_surface.get(), native_surface->getIdentity());
mp->setVideoSurface(native_surface);
}
process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
}
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);
return;
}
process_media_player_call( env, thiz, mp->start(), NULL, NULL );
}
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);
return;
}
process_media_player_call( env, thiz, mp->stop(), NULL, NULL );
}
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);
return;
}
process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
}
static jboolean
android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return false;
}
const jboolean is_playing = mp->isPlaying();
LOGV("isPlaying: %d", is_playing);
return is_playing;
}
static void
android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
LOGV("seekTo: %d(msec)", msec);
process_media_player_call( env, thiz, mp->seekTo(msec), NULL, NULL );
}
static int
android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
int w;
if (0 != mp->getVideoWidth(&w)) {
LOGE("getVideoWidth failed");
w = 0;
}
LOGV("getVideoWidth: %d", w);
return w;
}
static int
android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
int h;
if (0 != mp->getVideoHeight(&h)) {
LOGE("getVideoHeight failed");
h = 0;
}
LOGV("getVideoHeight: %d", h);
return h;
}
static int
android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
int msec;
process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL );
LOGV("getCurrentPosition: %d (msec)", msec);
return msec;
}
static int
android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
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);
return;
}
process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
}
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);
return;
}
process_media_player_call( env, thiz, mp->setAudioStreamType(streamtype) , NULL, NULL );
}
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);
return;
}
process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL );
}
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);
return false;
}
return mp->isLooping();
}
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);
return;
}
process_media_player_call( env, thiz, mp->setVolume(leftVolume, rightVolume), NULL, NULL );
}
// FIXME: deprecated
static jobject
android_media_MediaPlayer_getFrameAt(JNIEnv *env, jobject thiz, jint msec)
{
return NULL;
}
// Sends the request and reply parcels to the media player via the
// binder interface.
static jint
android_media_MediaPlayer_invoke(JNIEnv *env, jobject thiz,
jobject java_request, jobject java_reply)
{
sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
if (media_player == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return UNKNOWN_ERROR;
}
Parcel *request = parcelForJavaObject(env, java_request);
Parcel *reply = parcelForJavaObject(env, java_reply);
// Don't use process_media_player_call which use the async loop to
// report errors, instead returns the status.
return media_player->invoke(*request, reply);
}
// Sends the new filter to the client.
static jint
android_media_MediaPlayer_setMetadataFilter(JNIEnv *env, jobject thiz, jobject request)
{
sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
if (media_player == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return UNKNOWN_ERROR;
}
Parcel *filter = parcelForJavaObject(env, request);
if (filter == NULL ) {
jniThrowException(env, "java/lang/RuntimeException", "Filter is null");
return UNKNOWN_ERROR;
}
return media_player->setMetadataFilter(*filter);
}
static jboolean
android_media_MediaPlayer_getMetadata(JNIEnv *env, jobject thiz, jboolean update_only,
jboolean apply_filter, jobject reply)
{
sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
if (media_player == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return false;
}
Parcel *metadata = parcelForJavaObject(env, reply);
if (metadata == NULL ) {
jniThrowException(env, "java/lang/RuntimeException", "Reply parcel is null");
return false;
}
metadata->freeData();
// On return metadata is positioned at the beginning of the
// metadata. Note however that the parcel actually starts with the
// return code so you should not rewind the parcel using
// setDataPosition(0).
return media_player->getMetadata(update_only, apply_filter, metadata) == OK;
}
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in MediaPlayer, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
if (clazz == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaPlayer");
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
if (fields.context == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mNativeContext");
return;
}
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.postEventFromNative");
return;
}
fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
if (fields.surface == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mSurface");
return;
}
jclass surface = env->FindClass("android/view/Surface");
if (surface == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find android/view/Surface");
return;
}
fields.surface_native = env->GetFieldID(surface, ANDROID_VIEW_SURFACE_JNI_ID, "I");
if (fields.surface_native == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find Surface.mSurface");
return;
}
}
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
LOGV("native_setup");
sp<MediaPlayer> mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// create new listener and give it to MediaPlayer
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);
// Stow our new C++ MediaPlayer in an opaque field in the Java object.
setMediaPlayer(env, thiz, mp);
}
static void
android_media_MediaPlayer_release(JNIEnv *env, jobject thiz)
{
LOGV("release");
sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0);
if (mp != NULL) {
// this prevents native callbacks after the object is released
mp->setListener(0);
mp->disconnect();
}
}
static void
android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz)
{
LOGV("native_finalize");
android_media_MediaPlayer_release(env, thiz);
}
static jint
android_media_MediaPlayer_native_suspend_resume(
JNIEnv *env, jobject thiz, jboolean isSuspend) {
LOGV("suspend_resume(%d)", isSuspend);
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return UNKNOWN_ERROR;
}
return isSuspend ? mp->suspend() : mp->resume();
}
static void android_media_MediaPlayer_set_audio_session_id(JNIEnv *env, jobject thiz, jint sessionId) {
LOGV("set_session_id(): %d", sessionId);
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->setAudioSessionId(sessionId), NULL, NULL );
}
static jint android_media_MediaPlayer_get_audio_session_id(JNIEnv *env, jobject thiz) {
LOGV("get_session_id()");
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return 0;
}
return mp->getAudioSessionId();
}
static void
android_media_MediaPlayer_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level)
{
LOGV("setAuxEffectSendLevel: level %f", level);
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->setAuxEffectSendLevel(level), NULL, NULL );
}
static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env, jobject thiz, jint effectId) {
LOGV("attachAuxEffect(): %d", effectId);
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
}
static void
android_media_MediaPlayer_setParameters(JNIEnv *env, jobject thiz, jstring params)
{
LOGV("setParameters(%s)", env->GetStringUTFChars(params, NULL));
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
const char *paramsStr = env->GetStringUTFChars(params, NULL);
if (paramsStr == NULL) { // Out of memory
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
process_media_player_call(env, thiz, mp->setParameters(String8(paramsStr)), NULL, NULL);
}
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
{"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource},
{"setDataSource", "(Ljava/lang/String;Ljava/util/Map;)V",(void *)android_media_MediaPlayer_setDataSourceAndHeaders},
{"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setVideoSurface", "()V", (void *)android_media_MediaPlayer_setVideoSurface},
{"prepare", "()V", (void *)android_media_MediaPlayer_prepare},
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
{"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},
{"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo},
{"_pause", "()V", (void *)android_media_MediaPlayer_pause},
{"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying},
{"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition},
{"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},
{"_release", "()V", (void *)android_media_MediaPlayer_release},
{"_reset", "()V", (void *)android_media_MediaPlayer_reset},
{"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping},
{"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
{"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
{"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
{"native_suspend_resume", "(Z)I", (void *)android_media_MediaPlayer_native_suspend_resume},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
{"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
{"setParameters", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setParameters}
};
static const char* const kClassPathName = "android/media/MediaPlayer";
// This function only registers the native methods
static int register_android_media_MediaPlayer(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
extern int register_android_media_MediaRecorder(JNIEnv *env);
extern int register_android_media_MediaScanner(JNIEnv *env);
extern int register_android_media_ResampleInputStream(JNIEnv *env);
extern int register_android_media_MediaProfiles(JNIEnv *env);
extern int register_android_media_AmrInputStream(JNIEnv *env);
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
if (register_android_media_MediaPlayer(env) < 0) {
LOGE("ERROR: MediaPlayer native registration failed\n");
goto bail;
}
if (register_android_media_MediaRecorder(env) < 0) {
LOGE("ERROR: MediaRecorder native registration failed\n");
goto bail;
}
if (register_android_media_MediaScanner(env) < 0) {
LOGE("ERROR: MediaScanner native registration failed\n");
goto bail;
}
if (register_android_media_MediaMetadataRetriever(env) < 0) {
LOGE("ERROR: MediaMetadataRetriever native registration failed\n");
goto bail;
}
if (register_android_media_AmrInputStream(env) < 0) {
LOGE("ERROR: AmrInputStream native registration failed\n");
goto bail;
}
if (register_android_media_ResampleInputStream(env) < 0) {
LOGE("ERROR: ResampleInputStream native registration failed\n");
goto bail;
}
if (register_android_media_MediaProfiles(env) < 0) {
LOGE("ERROR: MediaProfiles native registration failed");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}
// KTHXBYE

View File

@ -0,0 +1,340 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaProfilesJNI"
#include <utils/Log.h>
#include <stdio.h>
#include <utils/threads.h>
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <media/MediaProfiles.h>
using namespace android;
static Mutex sLock;
MediaProfiles *sProfiles = NULL;
// This function is called from a static block in MediaProfiles.java class,
// which won't run until the first time an instance of this class is used.
static void
android_media_MediaProfiles_native_init(JNIEnv *env)
{
LOGV("native_init");
Mutex::Autolock lock(sLock);
if (sProfiles == NULL) {
sProfiles = MediaProfiles::getInstance();
}
}
static jint
android_media_MediaProfiles_native_get_num_file_formats(JNIEnv *env, jobject thiz)
{
LOGV("native_get_num_file_formats");
return sProfiles->getOutputFileFormats().size();
}
static jint
android_media_MediaProfiles_native_get_file_format(JNIEnv *env, jobject thiz, jint index)
{
LOGV("native_get_file_format: %d", index);
Vector<output_format> formats = sProfiles->getOutputFileFormats();
int nSize = formats.size();
if (index < 0 || index >= nSize) {
jniThrowException(env, "java/lang/IllegalArgumentException", "out of array boundary");
return -1;
}
return static_cast<jint>(formats[index]);
}
static jint
android_media_MediaProfiles_native_get_num_video_encoders(JNIEnv *env, jobject thiz)
{
LOGV("native_get_num_video_encoders");
return sProfiles->getVideoEncoders().size();
}
static jobject
android_media_MediaProfiles_native_get_video_encoder_cap(JNIEnv *env, jobject thiz, jint index)
{
LOGV("native_get_video_encoder_cap: %d", index);
Vector<video_encoder> encoders = sProfiles->getVideoEncoders();
int nSize = encoders.size();
if (index < 0 || index >= nSize) {
jniThrowException(env, "java/lang/IllegalArgumentException", "out of array boundary");
return NULL;
}
video_encoder encoder = encoders[index];
int minBitRate = sProfiles->getVideoEncoderParamByName("enc.vid.bps.min", encoder);
int maxBitRate = sProfiles->getVideoEncoderParamByName("enc.vid.bps.max", encoder);
int minFrameRate = sProfiles->getVideoEncoderParamByName("enc.vid.fps.min", encoder);
int maxFrameRate = sProfiles->getVideoEncoderParamByName("enc.vid.fps.max", encoder);
int minFrameWidth = sProfiles->getVideoEncoderParamByName("enc.vid.width.min", encoder);
int maxFrameWidth = sProfiles->getVideoEncoderParamByName("enc.vid.width.max", encoder);
int minFrameHeight = sProfiles->getVideoEncoderParamByName("enc.vid.height.min", encoder);
int maxFrameHeight = sProfiles->getVideoEncoderParamByName("enc.vid.height.max", encoder);
// Check on the values retrieved
if ((minBitRate == -1 || maxBitRate == -1) ||
(minFrameRate == -1 || maxFrameRate == -1) ||
(minFrameWidth == -1 || maxFrameWidth == -1) ||
(minFrameHeight == -1 || maxFrameHeight == -1)) {
jniThrowException(env, "java/lang/RuntimeException", "Error retrieving video encoder capability params");
return NULL;
}
// Construct an instance of the VideoEncoderCap and set its member variables
jclass videoEncoderCapClazz = env->FindClass("android/media/EncoderCapabilities$VideoEncoderCap");
jmethodID videoEncoderCapConstructorMethodID = env->GetMethodID(videoEncoderCapClazz, "<init>", "(IIIIIIIII)V");
jobject cap = env->NewObject(videoEncoderCapClazz,
videoEncoderCapConstructorMethodID,
static_cast<int>(encoder),
minBitRate, maxBitRate,
minFrameRate, maxFrameRate,
minFrameWidth, maxFrameWidth,
minFrameHeight, maxFrameHeight);
return cap;
}
static jint
android_media_MediaProfiles_native_get_num_audio_encoders(JNIEnv *env, jobject thiz)
{
LOGV("native_get_num_audio_encoders");
return sProfiles->getAudioEncoders().size();
}
static jobject
android_media_MediaProfiles_native_get_audio_encoder_cap(JNIEnv *env, jobject thiz, jint index)
{
LOGV("native_get_audio_encoder_cap: %d", index);
Vector<audio_encoder> encoders = sProfiles->getAudioEncoders();
int nSize = encoders.size();
if (index < 0 || index >= nSize) {
jniThrowException(env, "java/lang/IllegalArgumentException", "out of array boundary");
return NULL;
}
audio_encoder encoder = encoders[index];
int minBitRate = sProfiles->getAudioEncoderParamByName("enc.aud.bps.min", encoder);
int maxBitRate = sProfiles->getAudioEncoderParamByName("enc.aud.bps.max", encoder);
int minSampleRate = sProfiles->getAudioEncoderParamByName("enc.aud.hz.min", encoder);
int maxSampleRate = sProfiles->getAudioEncoderParamByName("enc.aud.hz.max", encoder);
int minChannels = sProfiles->getAudioEncoderParamByName("enc.aud.ch.min", encoder);
int maxChannels = sProfiles->getAudioEncoderParamByName("enc.aud.ch.max", encoder);
// Check on the values retrieved
if ((minBitRate == -1 || maxBitRate == -1) ||
(minSampleRate == -1 || maxSampleRate == -1) ||
(minChannels == -1 || maxChannels == -1)) {
jniThrowException(env, "java/lang/RuntimeException", "Error retrieving video encoder capability params");
return NULL;
}
jclass audioEncoderCapClazz = env->FindClass("android/media/EncoderCapabilities$AudioEncoderCap");
jmethodID audioEncoderCapConstructorMethodID = env->GetMethodID(audioEncoderCapClazz, "<init>", "(IIIIIII)V");
jobject cap = env->NewObject(audioEncoderCapClazz,
audioEncoderCapConstructorMethodID,
static_cast<int>(encoder),
minBitRate, maxBitRate,
minSampleRate, maxSampleRate,
minChannels, maxChannels);
return cap;
}
static jobject
android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint id, jint quality)
{
LOGV("native_get_camcorder_profile: %d %d", id, quality);
if (quality != CAMCORDER_QUALITY_HIGH && quality != CAMCORDER_QUALITY_LOW) {
jniThrowException(env, "java/lang/RuntimeException", "Unknown camcorder profile quality");
return NULL;
}
camcorder_quality q = static_cast<camcorder_quality>(quality);
int duration = sProfiles->getCamcorderProfileParamByName("duration", id, q);
int fileFormat = sProfiles->getCamcorderProfileParamByName("file.format", id, q);
int videoCodec = sProfiles->getCamcorderProfileParamByName("vid.codec", id, q);
int videoBitRate = sProfiles->getCamcorderProfileParamByName("vid.bps", id, q);
int videoFrameRate = sProfiles->getCamcorderProfileParamByName("vid.fps", id, q);
int videoFrameWidth = sProfiles->getCamcorderProfileParamByName("vid.width", id, q);
int videoFrameHeight = sProfiles->getCamcorderProfileParamByName("vid.height", id, q);
int audioCodec = sProfiles->getCamcorderProfileParamByName("aud.codec", id, q);
int audioBitRate = sProfiles->getCamcorderProfileParamByName("aud.bps", id, q);
int audioSampleRate = sProfiles->getCamcorderProfileParamByName("aud.hz", id, q);
int audioChannels = sProfiles->getCamcorderProfileParamByName("aud.ch", id, q);
// Check on the values retrieved
if (duration == -1 || fileFormat == -1 || videoCodec == -1 || audioCodec == -1 ||
videoBitRate == -1 || videoFrameRate == -1 || videoFrameWidth == -1 || videoFrameHeight == -1 ||
audioBitRate == -1 || audioSampleRate == -1 || audioChannels == -1) {
jniThrowException(env, "java/lang/RuntimeException", "Error retrieving camcorder profile params");
return NULL;
}
jclass camcorderProfileClazz = env->FindClass("android/media/CamcorderProfile");
jmethodID camcorderProfileConstructorMethodID = env->GetMethodID(camcorderProfileClazz, "<init>", "(IIIIIIIIIIII)V");
return env->NewObject(camcorderProfileClazz,
camcorderProfileConstructorMethodID,
duration,
quality,
fileFormat,
videoCodec,
videoBitRate,
videoFrameRate,
videoFrameWidth,
videoFrameHeight,
audioCodec,
audioBitRate,
audioSampleRate,
audioChannels);
}
static jint
android_media_MediaProfiles_native_get_num_video_decoders(JNIEnv *env, jobject thiz)
{
LOGV("native_get_num_video_decoders");
return sProfiles->getVideoDecoders().size();
}
static jint
android_media_MediaProfiles_native_get_video_decoder_type(JNIEnv *env, jobject thiz, jint index)
{
LOGV("native_get_video_decoder_type: %d", index);
Vector<video_decoder> decoders = sProfiles->getVideoDecoders();
int nSize = decoders.size();
if (index < 0 || index >= nSize) {
jniThrowException(env, "java/lang/IllegalArgumentException", "out of array boundary");
return -1;
}
return static_cast<jint>(decoders[index]);
}
static jint
android_media_MediaProfiles_native_get_num_audio_decoders(JNIEnv *env, jobject thiz)
{
LOGV("native_get_num_audio_decoders");
return sProfiles->getAudioDecoders().size();
}
static jint
android_media_MediaProfiles_native_get_audio_decoder_type(JNIEnv *env, jobject thiz, jint index)
{
LOGV("native_get_audio_decoder_type: %d", index);
Vector<audio_decoder> decoders = sProfiles->getAudioDecoders();
int nSize = decoders.size();
if (index < 0 || index >= nSize) {
jniThrowException(env, "java/lang/IllegalArgumentException", "out of array boundary");
return -1;
}
return static_cast<jint>(decoders[index]);
}
static jint
android_media_MediaProfiles_native_get_num_image_encoding_quality_levels(JNIEnv *env, jobject thiz, jint cameraId)
{
LOGV("native_get_num_image_encoding_quality_levels");
return sProfiles->getImageEncodingQualityLevels(cameraId).size();
}
static jint
android_media_MediaProfiles_native_get_image_encoding_quality_level(JNIEnv *env, jobject thiz, jint cameraId, jint index)
{
LOGV("native_get_image_encoding_quality_level");
Vector<int> levels = sProfiles->getImageEncodingQualityLevels(cameraId);
if (index < 0 || index >= levels.size()) {
jniThrowException(env, "java/lang/IllegalArgumentException", "out of array boundary");
return -1;
}
return static_cast<jint>(levels[index]);
}
static JNINativeMethod gMethodsForEncoderCapabilitiesClass[] = {
{"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
{"native_get_num_file_formats", "()I", (void *)android_media_MediaProfiles_native_get_num_file_formats},
{"native_get_file_format", "(I)I", (void *)android_media_MediaProfiles_native_get_file_format},
{"native_get_num_video_encoders", "()I", (void *)android_media_MediaProfiles_native_get_num_video_encoders},
{"native_get_num_audio_encoders", "()I", (void *)android_media_MediaProfiles_native_get_num_audio_encoders},
{"native_get_video_encoder_cap", "(I)Landroid/media/EncoderCapabilities$VideoEncoderCap;",
(void *)android_media_MediaProfiles_native_get_video_encoder_cap},
{"native_get_audio_encoder_cap", "(I)Landroid/media/EncoderCapabilities$AudioEncoderCap;",
(void *)android_media_MediaProfiles_native_get_audio_encoder_cap},
};
static JNINativeMethod gMethodsForCamcorderProfileClass[] = {
{"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
{"native_get_camcorder_profile", "(II)Landroid/media/CamcorderProfile;",
(void *)android_media_MediaProfiles_native_get_camcorder_profile},
};
static JNINativeMethod gMethodsForDecoderCapabilitiesClass[] = {
{"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
{"native_get_num_video_decoders", "()I", (void *)android_media_MediaProfiles_native_get_num_video_decoders},
{"native_get_num_audio_decoders", "()I", (void *)android_media_MediaProfiles_native_get_num_audio_decoders},
{"native_get_video_decoder_type", "(I)I", (void *)android_media_MediaProfiles_native_get_video_decoder_type},
{"native_get_audio_decoder_type", "(I)I", (void *)android_media_MediaProfiles_native_get_audio_decoder_type},
};
static JNINativeMethod gMethodsForCameraProfileClass[] = {
{"native_init", "()V", (void *)android_media_MediaProfiles_native_init},
{"native_get_num_image_encoding_quality_levels",
"(I)I", (void *)android_media_MediaProfiles_native_get_num_image_encoding_quality_levels},
{"native_get_image_encoding_quality_level","(II)I", (void *)android_media_MediaProfiles_native_get_image_encoding_quality_level},
};
static const char* const kEncoderCapabilitiesClassPathName = "android/media/EncoderCapabilities";
static const char* const kDecoderCapabilitiesClassPathName = "android/media/DecoderCapabilities";
static const char* const kCamcorderProfileClassPathName = "android/media/CamcorderProfile";
static const char* const kCameraProfileClassPathName = "android/media/CameraProfile";
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaProfiles(JNIEnv *env)
{
int ret1 = AndroidRuntime::registerNativeMethods(env,
kEncoderCapabilitiesClassPathName,
gMethodsForEncoderCapabilitiesClass,
NELEM(gMethodsForEncoderCapabilitiesClass));
int ret2 = AndroidRuntime::registerNativeMethods(env,
kCamcorderProfileClassPathName,
gMethodsForCamcorderProfileClass,
NELEM(gMethodsForCamcorderProfileClass));
int ret3 = AndroidRuntime::registerNativeMethods(env,
kDecoderCapabilitiesClassPathName,
gMethodsForDecoderCapabilitiesClass,
NELEM(gMethodsForDecoderCapabilitiesClass));
int ret4 = AndroidRuntime::registerNativeMethods(env,
kCameraProfileClassPathName,
gMethodsForCameraProfileClass,
NELEM(gMethodsForCameraProfileClass));
// Success if all return values from above are 0
return (ret1 || ret2 || ret3 || ret4);
}

View File

@ -0,0 +1,602 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaRecorderJNI"
#include <utils/Log.h>
#include <surfaceflinger/SurfaceComposerClient.h>
#include <camera/ICameraService.h>
#include <camera/Camera.h>
#include <media/mediarecorder.h>
#include <stdio.h>
#include <assert.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <utils/threads.h>
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
// ----------------------------------------------------------------------------
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, struct JNICameraContext** 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);
void postData(int32_t msgType, const sp<IMemory>& dataPtr);
private:
JNIMediaRecorderListener();
void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int32_t msgType);
jclass mClass; // Reference to MediaRecorder class
jobject mObject; // Weak ref to MediaRecorder Java object to call on
Mutex mLock;
};
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);
}
void JNIMediaRecorderListener::postData(int32_t msgType, const sp<IMemory>& dataPtr)
{
LOGV("JNIMediaRecorderListener::postData");
Mutex::Autolock _l(mLock);
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (mObject == NULL) {
LOGW("callback on dead MediaRecorder object");
return;
}
// return data based on callback type
switch(msgType) {
case CAMERA_MSG_VIDEO_FRAME:
// should never happen
break;
// don't return raw data to Java
case CAMERA_MSG_RAW_IMAGE:
LOGV("rawCallback");
//just notify java layer without any data.
env->CallStaticVoidMethod(mClass, fields.post_event,
mObject, msgType, 0, 0, NULL);
break;
case MEDIA_RECORDER_MSG_COMPRESSED_IMAGE:
LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
copyAndPost(env, dataPtr, msgType);
break;
default:
LOGV("Unhandled Message: %d",msgType);
break;
}
}
void JNIMediaRecorderListener::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int32_t msgType)
{
jbyteArray obj = NULL;
// allocate Java byte array and copy data
if (dataPtr != NULL) {
ssize_t offset;
size_t size;
sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
LOGV("postData: off=%d, size=%d", offset, size);
uint8_t *heapBase = (uint8_t*)heap->base();
if (heapBase != NULL) {
const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
LOGV("Allocating callback buffer");
obj = env->NewByteArray(size);
if (obj == NULL) {
LOGE("Couldn't allocate byte array for JPEG data");
env->ExceptionClear();
} else {
env->SetByteArrayRegion(obj, 0, size, data);
}
} else {
LOGE("image heap is NULL");
}
}
// post image data to Java
env->CallStaticVoidMethod(mClass, fields.post_event,
mObject, msgType, 0, 0, obj);
if (obj) {
env->DeleteLocalRef(obj);
}
}
// ----------------------------------------------------------------------------
static sp<Surface> get_surface(JNIEnv* env, jobject clazz)
{
LOGV("get_surface");
Surface* const p = (Surface*)env->GetIntField(clazz, fields.surface_native);
return sp<Surface>(p);
}
// 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);
}
env->SetIntField(thiz, fields.context, (int)recorder.get());
return old;
}
static void android_media_MediaRecorder_setCamera(JNIEnv* env, jobject thiz, jobject camera)
{
// we should not pass a null camera to get_native_camera() call.
if (camera == NULL) {
jniThrowException(env, "java/lang/NullPointerException", "camera object is a NULL pointer");
return;
}
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.");
}
static void
android_media_MediaRecorder_setVideoSource(JNIEnv *env, jobject thiz, jint vs)
{
LOGV("setVideoSource(%d)", vs);
if (vs < VIDEO_SOURCE_DEFAULT || vs >= VIDEO_SOURCE_LIST_END) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid video source");
return;
}
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
process_media_recorder_call(env, mr->setVideoSource(vs), "java/lang/RuntimeException", "setVideoSource failed.");
}
static void
android_media_MediaRecorder_setAudioSource(JNIEnv *env, jobject thiz, jint as)
{
LOGV("setAudioSource(%d)", as);
if (as < AUDIO_SOURCE_DEFAULT || as >= AUDIO_SOURCE_LIST_END) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio source");
return;
}
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
process_media_recorder_call(env, mr->setAudioSource(as), "java/lang/RuntimeException", "setAudioSource failed.");
}
static void
android_media_MediaRecorder_setOutputFormat(JNIEnv *env, jobject thiz, jint of)
{
LOGV("setOutputFormat(%d)", of);
if (of < OUTPUT_FORMAT_DEFAULT || of >= OUTPUT_FORMAT_LIST_END) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid output format");
return;
}
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
process_media_recorder_call(env, mr->setOutputFormat(of), "java/lang/RuntimeException", "setOutputFormat failed.");
}
static void
android_media_MediaRecorder_setVideoEncoder(JNIEnv *env, jobject thiz, jint ve)
{
LOGV("setVideoEncoder(%d)", ve);
if (ve < VIDEO_ENCODER_DEFAULT || ve >= VIDEO_ENCODER_LIST_END) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid video encoder");
return;
}
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
process_media_recorder_call(env, mr->setVideoEncoder(ve), "java/lang/RuntimeException", "setVideoEncoder failed.");
}
static void
android_media_MediaRecorder_setAudioEncoder(JNIEnv *env, jobject thiz, jint ae)
{
LOGV("setAudioEncoder(%d)", ae);
if (ae < AUDIO_ENCODER_DEFAULT || ae >= AUDIO_ENCODER_LIST_END) {
jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid audio encoder");
return;
}
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
process_media_recorder_call(env, mr->setAudioEncoder(ae), "java/lang/RuntimeException", "setAudioEncoder failed.");
}
static void
android_media_MediaRecorder_setParameter(JNIEnv *env, jobject thiz, jstring params)
{
LOGV("setParameter()");
if (params == NULL)
{
LOGE("Invalid or empty params string. This parameter will be ignored.");
return;
}
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
const char* params8 = env->GetStringUTFChars(params, NULL);
if (params8 == NULL)
{
LOGE("Failed to covert jstring to String8. This parameter will be ignored.");
return;
}
process_media_recorder_call(env, mr->setParameters(String8(params8)), "java/lang/RuntimeException", "setParameter failed.");
env->ReleaseStringUTFChars(params,params8);
}
static void
android_media_MediaRecorder_setCameraParameters(JNIEnv *env, jobject thiz, jstring params)
{
LOGV("setCameraParameters()");
if (params == NULL)
{
LOGE("Invalid or empty params string. This parameter will be ignored.");
return;
}
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
const char* params8 = env->GetStringUTFChars(params, NULL);
if (params8 == NULL)
{
LOGE("Failed to covert jstring to String8. This parameter will be ignored.");
return;
}
process_media_recorder_call(env, mr->setCameraParameters(String8(params8)), "java/lang/RuntimeException", "setCameraParameters failed.");
env->ReleaseStringUTFChars(params,params8);
}
static void
android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
LOGV("setOutputFile");
if (fileDescriptor == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
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);
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
if (width <= 0 || height <= 0) {
jniThrowException(env, "java/lang/IllegalArgumentException", "invalid video size");
return;
}
process_media_recorder_call(env, mr->setVideoSize(width, height), "java/lang/RuntimeException", "setVideoSize failed.");
}
static void
android_media_MediaRecorder_setVideoFrameRate(JNIEnv *env, jobject thiz, jint rate)
{
LOGV("setVideoFrameRate(%d)", rate);
if (rate <= 0) {
jniThrowException(env, "java/lang/IllegalArgumentException", "invalid frame rate");
return;
}
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_setMaxFileSize(
JNIEnv *env, jobject thiz, jlong max_filesize_bytes)
{
LOGV("setMaxFileSize(%lld)", max_filesize_bytes);
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
char params[64];
sprintf(params, "max-filesize=%lld", max_filesize_bytes);
process_media_recorder_call(env, mr->setParameters(String8(params)), "java/lang/RuntimeException", "setMaxFileSize failed.");
}
static void
android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz)
{
LOGV("prepare");
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);
// The application may misbehave and
// the preview surface becomes unavailable
if (native_surface.get() == 0) {
LOGE("Application lost the surface");
jniThrowException(env, "java/io/IOException", "invalid preview surface");
return;
}
LOGI("prepare: surface=%p (identity=%d)", native_surface.get(), native_surface->getIdentity());
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.");
}
static int
android_media_MediaRecorder_native_getMaxAmplitude(JNIEnv *env, jobject thiz)
{
LOGV("getMaxAmplitude");
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
int result = 0;
process_media_recorder_call(env, mr->getMaxAmplitude(&result), "java/lang/RuntimeException", "getMaxAmplitude failed.");
return result;
}
static void
android_media_MediaRecorder_start(JNIEnv *env, jobject thiz)
{
LOGV("start");
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
process_media_recorder_call(env, mr->start(), "java/lang/RuntimeException", "start failed.");
}
static bool
android_media_MediaRecorder_takeLiveSnapshot(JNIEnv *env, jobject thiz)
{
LOGV("takeLiveSnapshot");
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
if(process_media_recorder_call(env, mr->takeLiveSnapshot(), "java/lang/RuntimeException", "takeLiveSnapshot failed."))
return false;
return true;
}
static void
android_media_MediaRecorder_stop(JNIEnv *env, jobject thiz)
{
LOGV("stop");
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
process_media_recorder_call(env, mr->stop(), "java/lang/RuntimeException", "stop failed.");
}
static void
android_media_MediaRecorder_native_reset(JNIEnv *env, jobject thiz)
{
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");
sp<MediaRecorder> mr = setMediaRecorder(env, thiz, 0);
if (mr != NULL) {
mr->setListener(NULL);
mr->release();
}
}
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in MediaRecorder, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaRecorder_native_init(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaRecorder");
if (clazz == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaRecorder");
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
if (fields.context == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaRecorder.mNativeContext");
return;
}
fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
if (fields.surface == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaRecorder.mSurface");
return;
}
jclass surface = env->FindClass("android/view/Surface");
if (surface == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find android/view/Surface");
return;
}
fields.surface_native = env->GetFieldID(surface, ANDROID_VIEW_SURFACE_JNI_ID, "I");
if (fields.surface_native == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find Surface.mSurface");
return;
}
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "MediaRecorder.postEventFromNative");
return;
}
}
static void
android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
LOGV("setup");
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/RuntimeException", "Unable to initialize media recorder");
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");
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},
{"setParameter", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameter},
{"setCameraParameters", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setCameraParameters},
{"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD},
{"native_setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize},
{"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate},
{"setMaxDuration", "(I)V", (void *)android_media_MediaRecorder_setMaxDuration},
{"setMaxFileSize", "(J)V", (void *)android_media_MediaRecorder_setMaxFileSize},
{"_prepare", "()V", (void *)android_media_MediaRecorder_prepare},
{"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude},
{"native_start", "()V", (void *)android_media_MediaRecorder_start},
{"native_stop", "()V", (void *)android_media_MediaRecorder_stop},
{"native_takeLiveSnapshot", "()Z", (void *)android_media_MediaRecorder_takeLiveSnapshot},
{"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset},
{"release", "()V", (void *)android_media_MediaRecorder_release},
{"native_init", "()V", (void *)android_media_MediaRecorder_native_init},
{"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";
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaRecorder(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaRecorder", gMethods, NELEM(gMethods));
}

View File

@ -0,0 +1,345 @@
/* //device/libs/media_jni/MediaScanner.cpp
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#define LOG_TAG "MediaScanner"
#include "utils/Log.h"
#include <media/mediascanner.h>
#include <stdio.h>
#include <assert.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <cutils/properties.h>
#include <utils/threads.h>
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <media/stagefright/StagefrightMediaScanner.h>
#include <pvmediascanner.h>
// ----------------------------------------------------------------------------
using namespace android;
// ----------------------------------------------------------------------------
struct fields_t {
jfieldID context;
};
static fields_t fields;
// ----------------------------------------------------------------------------
class MyMediaScannerClient : public MediaScannerClient
{
public:
MyMediaScannerClient(JNIEnv *env, jobject client)
: mEnv(env),
mClient(env->NewGlobalRef(client)),
mScanFileMethodID(0),
mHandleStringTagMethodID(0),
mSetMimeTypeMethodID(0)
{
jclass mediaScannerClientInterface = env->FindClass("android/media/MediaScannerClient");
if (mediaScannerClientInterface == NULL) {
fprintf(stderr, "android/media/MediaScannerClient not found\n");
}
else {
mScanFileMethodID = env->GetMethodID(mediaScannerClientInterface, "scanFile",
"(Ljava/lang/String;JJ)V");
mHandleStringTagMethodID = env->GetMethodID(mediaScannerClientInterface, "handleStringTag",
"(Ljava/lang/String;Ljava/lang/String;)V");
mSetMimeTypeMethodID = env->GetMethodID(mediaScannerClientInterface, "setMimeType",
"(Ljava/lang/String;)V");
mAddNoMediaFolderMethodID = env->GetMethodID(mediaScannerClientInterface, "addNoMediaFolder",
"(Ljava/lang/String;)V");
}
}
virtual ~MyMediaScannerClient()
{
mEnv->DeleteGlobalRef(mClient);
}
// returns true if it succeeded, false if an exception occured in the Java code
virtual bool scanFile(const char* path, long long lastModified, long long fileSize)
{
jstring pathStr;
if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false;
mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize);
mEnv->DeleteLocalRef(pathStr);
return (!mEnv->ExceptionCheck());
}
// returns true if it succeeded, false if an exception occured in the Java code
virtual bool handleStringTag(const char* name, const char* value)
{
jstring nameStr, valueStr;
if ((nameStr = mEnv->NewStringUTF(name)) == NULL) return false;
if ((valueStr = mEnv->NewStringUTF(value)) == NULL) return false;
mEnv->CallVoidMethod(mClient, mHandleStringTagMethodID, nameStr, valueStr);
mEnv->DeleteLocalRef(nameStr);
mEnv->DeleteLocalRef(valueStr);
return (!mEnv->ExceptionCheck());
}
// returns true if it succeeded, false if an exception occured in the Java code
virtual bool setMimeType(const char* mimeType)
{
jstring mimeTypeStr;
if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) return false;
mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
mEnv->DeleteLocalRef(mimeTypeStr);
return (!mEnv->ExceptionCheck());
}
// returns true if it succeeded, false if an exception occured in the Java code
virtual bool addNoMediaFolder(const char* path)
{
jstring pathStr;
if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false;
mEnv->CallVoidMethod(mClient, mAddNoMediaFolderMethodID, pathStr);
mEnv->DeleteLocalRef(pathStr);
return (!mEnv->ExceptionCheck());
}
private:
JNIEnv *mEnv;
jobject mClient;
jmethodID mScanFileMethodID;
jmethodID mHandleStringTagMethodID;
jmethodID mSetMimeTypeMethodID;
jmethodID mAddNoMediaFolderMethodID;
};
// ----------------------------------------------------------------------------
static bool ExceptionCheck(void* env)
{
return ((JNIEnv *)env)->ExceptionCheck();
}
static void
android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client)
{
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
if (path == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
if (extensions == 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;
}
const char *extensionsStr = env->GetStringUTFChars(extensions, NULL);
if (extensionsStr == NULL) { // Out of memory
env->ReleaseStringUTFChars(path, pathStr);
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
MyMediaScannerClient myClient(env, client);
mp->processDirectory(pathStr, extensionsStr, myClient, ExceptionCheck, env);
env->ReleaseStringUTFChars(path, pathStr);
env->ReleaseStringUTFChars(extensions, extensionsStr);
}
static void
android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client)
{
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
if (path == 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;
}
const char *mimeTypeStr = (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
if (mimeType && mimeTypeStr == NULL) { // Out of memory
env->ReleaseStringUTFChars(path, pathStr);
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
MyMediaScannerClient myClient(env, client);
mp->processFile(pathStr, mimeTypeStr, myClient);
env->ReleaseStringUTFChars(path, pathStr);
if (mimeType) {
env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
}
}
static void
android_media_MediaScanner_setLocale(JNIEnv *env, jobject thiz, jstring locale)
{
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
if (locale == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
const char *localeStr = env->GetStringUTFChars(locale, NULL);
if (localeStr == NULL) { // Out of memory
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
mp->setLocale(localeStr);
env->ReleaseStringUTFChars(locale, localeStr);
}
static jbyteArray
android_media_MediaScanner_extractAlbumArt(JNIEnv *env, jobject thiz, jobject fileDescriptor)
{
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
if (fileDescriptor == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return NULL;
}
int fd = getParcelFileDescriptorFD(env, fileDescriptor);
char* data = mp->extractAlbumArt(fd);
if (!data) {
return NULL;
}
long len = *((long*)data);
jbyteArray array = env->NewByteArray(len);
if (array != NULL) {
jbyte* bytes = env->GetByteArrayElements(array, NULL);
memcpy(bytes, data + 4, len);
env->ReleaseByteArrayElements(array, bytes, 0);
}
done:
free(data);
// if NewByteArray() returned NULL, an out-of-memory
// exception will have been raised. I just want to
// return null in that case.
env->ExceptionClear();
return array;
}
// This function gets a field ID, which in turn causes class initialization.
// It is called from a static block in MediaScanner, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaScanner_native_init(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaScanner");
if (clazz == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaScanner");
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
if (fields.context == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaScanner.mNativeContext");
return;
}
}
static MediaScanner *createMediaScanner() {
char value[PROPERTY_VALUE_MAX];
if (property_get("media.stagefright.enable-scan", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
return new StagefrightMediaScanner;
}
else
return new PVMediaScanner();
}
static void
android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
{
MediaScanner *mp = createMediaScanner();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
env->SetIntField(thiz, fields.context, (int)mp);
}
static void
android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
{
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
//printf("##### android_media_MediaScanner_native_finalize: ctx=0x%p\n", ctx);
if (mp == 0)
return;
delete mp;
}
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
{"processDirectory", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
(void *)android_media_MediaScanner_processDirectory},
{"processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
(void *)android_media_MediaScanner_processFile},
{"setLocale", "(Ljava/lang/String;)V", (void *)android_media_MediaScanner_setLocale},
{"extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt},
{"native_init", "()V", (void *)android_media_MediaScanner_native_init},
{"native_setup", "()V", (void *)android_media_MediaScanner_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaScanner_native_finalize},
};
static const char* const kClassPathName = "android/media/MediaScanner";
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaScanner(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaScanner", gMethods, NELEM(gMethods));
}

View File

@ -0,0 +1,136 @@
/*
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#define LOG_TAG "ResampleInputStream"
#include "utils/Log.h"
#include <media/mediarecorder.h>
#include <stdio.h>
#include <assert.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <utils/threads.h>
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
// ----------------------------------------------------------------------------
using namespace android;
//
// helper function to throw an exception
//
static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) {
if (jclass cls = env->FindClass(ex)) {
char msg[1000];
sprintf(msg, fmt, data);
env->ThrowNew(cls, msg);
env->DeleteLocalRef(cls);
}
}
#define FIR_COEF(coef) (short)(0x10000 * coef)
static const short fir21[] = {
FIR_COEF(-0.006965742326),
FIR_COEF(-0.008428945737),
FIR_COEF( 0.004241280174),
FIR_COEF( 0.022141096893),
FIR_COEF( 0.018765669437),
FIR_COEF(-0.009871891152),
FIR_COEF(-0.024842433247),
FIR_COEF( 0.006121772058),
FIR_COEF( 0.045890841611),
FIR_COEF( 0.021573503509),
FIR_COEF(-0.059681984668),
FIR_COEF(-0.076036275138),
FIR_COEF( 0.072405390275),
FIR_COEF( 0.308255674582),
FIR_COEF( 0.424321210495),
FIR_COEF( 0.308255674582),
FIR_COEF( 0.072405390275),
FIR_COEF(-0.076036275138),
FIR_COEF(-0.059681984668),
FIR_COEF( 0.021573503509),
FIR_COEF( 0.045890841611),
FIR_COEF( 0.006121772058),
FIR_COEF(-0.024842433247),
FIR_COEF(-0.009871891152),
FIR_COEF( 0.018765669437),
FIR_COEF( 0.022141096893),
FIR_COEF( 0.004241280174),
FIR_COEF(-0.008428945737),
FIR_COEF(-0.006965742326)
};
static const int nFir21 = sizeof(fir21) / sizeof(fir21[0]);
static const int BUF_SIZE = 2048;
static void android_media_ResampleInputStream_fir21(JNIEnv *env, jclass clazz,
jbyteArray jIn, jint jInOffset,
jbyteArray jOut, jint jOutOffset,
jint jNpoints) {
// safety first!
if (nFir21 + jNpoints * 2 > BUF_SIZE) {
throwException(env, "java/lang/IllegalArgumentException",
"FIR+data too long %d", nFir21 + jNpoints);
return;
}
// get input data
short in[BUF_SIZE];
env->GetByteArrayRegion(jIn, jInOffset, (jNpoints * 2 + nFir21 - 1) * 2, (jbyte*)in);
// compute filter
short out[BUF_SIZE];
for (int i = 0; i < jNpoints; i++) {
long sum = 0;
const short* firp = &fir21[0];
const short* inp = &in[i * 2];
for (int n = nFir21; --n >= 0; ) {
sum += ((long)*firp++) * ((long)*inp++);
}
out[i] = (short)(sum >> 16);
}
// save new values
env->SetByteArrayRegion(jOut, jOutOffset, jNpoints * 2, (jbyte*)out);
}
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
{"fir21", "([BI[BII)V", (void*)android_media_ResampleInputStream_fir21},
};
int register_android_media_ResampleInputStream(JNIEnv *env)
{
const char* const kClassPathName = "android/media/ResampleInputStream";
return AndroidRuntime::registerNativeMethods(env,
kClassPathName, gMethods, NELEM(gMethods));
}

View File

@ -0,0 +1,17 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
android_media_AudioEffect.cpp \
android_media_Visualizer.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libandroid_runtime \
libnativehelper \
libmedia
LOCAL_MODULE:= libaudioeffect_jni
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,849 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioEffects-JNI"
#include <utils/Log.h>
#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include "media/AudioEffect.h"
using namespace android;
#define AUDIOEFFECT_SUCCESS 0
#define AUDIOEFFECT_ERROR -1
#define AUDIOEFFECT_ERROR_ALREADY_EXISTS -2
#define AUDIOEFFECT_ERROR_NO_INIT -3
#define AUDIOEFFECT_ERROR_BAD_VALUE -4
#define AUDIOEFFECT_ERROR_INVALID_OPERATION -5
#define AUDIOEFFECT_ERROR_NO_MEMORY -6
#define AUDIOEFFECT_ERROR_DEAD_OBJECT -7
// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/audiofx/AudioEffect";
struct fields_t {
// these fields provide access from C++ to the...
jclass clazzEffect; // AudioEffect class
jmethodID midPostNativeEvent; // event post callback method
jfieldID fidNativeAudioEffect; // stores in Java the native AudioEffect object
jfieldID fidJniData; // stores in Java additional resources used by the native AudioEffect
jclass clazzDesc; // AudioEffect.Descriptor class
jmethodID midDescCstor; // AudioEffect.Descriptor class constructor
};
static fields_t fields;
struct effect_callback_cookie {
jclass audioEffect_class; // AudioEffect class
jobject audioEffect_ref; // AudioEffect object instance
};
// ----------------------------------------------------------------------------
class AudioEffectJniStorage {
public:
effect_callback_cookie mCallbackData;
AudioEffectJniStorage() {
}
~AudioEffectJniStorage() {
}
};
static jint translateError(int code) {
switch(code) {
case NO_ERROR:
return AUDIOEFFECT_SUCCESS;
case ALREADY_EXISTS:
return AUDIOEFFECT_ERROR_ALREADY_EXISTS;
case NO_INIT:
return AUDIOEFFECT_ERROR_NO_INIT;
case BAD_VALUE:
return AUDIOEFFECT_ERROR_BAD_VALUE;
case INVALID_OPERATION:
return AUDIOEFFECT_ERROR_INVALID_OPERATION;
case NO_MEMORY:
return AUDIOEFFECT_ERROR_NO_MEMORY;
case DEAD_OBJECT:
return AUDIOEFFECT_ERROR_DEAD_OBJECT;
default:
return AUDIOEFFECT_ERROR;
}
}
// ----------------------------------------------------------------------------
static void effectCallback(int event, void* user, void *info) {
effect_param_t *p;
int arg1 = 0;
int arg2 = 0;
jobject obj = NULL;
jbyteArray array = NULL;
jbyte *bytes;
bool param;
size_t size;
effect_callback_cookie *callbackInfo = (effect_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
LOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
callbackInfo,
callbackInfo->audioEffect_ref,
callbackInfo->audioEffect_class);
if (!user || !env) {
LOGW("effectCallback error user %p, env %p", user, env);
return;
}
switch (event) {
case AudioEffect::EVENT_CONTROL_STATUS_CHANGED:
if (info == 0) {
LOGW("EVENT_CONTROL_STATUS_CHANGED info == NULL");
goto effectCallback_Exit;
}
param = *(bool *)info;
arg1 = (int)param;
LOGV("EVENT_CONTROL_STATUS_CHANGED");
break;
case AudioEffect::EVENT_ENABLE_STATUS_CHANGED:
if (info == 0) {
LOGW("EVENT_ENABLE_STATUS_CHANGED info == NULL");
goto effectCallback_Exit;
}
param = *(bool *)info;
arg1 = (int)param;
LOGV("EVENT_ENABLE_STATUS_CHANGED");
break;
case AudioEffect::EVENT_PARAMETER_CHANGED:
if (info == 0) {
LOGW("EVENT_PARAMETER_CHANGED info == NULL");
goto effectCallback_Exit;
}
p = (effect_param_t *)info;
if (p->psize == 0 || p->vsize == 0) {
goto effectCallback_Exit;
}
// arg1 contains offset of parameter value from start of byte array
arg1 = sizeof(effect_param_t) + ((p->psize - 1) / sizeof(int) + 1) * sizeof(int);
size = arg1 + p->vsize;
array = env->NewByteArray(size);
if (array == NULL) {
LOGE("effectCallback: Couldn't allocate byte array for parameter data");
goto effectCallback_Exit;
}
bytes = env->GetByteArrayElements(array, NULL);
memcpy(bytes, p, size);
env->ReleaseByteArrayElements(array, bytes, 0);
obj = array;
LOGV("EVENT_PARAMETER_CHANGED");
break;
case AudioEffect::EVENT_ERROR:
LOGW("EVENT_ERROR");
break;
}
env->CallStaticVoidMethod(
callbackInfo->audioEffect_class,
fields.midPostNativeEvent,
callbackInfo->audioEffect_ref, event, arg1, arg2, obj);
effectCallback_Exit:
if (array) {
env->DeleteLocalRef(array);
}
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
// ----------------------------------------------------------------------------
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in AudioEffect, which won't run until the
// first time an instance of this class is used.
static void
android_media_AudioEffect_native_init(JNIEnv *env)
{
LOGV("android_media_AudioEffect_native_init");
fields.clazzEffect = NULL;
fields.clazzDesc = NULL;
// Get the AudioEffect class
jclass clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
LOGE("Can't find %s", kClassPathName);
return;
}
fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
// Get the postEvent method
fields.midPostNativeEvent = env->GetStaticMethodID(
fields.clazzEffect,
"postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.midPostNativeEvent == NULL) {
LOGE("Can't find AudioEffect.%s", "postEventFromNative");
return;
}
// Get the variables fields
// nativeTrackInJavaObj
fields.fidNativeAudioEffect = env->GetFieldID(
fields.clazzEffect,
"mNativeAudioEffect", "I");
if (fields.fidNativeAudioEffect == NULL) {
LOGE("Can't find AudioEffect.%s", "mNativeAudioEffect");
return;
}
// fidJniData;
fields.fidJniData = env->GetFieldID(
fields.clazzEffect,
"mJniData", "I");
if (fields.fidJniData == NULL) {
LOGE("Can't find AudioEffect.%s", "mJniData");
return;
}
clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
if (clazz == NULL) {
LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
return;
}
fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);
fields.midDescCstor
= env->GetMethodID(
fields.clazzDesc,
"<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
if (fields.midDescCstor == NULL) {
LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
return;
}
}
static jint
android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId, jobjectArray javadesc)
{
LOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
AudioEffect* lpAudioEffect = NULL;
jint* nId = NULL;
const char *typeStr = NULL;
const char *uuidStr = NULL;
effect_descriptor_t desc;
jobject jdesc;
char str[EFFECT_STRING_LEN_MAX];
jstring jdescType;
jstring jdescUuid;
jstring jdescConnect;
jstring jdescName;
jstring jdescImplementor;
if (type != NULL) {
typeStr = env->GetStringUTFChars(type, NULL);
if (typeStr == NULL) { // Out of memory
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
goto setup_failure;
}
}
if (uuid != NULL) {
uuidStr = env->GetStringUTFChars(uuid, NULL);
if (uuidStr == NULL) { // Out of memory
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
goto setup_failure;
}
}
if (typeStr == NULL && uuidStr == NULL) {
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
lpJniStorage = new AudioEffectJniStorage();
if (lpJniStorage == NULL) {
LOGE("setup: Error creating JNI Storage");
goto setup_failure;
}
lpJniStorage->mCallbackData.audioEffect_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
// we use a weak reference so the AudioEffect object can be garbage collected.
lpJniStorage->mCallbackData.audioEffect_ref = env->NewGlobalRef(weak_this);
LOGV("setup: lpJniStorage: %p audioEffect_ref %p audioEffect_class %p, &mCallbackData %p",
lpJniStorage,
lpJniStorage->mCallbackData.audioEffect_ref,
lpJniStorage->mCallbackData.audioEffect_class,
&lpJniStorage->mCallbackData);
if (jId == NULL) {
LOGE("setup: NULL java array for id pointer");
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
// create the native AudioEffect object
lpAudioEffect = new AudioEffect(typeStr,
uuidStr,
priority,
effectCallback,
&lpJniStorage->mCallbackData,
sessionId,
0);
if (lpAudioEffect == NULL) {
LOGE("Error creating AudioEffect");
goto setup_failure;
}
lStatus = translateError(lpAudioEffect->initCheck());
if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
LOGE("AudioEffect initCheck failed %d", lStatus);
goto setup_failure;
}
nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
if (nId == NULL) {
LOGE("setup: Error retrieving id pointer");
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
goto setup_failure;
}
nId[0] = lpAudioEffect->id();
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
nId = NULL;
if (typeStr) {
env->ReleaseStringUTFChars(type, typeStr);
typeStr = NULL;
}
if (uuidStr) {
env->ReleaseStringUTFChars(uuid, uuidStr);
uuidStr = NULL;
}
// get the effect descriptor
desc = lpAudioEffect->descriptor();
AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
jdescType = env->NewStringUTF(str);
AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
jdescUuid = env->NewStringUTF(str);
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
jdescConnect = env->NewStringUTF("Auxiliary");
} else {
jdescConnect = env->NewStringUTF("Insert");
}
jdescName = env->NewStringUTF(desc.name);
jdescImplementor = env->NewStringUTF(desc.implementor);
jdesc = env->NewObject(fields.clazzDesc,
fields.midDescCstor,
jdescType,
jdescUuid,
jdescConnect,
jdescName,
jdescImplementor);
env->DeleteLocalRef(jdescType);
env->DeleteLocalRef(jdescUuid);
env->DeleteLocalRef(jdescConnect);
env->DeleteLocalRef(jdescName);
env->DeleteLocalRef(jdescImplementor);
if (jdesc == NULL) {
LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
goto setup_failure;
}
env->SetObjectArrayElement(javadesc, 0, jdesc);
env->SetIntField(thiz, fields.fidNativeAudioEffect, (int)lpAudioEffect);
env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
return AUDIOEFFECT_SUCCESS;
// failures:
setup_failure:
if (nId != NULL) {
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
}
if (lpAudioEffect) {
delete lpAudioEffect;
}
env->SetIntField(thiz, fields.fidNativeAudioEffect, 0);
if (lpJniStorage) {
delete lpJniStorage;
}
env->SetIntField(thiz, fields.fidJniData, 0);
if (uuidStr != NULL) {
env->ReleaseStringUTFChars(uuid, uuidStr);
}
if (typeStr != NULL) {
env->ReleaseStringUTFChars(type, typeStr);
}
return lStatus;
}
// ----------------------------------------------------------------------------
static void android_media_AudioEffect_native_finalize(JNIEnv *env, jobject thiz) {
LOGV("android_media_AudioEffect_native_finalize jobject: %x\n", (int)thiz);
// delete the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
thiz, fields.fidNativeAudioEffect);
if (lpAudioEffect) {
LOGV("deleting AudioEffect: %x\n", (int)lpAudioEffect);
delete lpAudioEffect;
}
// delete the JNI data
AudioEffectJniStorage* lpJniStorage = (AudioEffectJniStorage *)env->GetIntField(
thiz, fields.fidJniData);
if (lpJniStorage) {
LOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
delete lpJniStorage;
}
}
// ----------------------------------------------------------------------------
static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
// do everything a call to finalize would
android_media_AudioEffect_native_finalize(env, thiz);
// + reset the native resources in the Java object so any attempt to access
// them after a call to release fails.
env->SetIntField(thiz, fields.fidNativeAudioEffect, 0);
env->SetIntField(thiz, fields.fidJniData, 0);
}
static jint
android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
thiz, fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for enable()");
return AUDIOEFFECT_ERROR_NO_INIT;
}
return translateError(lpAudioEffect->setEnabled(enabled));
}
static jboolean
android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
thiz, fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for getEnabled()");
return false;
}
return (jboolean)lpAudioEffect->getEnabled();
}
static jboolean
android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
thiz, fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for hasControl()");
return false;
}
if (lpAudioEffect->initCheck() == NO_ERROR) {
return true;
} else {
return false;
}
}
static jint android_media_AudioEffect_native_setParameter(JNIEnv *env,
jobject thiz, int psize, jbyteArray pJavaParam, int vsize,
jbyteArray pJavaValue) {
// retrieve the AudioEffect object
jbyte* lpValue = NULL;
jbyte* lpParam = NULL;
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
effect_param_t *p;
int voffset;
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for setParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
}
if (psize == 0 || vsize == 0 || pJavaParam == NULL || pJavaValue == NULL) {
return AUDIOEFFECT_ERROR_BAD_VALUE;
}
// get the pointer for the param from the java array
lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
if (lpParam == NULL) {
LOGE("setParameter: Error retrieving param pointer");
goto setParameter_Exit;
}
// get the pointer for the value from the java array
lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
if (lpValue == NULL) {
LOGE("setParameter: Error retrieving value pointer");
goto setParameter_Exit;
}
voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize);
memcpy(p->data, lpParam, psize);
p->psize = psize;
memcpy(p->data + voffset, lpValue, vsize);
p->vsize = vsize;
lStatus = lpAudioEffect->setParameter(p);
if (lStatus == NO_ERROR) {
lStatus = p->status;
}
free(p);
setParameter_Exit:
if (lpParam != NULL) {
env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
}
if (lpValue != NULL) {
env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
}
return translateError(lStatus);
}
static jint
android_media_AudioEffect_native_getParameter(JNIEnv *env,
jobject thiz, int psize, jbyteArray pJavaParam,
jintArray pJavaValueSize, jbyteArray pJavaValue) {
// retrieve the AudioEffect object
jbyte* lpParam = NULL;
jbyte* lpValue = NULL;
jbyte* lpValueSize = NULL;
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
effect_param_t *p;
int voffset;
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for getParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
}
if (psize == 0 || pJavaValueSize == NULL || pJavaParam == NULL || pJavaValue == NULL) {
return AUDIOEFFECT_ERROR_BAD_VALUE;
}
// get the pointer for the param from the java array
lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
if (lpParam == NULL) {
LOGE("getParameter: Error retrieving param pointer");
goto getParameter_Exit;
}
// get the pointer for the value from the java array
lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
if (lpValue == NULL) {
LOGE("getParameter: Error retrieving value pointer");
goto getParameter_Exit;
}
// get the pointer for the value size from the java array
lpValueSize = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValueSize, NULL);
if (lpValueSize == NULL) {
LOGE("getParameter: Error retrieving value size pointer");
goto getParameter_Exit;
}
voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset
+ lpValueSize[0]);
memcpy(p->data, lpParam, psize);
p->psize = psize;
p->vsize = lpValueSize[0];
lStatus = lpAudioEffect->getParameter(p);
if (lStatus == NO_ERROR) {
lStatus = p->status;
if (lStatus == NO_ERROR) {
memcpy(lpValue, p->data + voffset, p->vsize);
lpValueSize[0] = p->vsize;
}
}
free(p);
getParameter_Exit:
if (lpParam != NULL) {
env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
}
if (lpValue != NULL) {
env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
}
if (lpValueSize != NULL) {
env->ReleasePrimitiveArrayCritical(pJavaValueSize, lpValueSize, 0);
}
return translateError(lStatus);
}
static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
jint cmdCode, jint cmdSize, jbyteArray jCmdData, jintArray jReplySize,
jbyteArray jReplyData) {
jbyte* pCmdData = NULL;
jbyte* pReplyData = NULL;
jint* pReplySize = NULL;
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
fields.fidNativeAudioEffect);
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve AudioEffect pointer for setParameter()");
return AUDIOEFFECT_ERROR_NO_INIT;
}
if ((cmdSize != 0 && jCmdData == NULL) || (jReplySize != NULL && jReplyData == NULL)) {
return AUDIOEFFECT_ERROR_BAD_VALUE;
}
// get the pointer for the command from the java array
if (cmdSize != 0) {
pCmdData = (jbyte *) env->GetPrimitiveArrayCritical(jCmdData, NULL);
if (pCmdData == NULL) {
LOGE("setParameter: Error retrieving command pointer");
goto command_Exit;
}
}
// get the pointer for the reply size from the java array
if (jReplySize != NULL) {
pReplySize = (jint *) env->GetPrimitiveArrayCritical(jReplySize, NULL);
if (pReplySize == NULL) {
LOGE("setParameter: Error retrieving reply pointer");
goto command_Exit;
}
}
// get the pointer for the reply from the java array
if (pReplySize != NULL && pReplySize[0] != 0 && jReplyData != NULL) {
pReplyData = (jbyte *) env->GetPrimitiveArrayCritical(jReplyData, NULL);
if (pReplyData == NULL) {
LOGE("setParameter: Error retrieving reply pointer");
goto command_Exit;
}
}
lStatus = translateError(lpAudioEffect->command((uint32_t)cmdCode,
(uint32_t)cmdSize,
pCmdData,
(uint32_t *)pReplySize,
pReplyData));
command_Exit:
if (pCmdData != NULL) {
env->ReleasePrimitiveArrayCritical(jCmdData, pCmdData, 0);
}
if (pReplyData != NULL) {
env->ReleasePrimitiveArrayCritical(jReplyData, pReplyData, 0);
}
if (pReplySize != NULL) {
env->ReleasePrimitiveArrayCritical(jReplySize, pReplySize, 0);
}
return lStatus;
}
static jobjectArray
android_media_AudioEffect_native_queryEffects(JNIEnv *env, jclass clazz)
{
effect_descriptor_t desc;
char str[EFFECT_STRING_LEN_MAX];
uint32_t numEffects;
uint32_t i = 0;
jstring jdescType;
jstring jdescUuid;
jstring jdescConnect;
jstring jdescName;
jstring jdescImplementor;
jobject jdesc;
AudioEffect::queryNumberEffects(&numEffects);
jobjectArray ret = env->NewObjectArray(numEffects, fields.clazzDesc, NULL);
if (ret == NULL) {
return ret;
}
LOGV("queryEffects() numEffects: %d", numEffects);
for (i = 0; i < numEffects; i++) {
if (AudioEffect::queryEffect(i, &desc) != NO_ERROR) {
goto queryEffects_failure;
}
AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
jdescType = env->NewStringUTF(str);
AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
jdescUuid = env->NewStringUTF(str);
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
jdescConnect = env->NewStringUTF("Auxiliary");
} else {
jdescConnect = env->NewStringUTF("Insert");
}
jdescName = env->NewStringUTF(desc.name);
jdescImplementor = env->NewStringUTF(desc.implementor);
jdesc = env->NewObject(fields.clazzDesc,
fields.midDescCstor,
jdescType,
jdescUuid,
jdescConnect,
jdescName,
jdescImplementor);
env->DeleteLocalRef(jdescType);
env->DeleteLocalRef(jdescUuid);
env->DeleteLocalRef(jdescConnect);
env->DeleteLocalRef(jdescName);
env->DeleteLocalRef(jdescImplementor);
if (jdesc == NULL) {
LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
goto queryEffects_failure;
}
env->SetObjectArrayElement(ret, i, jdesc);
}
return ret;
queryEffects_failure:
if (ret != NULL) {
env->DeleteLocalRef(ret);
}
return NULL;
}
// ----------------------------------------------------------------------------
// Dalvik VM type signatures
static JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_AudioEffect_native_init},
{"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;)I",
(void *)android_media_AudioEffect_native_setup},
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
{"native_setEnabled", "(Z)I", (void *)android_media_AudioEffect_native_setEnabled},
{"native_getEnabled", "()Z", (void *)android_media_AudioEffect_native_getEnabled},
{"native_hasControl", "()Z", (void *)android_media_AudioEffect_native_hasControl},
{"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter},
{"native_getParameter", "(I[B[I[B)I", (void *)android_media_AudioEffect_native_getParameter},
{"native_command", "(II[B[I[B)I", (void *)android_media_AudioEffect_native_command},
{"native_query_effects", "()[Ljava/lang/Object;", (void *)android_media_AudioEffect_native_queryEffects},
};
// ----------------------------------------------------------------------------
extern int register_android_media_visualizer(JNIEnv *env);
int register_android_media_AudioEffect(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
if (register_android_media_AudioEffect(env) < 0) {
LOGE("ERROR: AudioEffect native registration failed\n");
goto bail;
}
if (register_android_media_visualizer(env) < 0) {
LOGE("ERROR: Visualizer native registration failed\n");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}

View File

@ -0,0 +1,505 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "visualizers-JNI"
#include <utils/Log.h>
#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include "media/Visualizer.h"
using namespace android;
#define VISUALIZER_SUCCESS 0
#define VISUALIZER_ERROR -1
#define VISUALIZER_ERROR_ALREADY_EXISTS -2
#define VISUALIZER_ERROR_NO_INIT -3
#define VISUALIZER_ERROR_BAD_VALUE -4
#define VISUALIZER_ERROR_INVALID_OPERATION -5
#define VISUALIZER_ERROR_NO_MEMORY -6
#define VISUALIZER_ERROR_DEAD_OBJECT -7
#define NATIVE_EVENT_PCM_CAPTURE 0
#define NATIVE_EVENT_FFT_CAPTURE 1
// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/audiofx/Visualizer";
struct fields_t {
// these fields provide access from C++ to the...
jclass clazzEffect; // Visualizer class
jmethodID midPostNativeEvent; // event post callback method
jfieldID fidNativeVisualizer; // stores in Java the native Visualizer object
jfieldID fidJniData; // stores in Java additional resources used by the native Visualizer
};
static fields_t fields;
struct visualizer_callback_cookie {
jclass visualizer_class; // Visualizer class
jobject visualizer_ref; // Visualizer object instance
};
// ----------------------------------------------------------------------------
class visualizerJniStorage {
public:
visualizer_callback_cookie mCallbackData;
visualizerJniStorage() {
}
~visualizerJniStorage() {
}
};
static jint translateError(int code) {
switch(code) {
case NO_ERROR:
return VISUALIZER_SUCCESS;
case ALREADY_EXISTS:
return VISUALIZER_ERROR_ALREADY_EXISTS;
case NO_INIT:
return VISUALIZER_ERROR_NO_INIT;
case BAD_VALUE:
return VISUALIZER_ERROR_BAD_VALUE;
case INVALID_OPERATION:
return VISUALIZER_ERROR_INVALID_OPERATION;
case NO_MEMORY:
return VISUALIZER_ERROR_NO_MEMORY;
case DEAD_OBJECT:
return VISUALIZER_ERROR_DEAD_OBJECT;
default:
return VISUALIZER_ERROR;
}
}
// ----------------------------------------------------------------------------
static void captureCallback(void* user,
uint32_t waveformSize,
uint8_t *waveform,
uint32_t fftSize,
uint8_t *fft,
uint32_t samplingrate) {
int arg1 = 0;
int arg2 = 0;
size_t size;
visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
LOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
callbackInfo,
callbackInfo->visualizer_ref,
callbackInfo->visualizer_class);
if (!user || !env) {
LOGW("captureCallback error user %p, env %p", user, env);
return;
}
if (waveformSize != 0 && waveform != NULL) {
jbyteArray jArray = env->NewByteArray(waveformSize);
if (jArray != NULL) {
jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
memcpy(nArray, waveform, waveformSize);
env->ReleaseByteArrayElements(jArray, nArray, 0);
env->CallStaticVoidMethod(
callbackInfo->visualizer_class,
fields.midPostNativeEvent,
callbackInfo->visualizer_ref,
NATIVE_EVENT_PCM_CAPTURE,
samplingrate,
0,
jArray);
env->DeleteLocalRef(jArray);
}
}
if (fftSize != 0 && fft != NULL) {
jbyteArray jArray = env->NewByteArray(fftSize);
if (jArray != NULL) {
jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
memcpy(nArray, fft, fftSize);
env->ReleaseByteArrayElements(jArray, nArray, 0);
env->CallStaticVoidMethod(
callbackInfo->visualizer_class,
fields.midPostNativeEvent,
callbackInfo->visualizer_ref,
NATIVE_EVENT_FFT_CAPTURE,
samplingrate,
0,
jArray);
env->DeleteLocalRef(jArray);
}
}
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
static Visualizer *getVisualizer(JNIEnv* env, jobject thiz)
{
Visualizer *v = (Visualizer *)env->GetIntField(
thiz, fields.fidNativeVisualizer);
if (v == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Unable to retrieve Visualizer pointer");
}
return v;
}
// ----------------------------------------------------------------------------
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in Visualizer, which won't run until the
// first time an instance of this class is used.
static void
android_media_visualizer_native_init(JNIEnv *env)
{
LOGV("android_media_visualizer_native_init");
fields.clazzEffect = NULL;
// Get the Visualizer class
jclass clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
LOGE("Can't find %s", kClassPathName);
return;
}
fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
// Get the postEvent method
fields.midPostNativeEvent = env->GetStaticMethodID(
fields.clazzEffect,
"postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.midPostNativeEvent == NULL) {
LOGE("Can't find Visualizer.%s", "postEventFromNative");
return;
}
// Get the variables fields
// nativeTrackInJavaObj
fields.fidNativeVisualizer = env->GetFieldID(
fields.clazzEffect,
"mNativeVisualizer", "I");
if (fields.fidNativeVisualizer == NULL) {
LOGE("Can't find Visualizer.%s", "mNativeVisualizer");
return;
}
// fidJniData;
fields.fidJniData = env->GetFieldID(
fields.clazzEffect,
"mJniData", "I");
if (fields.fidJniData == NULL) {
LOGE("Can't find Visualizer.%s", "mJniData");
return;
}
}
static jint
android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jint sessionId, jintArray jId)
{
LOGV("android_media_visualizer_native_setup");
visualizerJniStorage* lpJniStorage = NULL;
int lStatus = VISUALIZER_ERROR_NO_MEMORY;
Visualizer* lpVisualizer = NULL;
jint* nId = NULL;
lpJniStorage = new visualizerJniStorage();
if (lpJniStorage == NULL) {
LOGE("setup: Error creating JNI Storage");
goto setup_failure;
}
lpJniStorage->mCallbackData.visualizer_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
// we use a weak reference so the Visualizer object can be garbage collected.
lpJniStorage->mCallbackData.visualizer_ref = env->NewGlobalRef(weak_this);
LOGV("setup: lpJniStorage: %p visualizer_ref %p visualizer_class %p, &mCallbackData %p",
lpJniStorage,
lpJniStorage->mCallbackData.visualizer_ref,
lpJniStorage->mCallbackData.visualizer_class,
&lpJniStorage->mCallbackData);
if (jId == NULL) {
LOGE("setup: NULL java array for id pointer");
lStatus = VISUALIZER_ERROR_BAD_VALUE;
goto setup_failure;
}
// create the native Visualizer object
lpVisualizer = new Visualizer(0,
NULL,
NULL,
sessionId);
if (lpVisualizer == NULL) {
LOGE("Error creating Visualizer");
goto setup_failure;
}
lStatus = translateError(lpVisualizer->initCheck());
if (lStatus != VISUALIZER_SUCCESS && lStatus != VISUALIZER_ERROR_ALREADY_EXISTS) {
LOGE("Visualizer initCheck failed %d", lStatus);
goto setup_failure;
}
nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
if (nId == NULL) {
LOGE("setup: Error retrieving id pointer");
lStatus = VISUALIZER_ERROR_BAD_VALUE;
goto setup_failure;
}
nId[0] = lpVisualizer->id();
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
nId = NULL;
env->SetIntField(thiz, fields.fidNativeVisualizer, (int)lpVisualizer);
env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
return VISUALIZER_SUCCESS;
// failures:
setup_failure:
if (nId != NULL) {
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
}
if (lpVisualizer) {
delete lpVisualizer;
}
env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
if (lpJniStorage) {
delete lpJniStorage;
}
env->SetIntField(thiz, fields.fidJniData, 0);
return lStatus;
}
// ----------------------------------------------------------------------------
static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
LOGV("android_media_visualizer_native_finalize jobject: %x\n", (int)thiz);
// delete the Visualizer object
Visualizer* lpVisualizer = (Visualizer *)env->GetIntField(
thiz, fields.fidNativeVisualizer);
if (lpVisualizer) {
LOGV("deleting Visualizer: %x\n", (int)lpVisualizer);
delete lpVisualizer;
}
// delete the JNI data
visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(
thiz, fields.fidJniData);
if (lpJniStorage) {
LOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
delete lpJniStorage;
}
}
// ----------------------------------------------------------------------------
static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
// do everything a call to finalize would
android_media_visualizer_native_finalize(env, thiz);
// + reset the native resources in the Java object so any attempt to access
// them after a call to release fails.
env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
env->SetIntField(thiz, fields.fidJniData, 0);
}
static jint
android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
Visualizer* lpVisualizer = getVisualizer(env, thiz);
if (lpVisualizer == NULL) {
return VISUALIZER_ERROR_NO_INIT;
}
return translateError(lpVisualizer->setEnabled(enabled));
}
static jboolean
android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
{
Visualizer* lpVisualizer = getVisualizer(env, thiz);
if (lpVisualizer == NULL) {
return false;
}
return (jboolean)lpVisualizer->getEnabled();
}
static jintArray
android_media_visualizer_native_getCaptureSizeRange(JNIEnv *env, jobject thiz)
{
jintArray jRange = env->NewIntArray(2);
jint *nRange = env->GetIntArrayElements(jRange, NULL);
nRange[0] = Visualizer::getMinCaptureSize();
nRange[1] = Visualizer::getMaxCaptureSize();
LOGV("getCaptureSizeRange() min %d max %d", nRange[0], nRange[1]);
env->ReleaseIntArrayElements(jRange, nRange, 0);
return jRange;
}
static jint
android_media_visualizer_native_getMaxCaptureRate(JNIEnv *env, jobject thiz)
{
return Visualizer::getMaxCaptureRate();
}
static jint
android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
{
Visualizer* lpVisualizer = getVisualizer(env, thiz);
if (lpVisualizer == NULL) {
return VISUALIZER_ERROR_NO_INIT;
}
return translateError(lpVisualizer->setCaptureSize(size));
}
static jint
android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
{
Visualizer* lpVisualizer = getVisualizer(env, thiz);
if (lpVisualizer == NULL) {
return -1;
}
return lpVisualizer->getCaptureSize();
}
static jint
android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
{
Visualizer* lpVisualizer = getVisualizer(env, thiz);
if (lpVisualizer == NULL) {
return -1;
}
return lpVisualizer->getSamplingRate();
}
static jint
android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
{
Visualizer* lpVisualizer = getVisualizer(env, thiz);
if (lpVisualizer == NULL) {
return VISUALIZER_ERROR_NO_INIT;
}
jbyte* nWaveform = (jbyte *) env->GetPrimitiveArrayCritical(jWaveform, NULL);
if (nWaveform == NULL) {
return VISUALIZER_ERROR_NO_MEMORY;
}
jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform));
env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0);
return status;
}
static jint
android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
{
Visualizer* lpVisualizer = getVisualizer(env, thiz);
if (lpVisualizer == NULL) {
return VISUALIZER_ERROR_NO_INIT;
}
jbyte* nFft = (jbyte *) env->GetPrimitiveArrayCritical(jFft, NULL);
if (nFft == NULL) {
return VISUALIZER_ERROR_NO_MEMORY;
}
jint status = translateError(lpVisualizer->getFft((uint8_t *)nFft));
env->ReleasePrimitiveArrayCritical(jFft, nFft, 0);
return status;
}
static jint
android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
{
Visualizer* lpVisualizer = getVisualizer(env, thiz);
if (lpVisualizer == NULL) {
return VISUALIZER_ERROR_NO_INIT;
}
visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(thiz,
fields.fidJniData);
if (lpJniStorage == NULL) {
return VISUALIZER_ERROR_NO_INIT;
}
LOGV("setPeriodicCapture: rate %d, jWaveform %d jFft %d",
rate,
jWaveform,
jFft);
uint32_t flags = Visualizer::CAPTURE_CALL_JAVA;
if (jWaveform) flags |= Visualizer::CAPTURE_WAVEFORM;
if (jFft) flags |= Visualizer::CAPTURE_FFT;
Visualizer::capture_cbk_t cbk = captureCallback;
if (!jWaveform && !jFft) cbk = NULL;
return translateError(lpVisualizer->setCaptureCallBack(cbk,
&lpJniStorage->mCallbackData,
flags,
rate));
}
// ----------------------------------------------------------------------------
// Dalvik VM type signatures
static JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_visualizer_native_init},
{"native_setup", "(Ljava/lang/Object;I[I)I",
(void *)android_media_visualizer_native_setup},
{"native_finalize", "()V", (void *)android_media_visualizer_native_finalize},
{"native_release", "()V", (void *)android_media_visualizer_native_release},
{"native_setEnabled", "(Z)I", (void *)android_media_visualizer_native_setEnabled},
{"native_getEnabled", "()Z", (void *)android_media_visualizer_native_getEnabled},
{"getCaptureSizeRange", "()[I", (void *)android_media_visualizer_native_getCaptureSizeRange},
{"getMaxCaptureRate", "()I", (void *)android_media_visualizer_native_getMaxCaptureRate},
{"native_setCaptureSize", "(I)I", (void *)android_media_visualizer_native_setCaptureSize},
{"native_getCaptureSize", "()I", (void *)android_media_visualizer_native_getCaptureSize},
{"native_getSamplingRate", "()I", (void *)android_media_visualizer_native_getSamplingRate},
{"native_getWaveForm", "([B)I", (void *)android_media_visualizer_native_getWaveForm},
{"native_getFft", "([B)I", (void *)android_media_visualizer_native_getFft},
{"native_setPeriodicCapture","(IZZ)I",(void *)android_media_setPeriodicCapture},
};
// ----------------------------------------------------------------------------
int register_android_media_visualizer(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}

View File

@ -0,0 +1,19 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
android_media_SoundPool.cpp \
SoundPool.cpp \
SoundPoolThread.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libbinder \
libandroid_runtime \
libnativehelper \
libmedia
LOCAL_MODULE:= libsoundpool
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,190 @@
Copyright (c) 2005-2008, The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,905 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "SoundPool"
#include <utils/Log.h>
//#define USE_SHARED_MEM_BUFFER
// XXX needed for timing latency
#include <utils/Timers.h>
#include <sys/resource.h>
#include <media/AudioTrack.h>
#include <media/mediaplayer.h>
#include "SoundPool.h"
#include "SoundPoolThread.h"
namespace android
{
int kDefaultBufferCount = 4;
uint32_t kMaxSampleRate = 48000;
uint32_t kDefaultSampleRate = 44100;
uint32_t kDefaultFrameCount = 1200;
SoundPool::SoundPool(int maxChannels, int streamType, int srcQuality)
{
LOGV("SoundPool constructor: maxChannels=%d, streamType=%d, srcQuality=%d",
maxChannels, streamType, srcQuality);
// check limits
mMaxChannels = maxChannels;
if (mMaxChannels < 1) {
mMaxChannels = 1;
}
else if (mMaxChannels > 32) {
mMaxChannels = 32;
}
LOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels);
mQuit = false;
mDecodeThread = 0;
mStreamType = streamType;
mSrcQuality = srcQuality;
mAllocated = 0;
mNextSampleID = 0;
mNextChannelID = 0;
mCallback = 0;
mUserData = 0;
mChannelPool = new SoundChannel[mMaxChannels];
for (int i = 0; i < mMaxChannels; ++i) {
mChannelPool[i].init(this);
mChannels.push_back(&mChannelPool[i]);
}
// start decode thread
startThreads();
}
SoundPool::~SoundPool()
{
LOGV("SoundPool destructor");
mDecodeThread->quit();
quit();
Mutex::Autolock lock(&mLock);
mChannels.clear();
if (mChannelPool)
delete [] mChannelPool;
// clean up samples
LOGV("clear samples");
mSamples.clear();
if (mDecodeThread)
delete mDecodeThread;
}
void SoundPool::addToRestartList(SoundChannel* channel)
{
Mutex::Autolock lock(&mRestartLock);
if (!mQuit) {
mRestart.push_back(channel);
mCondition.signal();
}
}
void SoundPool::addToStopList(SoundChannel* channel)
{
Mutex::Autolock lock(&mRestartLock);
if (!mQuit) {
mStop.push_back(channel);
mCondition.signal();
}
}
int SoundPool::beginThread(void* arg)
{
SoundPool* p = (SoundPool*)arg;
return p->run();
}
int SoundPool::run()
{
mRestartLock.lock();
while (!mQuit) {
mCondition.wait(mRestartLock);
LOGV("awake");
if (mQuit) break;
while (!mStop.empty()) {
SoundChannel* channel;
LOGV("Getting channel from stop list");
List<SoundChannel* >::iterator iter = mStop.begin();
channel = *iter;
mStop.erase(iter);
mRestartLock.unlock();
if (channel != 0) {
Mutex::Autolock lock(&mLock);
channel->stop();
}
mRestartLock.lock();
if (mQuit) break;
}
while (!mRestart.empty()) {
SoundChannel* channel;
LOGV("Getting channel from list");
List<SoundChannel*>::iterator iter = mRestart.begin();
channel = *iter;
mRestart.erase(iter);
mRestartLock.unlock();
if (channel != 0) {
Mutex::Autolock lock(&mLock);
channel->nextEvent();
}
mRestartLock.lock();
if (mQuit) break;
}
}
mStop.clear();
mRestart.clear();
mCondition.signal();
mRestartLock.unlock();
LOGV("goodbye");
return 0;
}
void SoundPool::quit()
{
mRestartLock.lock();
mQuit = true;
mCondition.signal();
mCondition.wait(mRestartLock);
LOGV("return from quit");
mRestartLock.unlock();
}
bool SoundPool::startThreads()
{
createThreadEtc(beginThread, this, "SoundPool");
if (mDecodeThread == NULL)
mDecodeThread = new SoundPoolThread(this);
return mDecodeThread != NULL;
}
SoundChannel* SoundPool::findChannel(int channelID)
{
for (int i = 0; i < mMaxChannels; ++i) {
if (mChannelPool[i].channelID() == channelID) {
return &mChannelPool[i];
}
}
return NULL;
}
SoundChannel* SoundPool::findNextChannel(int channelID)
{
for (int i = 0; i < mMaxChannels; ++i) {
if (mChannelPool[i].nextChannelID() == channelID) {
return &mChannelPool[i];
}
}
return NULL;
}
int SoundPool::load(const char* path, int priority)
{
LOGV("load: path=%s, priority=%d", path, priority);
Mutex::Autolock lock(&mLock);
sp<Sample> sample = new Sample(++mNextSampleID, path);
mSamples.add(sample->sampleID(), sample);
doLoad(sample);
return sample->sampleID();
}
int SoundPool::load(int fd, int64_t offset, int64_t length, int priority)
{
LOGV("load: fd=%d, offset=%lld, length=%lld, priority=%d",
fd, offset, length, priority);
Mutex::Autolock lock(&mLock);
sp<Sample> sample = new Sample(++mNextSampleID, fd, offset, length);
mSamples.add(sample->sampleID(), sample);
doLoad(sample);
return sample->sampleID();
}
void SoundPool::doLoad(sp<Sample>& sample)
{
LOGV("doLoad: loading sample sampleID=%d", sample->sampleID());
sample->startLoad();
mDecodeThread->loadSample(sample->sampleID());
}
bool SoundPool::unload(int sampleID)
{
LOGV("unload: sampleID=%d", sampleID);
Mutex::Autolock lock(&mLock);
return mSamples.removeItem(sampleID);
}
int SoundPool::play(int sampleID, float leftVolume, float rightVolume,
int priority, int loop, float rate)
{
LOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f",
sampleID, leftVolume, rightVolume, priority, loop, rate);
sp<Sample> sample;
SoundChannel* channel;
int channelID;
Mutex::Autolock lock(&mLock);
if (mQuit) {
return 0;
}
// is sample ready?
sample = findSample(sampleID);
if ((sample == 0) || (sample->state() != Sample::READY)) {
LOGW(" sample %d not READY", sampleID);
return 0;
}
dump();
// allocate a channel
channel = allocateChannel_l(priority);
// no channel allocated - return 0
if (!channel) {
LOGV("No channel allocated");
return 0;
}
channelID = ++mNextChannelID;
LOGV("play channel %p state = %d", channel, channel->state());
channel->play(sample, channelID, leftVolume, rightVolume, priority, loop, rate);
return channelID;
}
SoundChannel* SoundPool::allocateChannel_l(int priority)
{
List<SoundChannel*>::iterator iter;
SoundChannel* channel = NULL;
// allocate a channel
if (!mChannels.empty()) {
iter = mChannels.begin();
if (priority >= (*iter)->priority()) {
channel = *iter;
mChannels.erase(iter);
LOGV("Allocated active channel");
}
}
// update priority and put it back in the list
if (channel) {
channel->setPriority(priority);
for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
if (priority < (*iter)->priority()) {
break;
}
}
mChannels.insert(iter, channel);
}
return channel;
}
// move a channel from its current position to the front of the list
void SoundPool::moveToFront_l(SoundChannel* channel)
{
for (List<SoundChannel*>::iterator iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
if (*iter == channel) {
mChannels.erase(iter);
mChannels.push_front(channel);
break;
}
}
}
void SoundPool::pause(int channelID)
{
LOGV("pause(%d)", channelID);
Mutex::Autolock lock(&mLock);
SoundChannel* channel = findChannel(channelID);
if (channel) {
channel->pause();
}
}
void SoundPool::autoPause()
{
LOGV("autoPause()");
Mutex::Autolock lock(&mLock);
for (int i = 0; i < mMaxChannels; ++i) {
SoundChannel* channel = &mChannelPool[i];
channel->autoPause();
}
}
void SoundPool::resume(int channelID)
{
LOGV("resume(%d)", channelID);
Mutex::Autolock lock(&mLock);
SoundChannel* channel = findChannel(channelID);
if (channel) {
channel->resume();
}
}
void SoundPool::autoResume()
{
LOGV("autoResume()");
Mutex::Autolock lock(&mLock);
for (int i = 0; i < mMaxChannels; ++i) {
SoundChannel* channel = &mChannelPool[i];
channel->autoResume();
}
}
void SoundPool::stop(int channelID)
{
LOGV("stop(%d)", channelID);
Mutex::Autolock lock(&mLock);
SoundChannel* channel = findChannel(channelID);
if (channel) {
channel->stop();
} else {
channel = findNextChannel(channelID);
if (channel)
channel->clearNextEvent();
}
}
void SoundPool::setVolume(int channelID, float leftVolume, float rightVolume)
{
Mutex::Autolock lock(&mLock);
SoundChannel* channel = findChannel(channelID);
if (channel) {
channel->setVolume(leftVolume, rightVolume);
}
}
void SoundPool::setPriority(int channelID, int priority)
{
LOGV("setPriority(%d, %d)", channelID, priority);
Mutex::Autolock lock(&mLock);
SoundChannel* channel = findChannel(channelID);
if (channel) {
channel->setPriority(priority);
}
}
void SoundPool::setLoop(int channelID, int loop)
{
LOGV("setLoop(%d, %d)", channelID, loop);
Mutex::Autolock lock(&mLock);
SoundChannel* channel = findChannel(channelID);
if (channel) {
channel->setLoop(loop);
}
}
void SoundPool::setRate(int channelID, float rate)
{
LOGV("setRate(%d, %f)", channelID, rate);
Mutex::Autolock lock(&mLock);
SoundChannel* channel = findChannel(channelID);
if (channel) {
channel->setRate(rate);
}
}
// call with lock held
void SoundPool::done_l(SoundChannel* channel)
{
LOGV("done_l(%d)", channel->channelID());
// if "stolen", play next event
if (channel->nextChannelID() != 0) {
LOGV("add to restart list");
addToRestartList(channel);
}
// return to idle state
else {
LOGV("move to front");
moveToFront_l(channel);
}
}
void SoundPool::setCallback(SoundPoolCallback* callback, void* user)
{
Mutex::Autolock lock(&mCallbackLock);
mCallback = callback;
mUserData = user;
}
void SoundPool::notify(SoundPoolEvent event)
{
Mutex::Autolock lock(&mCallbackLock);
if (mCallback != NULL) {
mCallback(event, this, mUserData);
}
}
void SoundPool::dump()
{
for (int i = 0; i < mMaxChannels; ++i) {
mChannelPool[i].dump();
}
}
Sample::Sample(int sampleID, const char* url)
{
init();
mSampleID = sampleID;
mUrl = strdup(url);
LOGV("create sampleID=%d, url=%s", mSampleID, mUrl);
}
Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length)
{
init();
mSampleID = sampleID;
mFd = dup(fd);
mOffset = offset;
mLength = length;
LOGV("create sampleID=%d, fd=%d, offset=%lld, length=%lld", mSampleID, mFd, mLength, mOffset);
}
void Sample::init()
{
mData = 0;
mSize = 0;
mRefCount = 0;
mSampleID = 0;
mState = UNLOADED;
mFd = -1;
mOffset = 0;
mLength = 0;
mUrl = 0;
}
Sample::~Sample()
{
LOGV("Sample::destructor sampleID=%d, fd=%d", mSampleID, mFd);
if (mFd > 0) {
LOGV("close(%d)", mFd);
::close(mFd);
}
mData.clear();
delete mUrl;
}
status_t Sample::doLoad()
{
uint32_t sampleRate;
int numChannels;
int format;
sp<IMemory> p;
LOGV("Start decode");
if (mUrl) {
p = MediaPlayer::decode(mUrl, &sampleRate, &numChannels, &format);
} else {
p = MediaPlayer::decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format);
LOGV("close(%d)", mFd);
::close(mFd);
mFd = -1;
}
if (p == 0) {
LOGE("Unable to load sample: %s", mUrl);
return -1;
}
LOGV("pointer = %p, size = %u, sampleRate = %u, numChannels = %d",
p->pointer(), p->size(), sampleRate, numChannels);
if (sampleRate > kMaxSampleRate) {
LOGE("Sample rate (%u) out of range", sampleRate);
return - 1;
}
if ((numChannels < 1) || (numChannels > 2)) {
LOGE("Sample channel count (%d) out of range", numChannels);
return - 1;
}
//_dumpBuffer(p->pointer(), p->size());
uint8_t* q = static_cast<uint8_t*>(p->pointer()) + p->size() - 10;
//_dumpBuffer(q, 10, 10, false);
mData = p;
mSize = p->size();
mSampleRate = sampleRate;
mNumChannels = numChannels;
mFormat = format;
mState = READY;
return 0;
}
void SoundChannel::init(SoundPool* soundPool)
{
mSoundPool = soundPool;
}
// call with sound pool lock held
void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume,
float rightVolume, int priority, int loop, float rate)
{
AudioTrack* oldTrack;
AudioTrack* newTrack;
status_t status;
{ // scope for the lock
Mutex::Autolock lock(&mLock);
LOGV("SoundChannel::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) {
LOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID);
mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
stop_l();
return;
}
// 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 totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate;
uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount;
uint32_t frameCount = 0;
if (loop) {
frameCount = sample->size()/numChannels/
((sample->format() == AudioSystem::PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
}
#ifndef USE_SHARED_MEM_BUFFER
// Ensure minimum audio buffer size in case of short looped sample
if(frameCount < totalFrames) {
frameCount = totalFrames;
}
#endif
// 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);
uint32_t channels = (numChannels == 2) ?
AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO;
// do not create a new audio track if current track is compatible with sample parameters
#ifdef USE_SHARED_MEM_BUFFER
newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
channels, sample->getIMemory(), 0, callback, userData);
#else
newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
channels, frameCount, 0, callback, userData, bufferFrames);
#endif
oldTrack = mAudioTrack;
status = newTrack->initCheck();
if (status != NO_ERROR) {
LOGE("Error creating AudioTrack");
goto exit;
}
LOGV("setVolume %p", newTrack);
newTrack->setVolume(leftVolume, rightVolume);
newTrack->setLoop(0, frameCount, loop);
// From now on, AudioTrack callbacks recevieved with previous toggle value will be ignored.
mToggle = toggle;
mAudioTrack = newTrack;
mPos = 0;
mSample = sample;
mChannelID = nextChannelID;
mPriority = priority;
mLoop = loop;
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
mNumChannels = numChannels;
mRate = rate;
clearNextEvent();
mState = PLAYING;
mAudioTrack->start();
mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize();
}
exit:
LOGV("delete oldTrack %p", oldTrack);
delete oldTrack;
if (status != NO_ERROR) {
delete newTrack;
mAudioTrack = NULL;
}
}
void SoundChannel::nextEvent()
{
sp<Sample> sample;
int nextChannelID;
float leftVolume;
float rightVolume;
int priority;
int loop;
float rate;
// check for valid event
{
Mutex::Autolock lock(&mLock);
nextChannelID = mNextEvent.channelID();
if (nextChannelID == 0) {
LOGV("stolen channel has no event");
return;
}
sample = mNextEvent.sample();
leftVolume = mNextEvent.leftVolume();
rightVolume = mNextEvent.rightVolume();
priority = mNextEvent.priority();
loop = mNextEvent.loop();
rate = mNextEvent.rate();
}
LOGV("Starting stolen channel %d -> %d", channelID(), nextChannelID);
play(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
}
void SoundChannel::callback(int event, void* user, void *info)
{
SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1));
channel->process(event, info, (unsigned long)user & 1);
}
void SoundChannel::process(int event, void *info, unsigned long toggle)
{
//LOGV("process(%d)", mChannelID);
Mutex::Autolock lock(&mLock);
AudioTrack::Buffer* b = NULL;
if (event == AudioTrack::EVENT_MORE_DATA) {
b = static_cast<AudioTrack::Buffer *>(info);
}
if (mToggle != toggle) {
LOGV("process wrong toggle %p channel %d", this, mChannelID);
if (b != NULL) {
b->size = 0;
}
return;
}
sp<Sample> sample = mSample;
// LOGV("SoundChannel::process event %d", event);
if (event == AudioTrack::EVENT_MORE_DATA) {
// check for stop state
if (b->size == 0) return;
if (mState == IDLE) {
b->size = 0;
return;
}
if (sample != 0) {
// fill buffer
uint8_t* q = (uint8_t*) b->i8;
size_t count = 0;
if (mPos < (int)sample->size()) {
uint8_t* p = sample->data() + mPos;
count = sample->size() - mPos;
if (count > b->size) {
count = b->size;
}
memcpy(q, p, count);
// LOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size, count);
} else if (mPos < mAudioBufferSize) {
count = mAudioBufferSize - mPos;
if (count > b->size) {
count = b->size;
}
memset(q, 0, count);
// LOGV("fill extra: q=%p, mPos=%u, b->size=%u, count=%d", q, mPos, b->size, count);
}
mPos += count;
b->size = count;
//LOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]);
}
} else if (event == AudioTrack::EVENT_UNDERRUN) {
LOGV("process %p channel %d EVENT_UNDERRUN", this, mChannelID);
mSoundPool->addToStopList(this);
} else if (event == AudioTrack::EVENT_LOOP_END) {
LOGV("End loop %p channel %d count %d", this, mChannelID, *(int *)info);
}
}
// call with lock held
bool SoundChannel::doStop_l()
{
if (mState != IDLE) {
setVolume_l(0, 0);
LOGV("stop");
mAudioTrack->stop();
mSample.clear();
mState = IDLE;
mPriority = IDLE_PRIORITY;
return true;
}
return false;
}
// call with lock held and sound pool lock held
void SoundChannel::stop_l()
{
if (doStop_l()) {
mSoundPool->done_l(this);
}
}
// call with sound pool lock held
void SoundChannel::stop()
{
bool stopped;
{
Mutex::Autolock lock(&mLock);
stopped = doStop_l();
}
if (stopped) {
mSoundPool->done_l(this);
}
}
//FIXME: Pause is a little broken right now
void SoundChannel::pause()
{
Mutex::Autolock lock(&mLock);
if (mState == PLAYING) {
LOGV("pause track");
mState = PAUSED;
mAudioTrack->pause();
}
}
void SoundChannel::autoPause()
{
Mutex::Autolock lock(&mLock);
if (mState == PLAYING) {
LOGV("pause track");
mState = PAUSED;
mAutoPaused = true;
mAudioTrack->pause();
}
}
void SoundChannel::resume()
{
Mutex::Autolock lock(&mLock);
if (mState == PAUSED) {
LOGV("resume track");
mState = PLAYING;
mAutoPaused = false;
mAudioTrack->start();
}
}
void SoundChannel::autoResume()
{
Mutex::Autolock lock(&mLock);
if (mAutoPaused && (mState == PAUSED)) {
LOGV("resume track");
mState = PLAYING;
mAutoPaused = false;
mAudioTrack->start();
}
}
void SoundChannel::setRate(float rate)
{
Mutex::Autolock lock(&mLock);
if (mAudioTrack != 0 && mSample.get() != 0) {
uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5);
mAudioTrack->setSampleRate(sampleRate);
mRate = rate;
}
}
// call with lock held
void SoundChannel::setVolume_l(float leftVolume, float rightVolume)
{
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
if (mAudioTrack != 0) mAudioTrack->setVolume(leftVolume, rightVolume);
}
void SoundChannel::setVolume(float leftVolume, float rightVolume)
{
Mutex::Autolock lock(&mLock);
setVolume_l(leftVolume, rightVolume);
}
void SoundChannel::setLoop(int loop)
{
Mutex::Autolock lock(&mLock);
if (mAudioTrack != 0 && mSample.get() != 0) {
uint32_t loopEnd = mSample->size()/mNumChannels/
((mSample->format() == AudioSystem::PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
mAudioTrack->setLoop(0, loopEnd, loop);
mLoop = loop;
}
}
SoundChannel::~SoundChannel()
{
LOGV("SoundChannel destructor %p", this);
{
Mutex::Autolock lock(&mLock);
clearNextEvent();
doStop_l();
}
// do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack
// callback thread to exit which may need to execute process() and acquire the mLock.
delete mAudioTrack;
}
void SoundChannel::dump()
{
LOGV("mState = %d mChannelID=%d, mNumChannels=%d, mPos = %d, mPriority=%d, mLoop=%d",
mState, mChannelID, mNumChannels, mPos, mPriority, mLoop);
}
void SoundEvent::set(const sp<Sample>& sample, int channelID, float leftVolume,
float rightVolume, int priority, int loop, float rate)
{
mSample =sample;
mChannelID = channelID;
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
mPriority = priority;
mLoop = loop;
mRate =rate;
}
} // end namespace android

View File

@ -0,0 +1,238 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SOUNDPOOL_H_
#define SOUNDPOOL_H_
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
#include <media/AudioTrack.h>
namespace android {
static const int IDLE_PRIORITY = -1;
// forward declarations
class SoundEvent;
class SoundPoolThread;
class SoundPool;
// for queued events
class SoundPoolEvent {
public:
SoundPoolEvent(int msg, int arg1=0, int arg2=0) :
mMsg(msg), mArg1(arg1), mArg2(arg2) {}
int mMsg;
int mArg1;
int mArg2;
enum MessageType { INVALID, SAMPLE_LOADED };
};
// callback function prototype
typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user);
// tracks samples used by application
class Sample : public RefBase {
public:
enum sample_state { UNLOADED, LOADING, READY, UNLOADING };
Sample(int sampleID, const char* url);
Sample(int sampleID, int fd, int64_t offset, int64_t length);
~Sample();
int sampleID() { return mSampleID; }
int numChannels() { return mNumChannels; }
int sampleRate() { return mSampleRate; }
int format() { return mFormat; }
size_t size() { return mSize; }
int state() { return mState; }
uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); }
status_t doLoad();
void startLoad() { mState = LOADING; }
sp<IMemory> getIMemory() { return mData; }
// hack
void init(int numChannels, int sampleRate, int format, size_t size, sp<IMemory> data ) {
mNumChannels = numChannels; mSampleRate = sampleRate; mFormat = format; mSize = size; mData = data; }
private:
void init();
size_t mSize;
volatile int32_t mRefCount;
uint16_t mSampleID;
uint16_t mSampleRate;
uint8_t mState : 3;
uint8_t mNumChannels : 2;
uint8_t mFormat : 2;
int mFd;
int64_t mOffset;
int64_t mLength;
char* mUrl;
sp<IMemory> mData;
};
// stores pending events for stolen channels
class SoundEvent
{
public:
SoundEvent() : mChannelID(0), mLeftVolume(0), mRightVolume(0),
mPriority(IDLE_PRIORITY), mLoop(0), mRate(0) {}
void set(const sp<Sample>& sample, int channelID, float leftVolume,
float rightVolume, int priority, int loop, float rate);
sp<Sample> sample() { return mSample; }
int channelID() { return mChannelID; }
float leftVolume() { return mLeftVolume; }
float rightVolume() { return mRightVolume; }
int priority() { return mPriority; }
int loop() { return mLoop; }
float rate() { return mRate; }
void clear() { mChannelID = 0; mSample.clear(); }
protected:
sp<Sample> mSample;
int mChannelID;
float mLeftVolume;
float mRightVolume;
int mPriority;
int mLoop;
float mRate;
};
// for channels aka AudioTracks
class SoundChannel : public SoundEvent {
public:
enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING };
SoundChannel() : mAudioTrack(0), mState(IDLE), mNumChannels(1),
mPos(0), mToggle(0), mAutoPaused(false) {}
~SoundChannel();
void init(SoundPool* soundPool);
void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume,
int priority, int loop, float rate);
void setVolume_l(float leftVolume, float rightVolume);
void setVolume(float leftVolume, float rightVolume);
void stop_l();
void stop();
void pause();
void autoPause();
void resume();
void autoResume();
void setRate(float rate);
int state() { return mState; }
void setPriority(int priority) { mPriority = priority; }
void setLoop(int loop);
int numChannels() { return mNumChannels; }
void clearNextEvent() { mNextEvent.clear(); }
void nextEvent();
int nextChannelID() { return mNextEvent.channelID(); }
void dump();
private:
static void callback(int event, void* user, void *info);
void process(int event, void *info, unsigned long toggle);
bool doStop_l();
SoundPool* mSoundPool;
AudioTrack* mAudioTrack;
SoundEvent mNextEvent;
Mutex mLock;
int mState;
int mNumChannels;
int mPos;
int mAudioBufferSize;
unsigned long mToggle;
bool mAutoPaused;
};
// application object for managing a pool of sounds
class SoundPool {
friend class SoundPoolThread;
friend class SoundChannel;
public:
SoundPool(int maxChannels, int streamType, int srcQuality);
~SoundPool();
int load(const char* url, int priority);
int load(int fd, int64_t offset, int64_t length, int priority);
bool unload(int sampleID);
int play(int sampleID, float leftVolume, float rightVolume, int priority,
int loop, float rate);
void pause(int channelID);
void autoPause();
void resume(int channelID);
void autoResume();
void stop(int channelID);
void setVolume(int channelID, float leftVolume, float rightVolume);
void setPriority(int channelID, int priority);
void setLoop(int channelID, int loop);
void setRate(int channelID, float rate);
int streamType() const { return mStreamType; }
int srcQuality() const { return mSrcQuality; }
// called from SoundPoolThread
void sampleLoaded(int sampleID);
// called from AudioTrack thread
void done_l(SoundChannel* channel);
// callback function
void setCallback(SoundPoolCallback* callback, void* user);
void* getUserData() { return mUserData; }
private:
SoundPool() {} // no default constructor
bool startThreads();
void doLoad(sp<Sample>& sample);
sp<Sample> findSample(int sampleID) { return mSamples.valueFor(sampleID); }
SoundChannel* findChannel (int channelID);
SoundChannel* findNextChannel (int channelID);
SoundChannel* allocateChannel_l(int priority);
void moveToFront_l(SoundChannel* channel);
void notify(SoundPoolEvent event);
void dump();
// restart thread
void addToRestartList(SoundChannel* channel);
void addToStopList(SoundChannel* channel);
static int beginThread(void* arg);
int run();
void quit();
Mutex mLock;
Mutex mRestartLock;
Condition mCondition;
SoundPoolThread* mDecodeThread;
SoundChannel* mChannelPool;
List<SoundChannel*> mChannels;
List<SoundChannel*> mRestart;
List<SoundChannel*> mStop;
DefaultKeyedVector< int, sp<Sample> > mSamples;
int mMaxChannels;
int mStreamType;
int mSrcQuality;
int mAllocated;
int mNextSampleID;
int mNextChannelID;
bool mQuit;
// callback
Mutex mCallbackLock;
SoundPoolCallback* mCallback;
void* mUserData;
};
} // end namespace android
#endif /*SOUNDPOOL_H_*/

View File

@ -0,0 +1,114 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "SoundPoolThread"
#include "utils/Log.h"
#include "SoundPoolThread.h"
namespace android {
void SoundPoolThread::write(SoundPoolMsg msg) {
Mutex::Autolock lock(&mLock);
while (mMsgQueue.size() >= maxMessages) {
mCondition.wait(mLock);
}
// if thread is quitting, don't add to queue
if (mRunning) {
mMsgQueue.push(msg);
mCondition.signal();
}
}
const SoundPoolMsg SoundPoolThread::read() {
Mutex::Autolock lock(&mLock);
while (mMsgQueue.size() == 0) {
mCondition.wait(mLock);
}
SoundPoolMsg msg = mMsgQueue[0];
mMsgQueue.removeAt(0);
mCondition.signal();
return msg;
}
void SoundPoolThread::quit() {
Mutex::Autolock lock(&mLock);
if (mRunning) {
mRunning = false;
mMsgQueue.clear();
mMsgQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0));
mCondition.signal();
mCondition.wait(mLock);
}
LOGV("return from quit");
}
SoundPoolThread::SoundPoolThread(SoundPool* soundPool) :
mSoundPool(soundPool)
{
mMsgQueue.setCapacity(maxMessages);
if (createThreadEtc(beginThread, this, "SoundPoolThread")) {
mRunning = true;
}
}
SoundPoolThread::~SoundPoolThread()
{
quit();
}
int SoundPoolThread::beginThread(void* arg) {
LOGV("beginThread");
SoundPoolThread* soundPoolThread = (SoundPoolThread*)arg;
return soundPoolThread->run();
}
int SoundPoolThread::run() {
LOGV("run");
for (;;) {
SoundPoolMsg msg = read();
LOGV("Got message m=%d, mData=%d", msg.mMessageType, msg.mData);
switch (msg.mMessageType) {
case SoundPoolMsg::KILL:
LOGV("goodbye");
return NO_ERROR;
case SoundPoolMsg::LOAD_SAMPLE:
doLoadSample(msg.mData);
break;
default:
LOGW("run: Unrecognized message %d\n",
msg.mMessageType);
break;
}
}
}
void SoundPoolThread::loadSample(int sampleID) {
write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID));
}
void SoundPoolThread::doLoadSample(int sampleID) {
sp <Sample> sample = mSoundPool->findSample(sampleID);
status_t status = -1;
if (sample != 0) {
status = sample->doLoad();
}
mSoundPool->notify(SoundPoolEvent(SoundPoolEvent::SAMPLE_LOADED, sampleID, status));
}
} // end namespace android

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SOUNDPOOLTHREAD_H_
#define SOUNDPOOLTHREAD_H_
#include <utils/threads.h>
#include <utils/Vector.h>
#include <media/AudioTrack.h>
#include "SoundPool.h"
namespace android {
class SoundPoolMsg {
public:
enum MessageType { INVALID, KILL, LOAD_SAMPLE };
SoundPoolMsg() : mMessageType(INVALID), mData(0) {}
SoundPoolMsg(MessageType MessageType, int data) :
mMessageType(MessageType), mData(data) {}
uint16_t mMessageType;
uint16_t mData;
};
/*
* This class handles background requests from the SoundPool
*/
class SoundPoolThread {
public:
SoundPoolThread(SoundPool* SoundPool);
~SoundPoolThread();
void loadSample(int sampleID);
void quit();
void write(SoundPoolMsg msg);
private:
static const size_t maxMessages = 5;
static int beginThread(void* arg);
int run();
void doLoadSample(int sampleID);
const SoundPoolMsg read();
Mutex mLock;
Condition mCondition;
Vector<SoundPoolMsg> mMsgQueue;
SoundPool* mSoundPool;
bool mRunning;
};
} // end namespace android
#endif /*SOUNDPOOLTHREAD_H_*/

View File

@ -0,0 +1,327 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "SoundPool-JNI"
#include <utils/Log.h>
#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include "SoundPool.h"
using namespace android;
static struct fields_t {
jfieldID mNativeContext;
jmethodID mPostEvent;
jclass mSoundPoolClass;
} fields;
static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
return (SoundPool*)env->GetIntField(thiz, fields.mNativeContext);
}
// ----------------------------------------------------------------------------
static int
android_media_SoundPool_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
{
LOGV("android_media_SoundPool_load_URL");
SoundPool *ap = MusterSoundPool(env, thiz);
if (path == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return 0;
}
const char* s = env->GetStringUTFChars(path, NULL);
int id = ap->load(s, priority);
env->ReleaseStringUTFChars(path, s);
return id;
}
static int
android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
jlong offset, jlong length, jint priority)
{
LOGV("android_media_SoundPool_load_FD");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return 0;
return ap->load(getParcelFileDescriptorFD(env, fileDescriptor),
int64_t(offset), int64_t(length), int(priority));
}
static bool
android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
LOGV("android_media_SoundPool_unload\n");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return 0;
return ap->unload(sampleID);
}
static int
android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
jfloat rate)
{
LOGV("android_media_SoundPool_play\n");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return 0;
return ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
}
static void
android_media_SoundPool_pause(JNIEnv *env, jobject thiz, jint channelID)
{
LOGV("android_media_SoundPool_pause");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->pause(channelID);
}
static void
android_media_SoundPool_resume(JNIEnv *env, jobject thiz, jint channelID)
{
LOGV("android_media_SoundPool_resume");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->resume(channelID);
}
static void
android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz)
{
LOGV("android_media_SoundPool_autoPause");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->autoPause();
}
static void
android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz)
{
LOGV("android_media_SoundPool_autoResume");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->autoResume();
}
static void
android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID)
{
LOGV("android_media_SoundPool_stop");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->stop(channelID);
}
static void
android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
float leftVolume, float rightVolume)
{
LOGV("android_media_SoundPool_setVolume");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setVolume(channelID, leftVolume, rightVolume);
}
static void
android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
int priority)
{
LOGV("android_media_SoundPool_setPriority");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setPriority(channelID, priority);
}
static void
android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
int loop)
{
LOGV("android_media_SoundPool_setLoop");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setLoop(channelID, loop);
}
static void
android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
float rate)
{
LOGV("android_media_SoundPool_setRate");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap == NULL) return;
ap->setRate(channelID, rate);
}
static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, void* user)
{
LOGV("callback: (%d, %d, %d, %p, %p)", event.mMsg, event.mArg1, event.mArg2, soundPool, user);
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->CallStaticVoidMethod(fields.mSoundPoolClass, fields.mPostEvent, user, event.mMsg, event.mArg1, event.mArg2, NULL);
}
static jint
android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
{
LOGV("android_media_SoundPool_native_setup");
SoundPool *ap = new SoundPool(maxChannels, streamType, srcQuality);
if (ap == NULL) {
return -1;
}
// save pointer to SoundPool C++ object in opaque field in Java object
env->SetIntField(thiz, fields.mNativeContext, (int)ap);
// set callback with weak reference
jobject globalWeakRef = env->NewGlobalRef(weakRef);
ap->setCallback(android_media_callback, globalWeakRef);
return 0;
}
static void
android_media_SoundPool_release(JNIEnv *env, jobject thiz)
{
LOGV("android_media_SoundPool_release");
SoundPool *ap = MusterSoundPool(env, thiz);
if (ap != NULL) {
// release weak reference
jobject weakRef = (jobject) ap->getUserData();
if (weakRef != NULL) {
env->DeleteGlobalRef(weakRef);
}
// clear callback and native context
ap->setCallback(NULL, NULL);
env->SetIntField(thiz, fields.mNativeContext, 0);
delete ap;
}
}
// ----------------------------------------------------------------------------
// Dalvik VM type signatures
static JNINativeMethod gMethods[] = {
{ "_load",
"(Ljava/lang/String;I)I",
(void *)android_media_SoundPool_load_URL
},
{ "_load",
"(Ljava/io/FileDescriptor;JJI)I",
(void *)android_media_SoundPool_load_FD
},
{ "unload",
"(I)Z",
(void *)android_media_SoundPool_unload
},
{ "play",
"(IFFIIF)I",
(void *)android_media_SoundPool_play
},
{ "pause",
"(I)V",
(void *)android_media_SoundPool_pause
},
{ "resume",
"(I)V",
(void *)android_media_SoundPool_resume
},
{ "autoPause",
"()V",
(void *)android_media_SoundPool_autoPause
},
{ "autoResume",
"()V",
(void *)android_media_SoundPool_autoResume
},
{ "stop",
"(I)V",
(void *)android_media_SoundPool_stop
},
{ "setVolume",
"(IFF)V",
(void *)android_media_SoundPool_setVolume
},
{ "setPriority",
"(II)V",
(void *)android_media_SoundPool_setPriority
},
{ "setLoop",
"(II)V",
(void *)android_media_SoundPool_setLoop
},
{ "setRate",
"(IF)V",
(void *)android_media_SoundPool_setRate
},
{ "native_setup",
"(Ljava/lang/Object;III)I",
(void*)android_media_SoundPool_native_setup
},
{ "release",
"()V",
(void*)android_media_SoundPool_release
}
};
static const char* const kClassPathName = "android/media/SoundPool";
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
jclass clazz;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
LOGE("Can't find %s", kClassPathName);
goto bail;
}
fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "I");
if (fields.mNativeContext == NULL) {
LOGE("Can't find SoundPool.mNativeContext");
goto bail;
}
fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.mPostEvent == NULL) {
LOGE("Can't find android/media/SoundPool.postEventFromNative");
goto bail;
}
// create a reference to class. Technically, we're leaking this reference
// since it's a static object.
fields.mSoundPoolClass = (jclass) env->NewGlobalRef(clazz);
if (AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
goto bail;
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}