diff options
Diffstat (limited to 'media/jni')
22 files changed, 1248 insertions, 176 deletions
diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 416a2a1..63a61e2 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + android_media_ImageReader.cpp \ android_media_MediaCrypto.cpp \ android_media_MediaCodec.cpp \ android_media_MediaCodecList.cpp \ @@ -56,6 +57,8 @@ LOCAL_C_INCLUDES += \ frameworks/av/media/libstagefright/codecs/amrnb/common/include \ frameworks/av/media/mtp \ frameworks/native/include/media/openmax \ + $(call include-path-for, libhardware)/hardware \ + system/media/camera/include \ $(PV_INCLUDES) \ $(JNI_H_INCLUDE) \ $(call include-path-for, corecg graphics) diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp new file mode 100644 index 0000000..0030dbd --- /dev/null +++ b/media/jni/android_media_ImageReader.cpp @@ -0,0 +1,879 @@ +/* + * Copyright 2013 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 "ImageReader_JNI" +#include <utils/Log.h> +#include <utils/misc.h> +#include <utils/List.h> +#include <utils/String8.h> + +#include <cstdio> + +#include <gui/CpuConsumer.h> +#include <gui/Surface.h> +#include <camera3.h> + +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/android_view_Surface.h> + +#include <jni.h> +#include <JNIHelp.h> + +#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) ) + +#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext" +#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mLockedBuffer" +#define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID "mTimestamp" + +// ---------------------------------------------------------------------------- + +using namespace android; + +enum { + IMAGE_READER_MAX_NUM_PLANES = 3, +}; + +enum { + ACQUIRE_SUCCESS = 0, + ACQUIRE_NO_BUFFERS = 1, + ACQUIRE_MAX_IMAGES = 2, +}; + +static struct { + jfieldID mNativeContext; + jmethodID postEventFromNative; +} gImageReaderClassInfo; + +static struct { + jfieldID mLockedBuffer; + jfieldID mTimestamp; +} gSurfaceImageClassInfo; + +static struct { + jclass clazz; + jmethodID ctor; +} gSurfacePlaneClassInfo; + +// ---------------------------------------------------------------------------- + +class JNIImageReaderContext : public CpuConsumer::FrameAvailableListener +{ +public: + JNIImageReaderContext(JNIEnv* env, jobject weakThiz, jclass clazz, int maxImages); + + virtual ~JNIImageReaderContext(); + + virtual void onFrameAvailable(); + + CpuConsumer::LockedBuffer* getLockedBuffer(); + + void returnLockedBuffer(CpuConsumer::LockedBuffer* buffer); + + void setCpuConsumer(const sp<CpuConsumer>& consumer) { mConsumer = consumer; } + CpuConsumer* getCpuConsumer() { return mConsumer.get(); } + + void setBufferQueue(const sp<BufferQueue>& bq) { mBufferQueue = bq; } + BufferQueue* getBufferQueue() { return mBufferQueue.get(); } + + void setBufferFormat(int format) { mFormat = format; } + int getBufferFormat() { return mFormat; } + + void setBufferWidth(int width) { mWidth = width; } + int getBufferWidth() { return mWidth; } + + void setBufferHeight(int height) { mHeight = height; } + int getBufferHeight() { return mHeight; } + +private: + static JNIEnv* getJNIEnv(bool* needsDetach); + static void detachJNI(); + + List<CpuConsumer::LockedBuffer*> mBuffers; + sp<CpuConsumer> mConsumer; + sp<BufferQueue> mBufferQueue; + jobject mWeakThiz; + jclass mClazz; + int mFormat; + int mWidth; + int mHeight; +}; + +JNIImageReaderContext::JNIImageReaderContext(JNIEnv* env, + jobject weakThiz, jclass clazz, int maxImages) : + mWeakThiz(env->NewGlobalRef(weakThiz)), + mClazz((jclass)env->NewGlobalRef(clazz)) { + for (int i = 0; i < maxImages; i++) { + CpuConsumer::LockedBuffer *buffer = new CpuConsumer::LockedBuffer; + mBuffers.push_back(buffer); + } +} + +JNIEnv* JNIImageReaderContext::getJNIEnv(bool* needsDetach) { + LOG_ALWAYS_FATAL_IF(needsDetach == NULL, "needsDetach is null!!!"); + *needsDetach = false; + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (env == NULL) { + JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL}; + JavaVM* vm = AndroidRuntime::getJavaVM(); + int result = vm->AttachCurrentThread(&env, (void*) &args); + if (result != JNI_OK) { + ALOGE("thread attach failed: %#x", result); + return NULL; + } + *needsDetach = true; + } + return env; +} + +void JNIImageReaderContext::detachJNI() { + JavaVM* vm = AndroidRuntime::getJavaVM(); + int result = vm->DetachCurrentThread(); + if (result != JNI_OK) { + ALOGE("thread detach failed: %#x", result); + } +} + +CpuConsumer::LockedBuffer* JNIImageReaderContext::getLockedBuffer() { + if (mBuffers.empty()) { + return NULL; + } + // Return a LockedBuffer pointer and remove it from the list + List<CpuConsumer::LockedBuffer*>::iterator it = mBuffers.begin(); + CpuConsumer::LockedBuffer* buffer = *it; + mBuffers.erase(it); + return buffer; +} + +void JNIImageReaderContext::returnLockedBuffer(CpuConsumer::LockedBuffer* buffer) { + mBuffers.push_back(buffer); +} + +JNIImageReaderContext::~JNIImageReaderContext() { + bool needsDetach = false; + JNIEnv* env = getJNIEnv(&needsDetach); + if (env != NULL) { + env->DeleteGlobalRef(mWeakThiz); + env->DeleteGlobalRef(mClazz); + } else { + ALOGW("leaking JNI object references"); + } + if (needsDetach) { + detachJNI(); + } + + // Delete LockedBuffers + for (List<CpuConsumer::LockedBuffer *>::iterator it = mBuffers.begin(); + it != mBuffers.end(); it++) { + delete *it; + } + mBuffers.clear(); + mConsumer.clear(); +} + +void JNIImageReaderContext::onFrameAvailable() +{ + ALOGV("%s: frame available", __FUNCTION__); + bool needsDetach = false; + JNIEnv* env = getJNIEnv(&needsDetach); + if (env != NULL) { + env->CallStaticVoidMethod(mClazz, gImageReaderClassInfo.postEventFromNative, mWeakThiz); + } else { + ALOGW("onFrameAvailable event will not posted"); + } + if (needsDetach) { + detachJNI(); + } +} + +// ---------------------------------------------------------------------------- + +extern "C" { + +static JNIImageReaderContext* ImageReader_getContext(JNIEnv* env, jobject thiz) +{ + JNIImageReaderContext *ctx; + ctx = reinterpret_cast<JNIImageReaderContext *> + (env->GetLongField(thiz, gImageReaderClassInfo.mNativeContext)); + return ctx; +} + +static CpuConsumer* ImageReader_getCpuConsumer(JNIEnv* env, jobject thiz) +{ + ALOGV("%s:", __FUNCTION__); + JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz); + if (ctx == NULL) { + jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); + return NULL; + } + return ctx->getCpuConsumer(); +} + +static BufferQueue* ImageReader_getBufferQueue(JNIEnv* env, jobject thiz) +{ + ALOGV("%s:", __FUNCTION__); + JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz); + if (ctx == NULL) { + jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); + return NULL; + } + return ctx->getBufferQueue(); +} + +static void ImageReader_setNativeContext(JNIEnv* env, + jobject thiz, sp<JNIImageReaderContext> ctx) +{ + ALOGV("%s:", __FUNCTION__); + JNIImageReaderContext* const p = ImageReader_getContext(env, thiz); + if (ctx != 0) { + ctx->incStrong((void*)ImageReader_setNativeContext); + } + if (p) { + p->decStrong((void*)ImageReader_setNativeContext); + } + env->SetLongField(thiz, gImageReaderClassInfo.mNativeContext, + reinterpret_cast<jlong>(ctx.get())); +} + +static CpuConsumer::LockedBuffer* Image_getLockedBuffer(JNIEnv* env, jobject image) +{ + return reinterpret_cast<CpuConsumer::LockedBuffer*>( + env->GetLongField(image, gSurfaceImageClassInfo.mLockedBuffer)); +} + +static void Image_setBuffer(JNIEnv* env, jobject thiz, + const CpuConsumer::LockedBuffer* buffer) +{ + env->SetLongField(thiz, gSurfaceImageClassInfo.mLockedBuffer, reinterpret_cast<jlong>(buffer)); +} + +// Some formats like JPEG defined with different values between android.graphics.ImageFormat and +// graphics.h, need convert to the one defined in graphics.h here. +static int Image_getPixelFormat(JNIEnv* env, int format) +{ + int jpegFormat, rawSensorFormat; + jfieldID fid; + + ALOGV("%s: format = 0x%x", __FUNCTION__, format); + + jclass imageFormatClazz = env->FindClass("android/graphics/ImageFormat"); + ALOG_ASSERT(imageFormatClazz != NULL); + + fid = env->GetStaticFieldID(imageFormatClazz, "JPEG", "I"); + jpegFormat = env->GetStaticIntField(imageFormatClazz, fid); + fid = env->GetStaticFieldID(imageFormatClazz, "RAW_SENSOR", "I"); + rawSensorFormat = env->GetStaticIntField(imageFormatClazz, fid); + + // Translate the JPEG to BLOB for camera purpose, an add more if more mismatch is found. + if (format == jpegFormat) { + format = HAL_PIXEL_FORMAT_BLOB; + } + // Same thing for RAW_SENSOR format + if (format == rawSensorFormat) { + format = HAL_PIXEL_FORMAT_RAW_SENSOR; + } + + return format; +} + +static uint32_t Image_getJpegSize(CpuConsumer::LockedBuffer* buffer) +{ + ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!"); + uint32_t size = 0; + uint32_t width = buffer->width; + uint8_t* jpegBuffer = buffer->data; + + // First check for JPEG transport header at the end of the buffer + uint8_t* header = jpegBuffer + (width - sizeof(struct camera3_jpeg_blob)); + struct camera3_jpeg_blob *blob = (struct camera3_jpeg_blob*)(header); + if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID) { + size = blob->jpeg_size; + ALOGV("%s: Jpeg size = %d", __FUNCTION__, size); + } + + // failed to find size, default to whole buffer + if (size == 0) { + size = width; + } + + return size; +} + +static void Image_getLockedBufferInfo(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx, + uint8_t **base, uint32_t *size) +{ + ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!"); + ALOG_ASSERT(base != NULL, "base is NULL!!!"); + ALOG_ASSERT(size != NULL, "size is NULL!!!"); + ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0)); + + ALOGV("%s: buffer: %p", __FUNCTION__, buffer); + + uint32_t dataSize, ySize, cSize, cStride; + uint8_t *cb, *cr; + uint8_t *pData = NULL; + int bytesPerPixel = 0; + + dataSize = ySize = cSize = cStride = 0; + int32_t fmt = buffer->format; + switch (fmt) { + case HAL_PIXEL_FORMAT_YCbCr_420_888: + pData = + (idx == 0) ? + buffer->data : + (idx == 1) ? + buffer->dataCb : + buffer->dataCr; + if (idx == 0) { + dataSize = buffer->stride * buffer->height; + } else { + dataSize = buffer->chromaStride * buffer->height / 2; + } + break; + // NV21 + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + cr = buffer->data + (buffer->stride * buffer->height); + cb = cr + 1; + ySize = buffer->width * buffer->height; + cSize = buffer->width * buffer->height / 2; + + pData = + (idx == 0) ? + buffer->data : + (idx == 1) ? + cb: + cr; + + dataSize = (idx == 0) ? ySize : cSize; + break; + case HAL_PIXEL_FORMAT_YV12: + // Y and C stride need to be 16 pixel aligned. + LOG_ALWAYS_FATAL_IF(buffer->stride % 16, + "Stride is not 16 pixel aligned %d", buffer->stride); + + ySize = buffer->stride * buffer->height; + cStride = ALIGN(buffer->stride / 2, 16); + cr = buffer->data + ySize; + cSize = cStride * buffer->height / 2; + cb = cr + cSize; + + pData = + (idx == 0) ? + buffer->data : + (idx == 1) ? + cb : + cr; + dataSize = (idx == 0) ? ySize : cSize; + break; + case HAL_PIXEL_FORMAT_Y8: + // Single plane, 8bpp. + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + + pData = buffer->data; + dataSize = buffer->stride * buffer->height; + break; + case HAL_PIXEL_FORMAT_Y16: + // Single plane, 16bpp, strides are specified in pixels, not in bytes + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + + pData = buffer->data; + dataSize = buffer->stride * buffer->height * 2; + break; + case HAL_PIXEL_FORMAT_BLOB: + // Used for JPEG data, height must be 1, width == size, single plane. + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + ALOG_ASSERT(buffer->height == 1, "JPEG should has height value %d", buffer->height); + + pData = buffer->data; + dataSize = Image_getJpegSize(buffer); + break; + case HAL_PIXEL_FORMAT_RAW_SENSOR: + // Single plane 16bpp bayer data. + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + pData = buffer->data; + dataSize = buffer->width * 2 * buffer->height; + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + // Single plane, 32bpp. + bytesPerPixel = 4; + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + pData = buffer->data; + dataSize = buffer->stride * buffer->height * bytesPerPixel; + break; + case HAL_PIXEL_FORMAT_RGB_565: + // Single plane, 16bpp. + bytesPerPixel = 2; + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + pData = buffer->data; + dataSize = buffer->stride * buffer->height * bytesPerPixel; + break; + case HAL_PIXEL_FORMAT_RGB_888: + // Single plane, 24bpp. + bytesPerPixel = 3; + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + pData = buffer->data; + dataSize = buffer->stride * buffer->height * bytesPerPixel; + break; + default: + jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", + "Pixel format: 0x%x is unsupported", fmt); + break; + } + + *base = pData; + *size = dataSize; +} + +static jint Image_imageGetPixelStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx) +{ + ALOGV("%s: buffer index: %d", __FUNCTION__, idx); + ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0), "Index is out of range:%d", idx); + + int pixelStride = 0; + ALOG_ASSERT(buffer != NULL, "buffer is NULL"); + + int32_t fmt = buffer->format; + switch (fmt) { + case HAL_PIXEL_FORMAT_YCbCr_420_888: + pixelStride = (idx == 0) ? 1 : buffer->chromaStep; + break; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + pixelStride = (idx == 0) ? 1 : 2; + break; + case HAL_PIXEL_FORMAT_Y8: + // Single plane 8bpp data. + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + pixelStride; + break; + case HAL_PIXEL_FORMAT_YV12: + pixelStride = 1; + break; + case HAL_PIXEL_FORMAT_BLOB: + // Used for JPEG data, single plane, row and pixel strides are 0 + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + pixelStride = 0; + break; + case HAL_PIXEL_FORMAT_Y16: + case HAL_PIXEL_FORMAT_RAW_SENSOR: + case HAL_PIXEL_FORMAT_RGB_565: + // Single plane 16bpp data. + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + pixelStride = 2; + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + pixelStride = 4; + break; + case HAL_PIXEL_FORMAT_RGB_888: + // Single plane, 24bpp. + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + pixelStride = 3; + break; + default: + jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", + "Pixel format: 0x%x is unsupported", fmt); + break; + } + + return pixelStride; +} + +static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx) +{ + ALOGV("%s: buffer index: %d", __FUNCTION__, idx); + ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0)); + + int rowStride = 0; + ALOG_ASSERT(buffer != NULL, "buffer is NULL"); + + int32_t fmt = buffer->format; + + switch (fmt) { + case HAL_PIXEL_FORMAT_YCbCr_420_888: + rowStride = (idx == 0) ? buffer->stride : buffer->chromaStride; + break; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + rowStride = buffer->width; + break; + case HAL_PIXEL_FORMAT_YV12: + LOG_ALWAYS_FATAL_IF(buffer->stride % 16, + "Stride is not 16 pixel aligned %d", buffer->stride); + rowStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16); + break; + case HAL_PIXEL_FORMAT_BLOB: + // Used for JPEG data, single plane, row and pixel strides are 0 + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + rowStride = 0; + break; + case HAL_PIXEL_FORMAT_Y8: + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + LOG_ALWAYS_FATAL_IF(buffer->stride % 16, + "Stride is not 16 pixel aligned %d", buffer->stride); + rowStride = buffer->stride; + break; + case HAL_PIXEL_FORMAT_Y16: + case HAL_PIXEL_FORMAT_RAW_SENSOR: + // In native side, strides are specified in pixels, not in bytes. + // Single plane 16bpp bayer data. even width/height, + // row stride multiple of 16 pixels (32 bytes) + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + LOG_ALWAYS_FATAL_IF(buffer->stride % 16, + "Stride is not 16 pixel aligned %d", buffer->stride); + rowStride = buffer->stride * 2; + break; + case HAL_PIXEL_FORMAT_RGB_565: + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + rowStride = buffer->stride * 2; + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + rowStride = buffer->stride * 4; + break; + case HAL_PIXEL_FORMAT_RGB_888: + // Single plane, 24bpp. + ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); + rowStride = buffer->stride * 3; + break; + default: + ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt); + jniThrowException(env, "java/lang/UnsupportedOperationException", + "unsupported buffer format"); + break; + } + + return rowStride; +} + +// ---------------------------------------------------------------------------- + +static void ImageReader_classInit(JNIEnv* env, jclass clazz) +{ + ALOGV("%s:", __FUNCTION__); + + jclass imageClazz = env->FindClass("android/media/ImageReader$SurfaceImage"); + LOG_ALWAYS_FATAL_IF(imageClazz == NULL, + "can't find android/graphics/ImageReader$SurfaceImage"); + gSurfaceImageClassInfo.mLockedBuffer = env->GetFieldID( + imageClazz, ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID, "J"); + LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mLockedBuffer == NULL, + "can't find android/graphics/ImageReader.%s", + ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID); + + gSurfaceImageClassInfo.mTimestamp = env->GetFieldID( + imageClazz, ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID, "J"); + LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mTimestamp == NULL, + "can't find android/graphics/ImageReader.%s", + ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID); + + gImageReaderClassInfo.mNativeContext = env->GetFieldID( + clazz, ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID, "J"); + LOG_ALWAYS_FATAL_IF(gImageReaderClassInfo.mNativeContext == NULL, + "can't find android/graphics/ImageReader.%s", + ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID); + + gImageReaderClassInfo.postEventFromNative = env->GetStaticMethodID( + clazz, "postEventFromNative", "(Ljava/lang/Object;)V"); + LOG_ALWAYS_FATAL_IF(gImageReaderClassInfo.postEventFromNative == NULL, + "can't find android/graphics/ImageReader.postEventFromNative"); + + jclass planeClazz = env->FindClass("android/media/ImageReader$SurfaceImage$SurfacePlane"); + LOG_ALWAYS_FATAL_IF(planeClazz == NULL, "Can not find SurfacePlane class"); + // FindClass only gives a local reference of jclass object. + gSurfacePlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz); + gSurfacePlaneClassInfo.ctor = env->GetMethodID(gSurfacePlaneClassInfo.clazz, "<init>", + "(Landroid/media/ImageReader$SurfaceImage;III)V"); + LOG_ALWAYS_FATAL_IF(gSurfacePlaneClassInfo.ctor == NULL, + "Can not find SurfacePlane constructor"); +} + +static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, + jint width, jint height, jint format, jint maxImages) +{ + status_t res; + int nativeFormat; + + ALOGV("%s: width:%d, height: %d, format: 0x%x, maxImages:%d", + __FUNCTION__, width, height, format, maxImages); + + nativeFormat = Image_getPixelFormat(env, format); + + sp<BufferQueue> bq = new BufferQueue(); + sp<CpuConsumer> consumer = new CpuConsumer(bq, maxImages, + /*controlledByApp*/true); + // TODO: throw dvm exOutOfMemoryError? + if (consumer == NULL) { + jniThrowRuntimeException(env, "Failed to allocate native CpuConsumer"); + return; + } + + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + jniThrowRuntimeException(env, "Can't find android/graphics/ImageReader"); + return; + } + sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages)); + ctx->setCpuConsumer(consumer); + ctx->setBufferQueue(bq); + consumer->setFrameAvailableListener(ctx); + ImageReader_setNativeContext(env, thiz, ctx); + ctx->setBufferFormat(nativeFormat); + ctx->setBufferWidth(width); + ctx->setBufferHeight(height); + + // Set the width/height/format to the CpuConsumer + res = consumer->setDefaultBufferSize(width, height); + if (res != OK) { + jniThrowException(env, "java/lang/IllegalStateException", + "Failed to set CpuConsumer buffer size"); + return; + } + res = consumer->setDefaultBufferFormat(nativeFormat); + if (res != OK) { + jniThrowException(env, "java/lang/IllegalStateException", + "Failed to set CpuConsumer buffer format"); + } +} + +static void ImageReader_close(JNIEnv* env, jobject thiz) +{ + ALOGV("%s:", __FUNCTION__); + + JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz); + if (ctx == NULL) { + // ImageReader is already closed. + return; + } + + CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz); + if (consumer != NULL) { + consumer->abandon(); + consumer->setFrameAvailableListener(NULL); + } + ImageReader_setNativeContext(env, thiz, NULL); +} + +static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image) +{ + ALOGV("%s:", __FUNCTION__); + JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); + if (ctx == NULL) { + ALOGW("ImageReader#close called before Image#close, consider calling Image#close first"); + return; + } + + CpuConsumer* consumer = ctx->getCpuConsumer(); + CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, image); + if (!buffer) { + ALOGW("Image already released!!!"); + return; + } + consumer->unlockBuffer(*buffer); + Image_setBuffer(env, image, NULL); + ctx->returnLockedBuffer(buffer); +} + +static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, + jobject image) +{ + ALOGV("%s:", __FUNCTION__); + JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); + if (ctx == NULL) { + jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); + return -1; + } + + CpuConsumer* consumer = ctx->getCpuConsumer(); + CpuConsumer::LockedBuffer* buffer = ctx->getLockedBuffer(); + if (buffer == NULL) { + ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than" + " maxImages buffers"); + return ACQUIRE_MAX_IMAGES; + } + status_t res = consumer->lockNextBuffer(buffer); + if (res != NO_ERROR) { + if (res != BAD_VALUE /*no buffers*/) { + if (res == NOT_ENOUGH_DATA) { + return ACQUIRE_MAX_IMAGES; + } else { + ALOGE("%s Fail to lockNextBuffer with error: %d ", + __FUNCTION__, res); + jniThrowExceptionFmt(env, "java/lang/AssertionError", + "Unknown error (%d) when we tried to lock buffer.", + res); + } + } + return ACQUIRE_NO_BUFFERS; + } + + if (buffer->format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { + jniThrowException(env, "java/lang/UnsupportedOperationException", + "NV21 format is not supported by ImageReader"); + return -1; + } + + // Check if the left-top corner of the crop rect is origin, we currently assume this point is + // zero, will revist this once this assumption turns out problematic. + Point lt = buffer->crop.leftTop(); + if (lt.x != 0 || lt.y != 0) { + jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", + "crop left top corner [%d, %d] need to be at origin", lt.x, lt.y); + return -1; + } + + // Check if the producer buffer configurations match what ImageReader configured. + // We want to fail for the very first image because this case is too bad. + int outputWidth = buffer->width; + int outputHeight = buffer->height; + + // Correct width/height when crop is set. + if (!buffer->crop.isEmpty()) { + outputWidth = buffer->crop.getWidth(); + outputHeight = buffer->crop.getHeight(); + } + + int imageReaderWidth = ctx->getBufferWidth(); + int imageReaderHeight = ctx->getBufferHeight(); + if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) && + (imageReaderWidth != outputWidth || imageReaderHeight > outputHeight)) { + /** + * For video decoder, the buffer height is actually the vertical stride, + * which is always >= actual image height. For future, decoder need provide + * right crop rectangle to CpuConsumer to indicate the actual image height, + * see bug 9563986. After this bug is fixed, we can enforce the height equal + * check. Right now, only make sure buffer height is no less than ImageReader + * height. + */ + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", + outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); + return -1; + } + + if (ctx->getBufferFormat() != buffer->format) { + // Return the buffer to the queue. + consumer->unlockBuffer(*buffer); + ctx->returnLockedBuffer(buffer); + + // Throw exception + ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x", + buffer->format, ctx->getBufferFormat()); + String8 msg; + msg.appendFormat("The producer output buffer format 0x%x doesn't " + "match the ImageReader's configured buffer format 0x%x.", + buffer->format, ctx->getBufferFormat()); + jniThrowException(env, "java/lang/UnsupportedOperationException", + msg.string()); + return -1; + } + // Set SurfaceImage instance member variables + Image_setBuffer(env, image, buffer); + env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp, + static_cast<jlong>(buffer->timestamp)); + + return ACQUIRE_SUCCESS; +} + +static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) +{ + ALOGV("%s: ", __FUNCTION__); + + BufferQueue* bq = ImageReader_getBufferQueue(env, thiz); + if (bq == NULL) { + jniThrowRuntimeException(env, "CpuConsumer is uninitialized"); + return NULL; + } + + // Wrap the IGBP in a Java-language Surface. + return android_view_Surface_createFromIGraphicBufferProducer(env, bq); +} + +static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx) +{ + int rowStride, pixelStride; + ALOGV("%s: buffer index: %d", __FUNCTION__, idx); + + CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); + + ALOG_ASSERT(buffer != NULL); + if (buffer == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "Image was released"); + } + rowStride = Image_imageGetRowStride(env, buffer, idx); + pixelStride = Image_imageGetPixelStride(env, buffer, idx); + + jobject surfPlaneObj = env->NewObject(gSurfacePlaneClassInfo.clazz, + gSurfacePlaneClassInfo.ctor, thiz, idx, rowStride, pixelStride); + + return surfPlaneObj; +} + +static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx) +{ + uint8_t *base = NULL; + uint32_t size = 0; + jobject byteBuffer; + + ALOGV("%s: buffer index: %d", __FUNCTION__, idx); + + CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); + + if (buffer == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", "Image was released"); + } + + // Create byteBuffer from native buffer + Image_getLockedBufferInfo(env, buffer, idx, &base, &size); + byteBuffer = env->NewDirectByteBuffer(base, size); + // TODO: throw dvm exOutOfMemoryError? + if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) { + jniThrowException(env, "java/lang/IllegalStateException", "Failed to allocate ByteBuffer"); + } + + return byteBuffer; +} + +} // extern "C" + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gImageReaderMethods[] = { + {"nativeClassInit", "()V", (void*)ImageReader_classInit }, + {"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init }, + {"nativeClose", "()V", (void*)ImageReader_close }, + {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease }, + {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup }, + {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface }, +}; + +static JNINativeMethod gImageMethods[] = { + {"nativeImageGetBuffer", "(I)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer }, + {"nativeCreatePlane", "(I)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;", + (void*)Image_createSurfacePlane }, +}; + +int register_android_media_ImageReader(JNIEnv *env) { + + int ret1 = AndroidRuntime::registerNativeMethods(env, + "android/media/ImageReader", gImageReaderMethods, NELEM(gImageReaderMethods)); + + int ret2 = AndroidRuntime::registerNativeMethods(env, + "android/media/ImageReader$SurfaceImage", gImageMethods, NELEM(gImageMethods)); + + return (ret1 || ret2); +} diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index cd1d9ce..b8d437c 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -38,6 +38,8 @@ #include <media/stagefright/foundation/AString.h> #include <media/stagefright/MediaErrors.h> +#include <nativehelper/ScopedLocalRef.h> + #include <system/window.h> namespace android { @@ -49,9 +51,14 @@ enum { DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3, }; +struct CryptoErrorCodes { + jint cryptoErrorNoKey; + jint cryptoErrorKeyExpired; + jint cryptoErrorResourceBusy; +} gCryptoErrorCodes; + struct fields_t { jfieldID context; - jfieldID cryptoInfoNumSubSamplesID; jfieldID cryptoInfoNumBytesOfClearDataID; jfieldID cryptoInfoNumBytesOfEncryptedDataID; @@ -81,7 +88,7 @@ JMediaCodec::JMediaCodec( mLooper->start( false, // runOnCallingThread false, // canCallJava - PRIORITY_DEFAULT); + PRIORITY_FOREGROUND); if (nameIsType) { mCodec = MediaCodec::CreateByType(mLooper, name, encoder); @@ -115,7 +122,7 @@ status_t JMediaCodec::configure( int flags) { sp<Surface> client; if (bufferProducer != NULL) { - mSurfaceTextureClient = new Surface(bufferProducer); + mSurfaceTextureClient = new Surface(bufferProducer, true /* controlledByApp */); } else { mSurfaceTextureClient.clear(); } @@ -181,9 +188,10 @@ status_t JMediaCodec::dequeueOutputBuffer( return err; } - jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo"); + ScopedLocalRef<jclass> clazz( + env, env->FindClass("android/media/MediaCodec$BufferInfo")); - jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V"); + jmethodID method = env->GetMethodID(clazz.get(), "set", "(IIJI)V"); env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags); return OK; @@ -222,29 +230,33 @@ status_t JMediaCodec::getBuffers( return err; } - jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer"); - CHECK(byteBufferClass != NULL); + ScopedLocalRef<jclass> byteBufferClass( + env, env->FindClass("java/nio/ByteBuffer")); + + CHECK(byteBufferClass.get() != NULL); jmethodID orderID = env->GetMethodID( - byteBufferClass, + byteBufferClass.get(), "order", "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"); CHECK(orderID != NULL); - jclass byteOrderClass = env->FindClass("java/nio/ByteOrder"); - CHECK(byteOrderClass != NULL); + ScopedLocalRef<jclass> byteOrderClass( + env, env->FindClass("java/nio/ByteOrder")); + + CHECK(byteOrderClass.get() != NULL); jmethodID nativeOrderID = env->GetStaticMethodID( - byteOrderClass, "nativeOrder", "()Ljava/nio/ByteOrder;"); + byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;"); CHECK(nativeOrderID != NULL); jobject nativeByteOrderObj = - env->CallStaticObjectMethod(byteOrderClass, nativeOrderID); + env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID); CHECK(nativeByteOrderObj != NULL); *bufArray = (jobjectArray)env->NewObjectArray( - buffers.size(), byteBufferClass, NULL); + buffers.size(), byteBufferClass.get(), NULL); if (*bufArray == NULL) { env->DeleteLocalRef(nativeByteOrderObj); return NO_MEMORY; @@ -298,6 +310,10 @@ status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const { return OK; } +status_t JMediaCodec::setParameters(const sp<AMessage> &msg) { + return mCodec->setParameters(msg); +} + void JMediaCodec::setVideoScalingMode(int mode) { if (mSurfaceTextureClient != NULL) { native_window_set_scaling_mode(mSurfaceTextureClient.get(), mode); @@ -333,26 +349,41 @@ static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { } static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { - jclass clazz = env->FindClass("android/media/MediaCodec$CryptoException"); - CHECK(clazz != NULL); + ScopedLocalRef<jclass> clazz( + env, env->FindClass("android/media/MediaCodec$CryptoException")); + CHECK(clazz.get() != NULL); jmethodID constructID = - env->GetMethodID(clazz, "<init>", "(ILjava/lang/String;)V"); + env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V"); CHECK(constructID != NULL); jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error"); + /* translate OS errors to Java API CryptoException errorCodes */ + switch (err) { + case ERROR_DRM_NO_LICENSE: + err = gCryptoErrorCodes.cryptoErrorNoKey; + break; + case ERROR_DRM_LICENSE_EXPIRED: + err = gCryptoErrorCodes.cryptoErrorKeyExpired; + break; + case ERROR_DRM_RESOURCE_BUSY: + err = gCryptoErrorCodes.cryptoErrorResourceBusy; + break; + default: + break; + } + jthrowable exception = - (jthrowable)env->NewObject(clazz, constructID, err, msgObj); + (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj); env->Throw(exception); } static jint throwExceptionAsNecessary( JNIEnv *env, status_t err, const char *msg = NULL) { - if (err >= ERROR_DRM_WV_VENDOR_MIN && err <= ERROR_DRM_WV_VENDOR_MAX) { + if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) { // We'll throw our custom MediaCodec.CryptoException - throwCryptoException(env, err, msg); return 0; } @@ -370,6 +401,12 @@ static jint throwExceptionAsNecessary( case INFO_OUTPUT_BUFFERS_CHANGED: return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED; + case ERROR_DRM_NO_LICENSE: + case ERROR_DRM_LICENSE_EXPIRED: + case ERROR_DRM_RESOURCE_BUSY: + throwCryptoException(env, err, msg); + break; + default: { jniThrowException(env, "java/lang/IllegalStateException", msg); @@ -804,6 +841,27 @@ static jobject android_media_MediaCodec_getName( return NULL; } +static void android_media_MediaCodec_setParameters( + JNIEnv *env, jobject thiz, jobjectArray keys, jobjectArray vals) { + ALOGV("android_media_MediaCodec_setParameters"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + sp<AMessage> params; + status_t err = ConvertKeyValueArraysToMessage(env, keys, vals, ¶ms); + + if (err == OK) { + err = codec->setParameters(params); + } + + throwExceptionAsNecessary(env, err); +} + static void android_media_MediaCodec_setVideoScalingMode( JNIEnv *env, jobject thiz, jint mode) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); @@ -823,35 +881,55 @@ static void android_media_MediaCodec_setVideoScalingMode( } static void android_media_MediaCodec_native_init(JNIEnv *env) { - jclass clazz = env->FindClass("android/media/MediaCodec"); - CHECK(clazz != NULL); + ScopedLocalRef<jclass> clazz( + env, env->FindClass("android/media/MediaCodec")); + CHECK(clazz.get() != NULL); - gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); + gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "I"); CHECK(gFields.context != NULL); - clazz = env->FindClass("android/media/MediaCodec$CryptoInfo"); - CHECK(clazz != NULL); + clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo")); + CHECK(clazz.get() != NULL); gFields.cryptoInfoNumSubSamplesID = - env->GetFieldID(clazz, "numSubSamples", "I"); + env->GetFieldID(clazz.get(), "numSubSamples", "I"); CHECK(gFields.cryptoInfoNumSubSamplesID != NULL); gFields.cryptoInfoNumBytesOfClearDataID = - env->GetFieldID(clazz, "numBytesOfClearData", "[I"); + env->GetFieldID(clazz.get(), "numBytesOfClearData", "[I"); CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL); gFields.cryptoInfoNumBytesOfEncryptedDataID = - env->GetFieldID(clazz, "numBytesOfEncryptedData", "[I"); + env->GetFieldID(clazz.get(), "numBytesOfEncryptedData", "[I"); CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL); - gFields.cryptoInfoKeyID = env->GetFieldID(clazz, "key", "[B"); + gFields.cryptoInfoKeyID = env->GetFieldID(clazz.get(), "key", "[B"); CHECK(gFields.cryptoInfoKeyID != NULL); - gFields.cryptoInfoIVID = env->GetFieldID(clazz, "iv", "[B"); + gFields.cryptoInfoIVID = env->GetFieldID(clazz.get(), "iv", "[B"); CHECK(gFields.cryptoInfoIVID != NULL); - gFields.cryptoInfoModeID = env->GetFieldID(clazz, "mode", "I"); + gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I"); CHECK(gFields.cryptoInfoModeID != NULL); + + clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException")); + CHECK(clazz.get() != NULL); + + jfieldID field; + field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I"); + CHECK(field != NULL); + gCryptoErrorCodes.cryptoErrorNoKey = + env->GetStaticIntField(clazz.get(), field); + + field = env->GetStaticFieldID(clazz.get(), "ERROR_KEY_EXPIRED", "I"); + CHECK(field != NULL); + gCryptoErrorCodes.cryptoErrorKeyExpired = + env->GetStaticIntField(clazz.get(), field); + + field = env->GetStaticFieldID(clazz.get(), "ERROR_RESOURCE_BUSY", "I"); + CHECK(field != NULL); + gCryptoErrorCodes.cryptoErrorResourceBusy = + env->GetStaticIntField(clazz.get(), field); } static void android_media_MediaCodec_native_setup( @@ -933,6 +1011,9 @@ static JNINativeMethod gMethods[] = { { "getName", "()Ljava/lang/String;", (void *)android_media_MediaCodec_getName }, + { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V", + (void *)android_media_MediaCodec_setParameters }, + { "setVideoScalingMode", "(I)V", (void *)android_media_MediaCodec_setVideoScalingMode }, diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 282d2c5..2fbbd72 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -87,6 +87,8 @@ struct JMediaCodec : public RefBase { status_t getName(JNIEnv *env, jstring *name) const; + status_t setParameters(const sp<AMessage> ¶ms); + void setVideoScalingMode(int mode); protected: diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp index 04430ec..caa594e 100644 --- a/media/jni/android_media_MediaCodecList.cpp +++ b/media/jni/android_media_MediaCodecList.cpp @@ -110,10 +110,11 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( Vector<MediaCodecList::ProfileLevel> profileLevels; Vector<uint32_t> colorFormats; + uint32_t flags; status_t err = MediaCodecList::getInstance()->getCodecCapabilities( - index, typeStr, &profileLevels, &colorFormats); + index, typeStr, &profileLevels, &colorFormats, &flags); env->ReleaseStringUTFChars(type, typeStr); typeStr = NULL; @@ -127,6 +128,9 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( env->FindClass("android/media/MediaCodecInfo$CodecCapabilities"); CHECK(capsClazz != NULL); + jfieldID flagsField = + env->GetFieldID(capsClazz, "flags", "I"); + jobject caps = env->AllocObject(capsClazz); jclass profileLevelClazz = @@ -163,6 +167,8 @@ static jobject android_media_MediaCodecList_getCodecCapabilities( env->SetObjectField(caps, profileLevelsField, profileLevelArray); + env->SetIntField(caps, flagsField, flags); + env->DeleteLocalRef(profileLevelArray); profileLevelArray = NULL; diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index 7799ca4..bbb74d2 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -21,6 +21,7 @@ #include "android_media_MediaDrm.h" #include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" #include "android_os_Parcel.h" #include "jni.h" #include "JNIHelp.h" @@ -242,6 +243,9 @@ static bool throwExceptionAsNecessary( } else if (err == ERROR_DRM_NOT_PROVISIONED) { jniThrowException(env, "android/media/NotProvisionedException", msg); return true; + } else if (err == ERROR_DRM_RESOURCE_BUSY) { + jniThrowException(env, "android/media/ResourceBusyException", msg); + return true; } else if (err == ERROR_DRM_DEVICE_REVOKED) { jniThrowException(env, "android/media/DeniedByServerException", msg); return true; @@ -345,14 +349,14 @@ void JDrm::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) // static -bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16]) { +bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) { sp<IDrm> drm = MakeDrm(); if (drm == NULL) { return false; } - return drm->isCryptoSchemeSupported(uuid); + return drm->isCryptoSchemeSupported(uuid, mimeType); } status_t JDrm::initCheck() const { @@ -608,7 +612,7 @@ static void android_media_MediaDrm_native_finalize( } static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative( - JNIEnv *env, jobject thiz, jbyteArray uuidObj) { + JNIEnv *env, jobject thiz, jbyteArray uuidObj, jstring jmimeType) { if (uuidObj == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); @@ -625,7 +629,12 @@ static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative( return false; } - return JDrm::IsCryptoSchemeSupported(uuid.array()); + String8 mimeType; + if (jmimeType != NULL) { + mimeType = JStringToString8(env, jmimeType); + } + + return JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType); } static jbyteArray android_media_MediaDrm_openSession( @@ -750,7 +759,9 @@ static jbyteArray android_media_MediaDrm_provideKeyResponse( status_t err = drm->provideKeyResponse(sessionId, response, keySetId); - throwExceptionAsNecessary(env, err, "Failed to handle key response"); + if (throwExceptionAsNecessary(env, err, "Failed to handle key response")) { + return NULL; + } return VectorToJByteArray(env, keySetId); } @@ -1101,7 +1112,9 @@ static jbyteArray android_media_MediaDrm_encryptNative( status_t err = drm->encrypt(sessionId, keyId, input, iv, output); - throwExceptionAsNecessary(env, err, "Failed to encrypt"); + if (throwExceptionAsNecessary(env, err, "Failed to encrypt")) { + return NULL; + } return VectorToJByteArray(env, output); } @@ -1129,7 +1142,9 @@ static jbyteArray android_media_MediaDrm_decryptNative( Vector<uint8_t> output; status_t err = drm->decrypt(sessionId, keyId, input, iv, output); - throwExceptionAsNecessary(env, err, "Failed to decrypt"); + if (throwExceptionAsNecessary(env, err, "Failed to decrypt")) { + return NULL; + } return VectorToJByteArray(env, output); } @@ -1157,7 +1172,9 @@ static jbyteArray android_media_MediaDrm_signNative( status_t err = drm->sign(sessionId, keyId, message, signature); - throwExceptionAsNecessary(env, err, "Failed to sign"); + if (throwExceptionAsNecessary(env, err, "Failed to sign")) { + return NULL; + } return VectorToJByteArray(env, signature); } @@ -1201,7 +1218,7 @@ static JNINativeMethod gMethods[] = { { "native_finalize", "()V", (void *)android_media_MediaDrm_native_finalize }, - { "isCryptoSchemeSupportedNative", "([B)Z", + { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z", (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative }, { "openSession", "()[B", diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h index 9b3917f..620ad28 100644 --- a/media/jni/android_media_MediaDrm.h +++ b/media/jni/android_media_MediaDrm.h @@ -37,7 +37,7 @@ public: }; struct JDrm : public BnDrmClient { - static bool IsCryptoSchemeSupported(const uint8_t uuid[16]); + static bool IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType); JDrm(JNIEnv *env, jobject thiz, const uint8_t uuid[16]); diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp index 1704d5c..1ac45d4 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -22,6 +22,7 @@ #include "android_media_Utils.h" #include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" #include "jni.h" #include "JNIHelp.h" diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp index 7517e85..457b956 100644 --- a/media/jni/android_media_MediaMuxer.cpp +++ b/media/jni/android_media_MediaMuxer.cpp @@ -164,6 +164,18 @@ static void android_media_MediaMuxer_setOrientationHint( } +static void android_media_MediaMuxer_setLocation( + JNIEnv *env, jclass clazz, jint nativeObject, jint latitude, jint longitude) { + MediaMuxer* muxer = reinterpret_cast<MediaMuxer *>(nativeObject); + + status_t res = muxer->setLocation(latitude, longitude); + if (res != OK) { + jniThrowException(env, "java/lang/IllegalStateException", + "Failed to set location"); + return; + } +} + static void android_media_MediaMuxer_start(JNIEnv *env, jclass clazz, jint nativeObject) { sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject)); @@ -216,6 +228,9 @@ static JNINativeMethod gMethods[] = { { "nativeSetOrientationHint", "(II)V", (void *)android_media_MediaMuxer_setOrientationHint}, + { "nativeSetLocation", "(III)V", + (void *)android_media_MediaMuxer_setLocation}, + { "nativeStart", "(I)V", (void *)android_media_MediaMuxer_start}, { "nativeWriteSampleData", "(IILjava/nio/ByteBuffer;IIJI)V", diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index d06380d..4be9cd6 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -31,6 +31,7 @@ #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" +#include "android_runtime/Log.h" #include "utils/Errors.h" // for status_t #include "utils/KeyedVector.h" #include "utils/String8.h" @@ -526,14 +527,6 @@ android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, float leftVolume, 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 @@ -782,39 +775,6 @@ android_media_MediaPlayer_setRetransmitEndpoint(JNIEnv *env, jobject thiz, return ret; } -static jboolean -android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request) -{ - ALOGV("setParameter: key %d", key); - sp<MediaPlayer> mp = getMediaPlayer(env, thiz); - if (mp == NULL ) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return false; - } - - Parcel *request = parcelForJavaObject(env, java_request); - status_t err = mp->setParameter(key, *request); - if (err == OK) { - return true; - } else { - return false; - } -} - -static void -android_media_MediaPlayer_getParameter(JNIEnv *env, jobject thiz, jint key, jobject java_reply) -{ - ALOGV("getParameter: key %d", key); - sp<MediaPlayer> mp = getMediaPlayer(env, thiz); - if (mp == NULL ) { - jniThrowException(env, "java/lang/IllegalStateException", NULL); - return; - } - - Parcel *reply = parcelForJavaObject(env, java_reply); - process_media_player_call(env, thiz, mp->getParameter(key, reply), NULL, NULL ); -} - static void android_media_MediaPlayer_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject java_player) { @@ -913,7 +873,6 @@ static JNINativeMethod gMethods[] = { {"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}, @@ -925,8 +884,6 @@ static JNINativeMethod gMethods[] = { {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel}, {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect}, {"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData}, - {"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_setParameter}, - {"getParameter", "(ILandroid/os/Parcel;)V", (void *)android_media_MediaPlayer_getParameter}, {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint}, {"setNextMediaPlayer", "(Landroid/media/MediaPlayer;)V", (void *)android_media_MediaPlayer_setNextMediaPlayer}, {"updateProxyConfig", "(Landroid/net/ProxyProperties;)V", (void *)android_media_MediaPlayer_updateProxyConfig}, @@ -941,6 +898,7 @@ static int register_android_media_MediaPlayer(JNIEnv *env) "android/media/MediaPlayer", gMethods, NELEM(gMethods)); } +extern int register_android_media_ImageReader(JNIEnv *env); extern int register_android_media_Crypto(JNIEnv *env); extern int register_android_media_Drm(JNIEnv *env); extern int register_android_media_MediaCodec(JNIEnv *env); @@ -968,6 +926,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) } assert(env != NULL); + if (register_android_media_ImageReader(env) < 0) { + ALOGE("ERROR: ImageReader native registration failed"); + goto bail; + } + if (register_android_media_MediaPlayer(env) < 0) { ALOGE("ERROR: MediaPlayer native registration failed\n"); goto bail; diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp index 5d27966..4e3d14e 100644 --- a/media/jni/android_media_MediaScanner.cpp +++ b/media/jni/android_media_MediaScanner.cpp @@ -25,6 +25,7 @@ #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" using namespace android; diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index e35ace3..54c5e9b 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -24,6 +24,8 @@ #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/AMessage.h> +#include <nativehelper/ScopedLocalRef.h> + namespace android { bool ConvertKeyValueArraysToKeyedVector( @@ -76,33 +78,35 @@ bool ConvertKeyValueArraysToKeyedVector( } static jobject makeIntegerObject(JNIEnv *env, int32_t value) { - jclass clazz = env->FindClass("java/lang/Integer"); - CHECK(clazz != NULL); + ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer")); + CHECK(clazz.get() != NULL); - jmethodID integerConstructID = env->GetMethodID(clazz, "<init>", "(I)V"); + jmethodID integerConstructID = + env->GetMethodID(clazz.get(), "<init>", "(I)V"); CHECK(integerConstructID != NULL); - return env->NewObject(clazz, integerConstructID, value); + return env->NewObject(clazz.get(), integerConstructID, value); } static jobject makeLongObject(JNIEnv *env, int64_t value) { - jclass clazz = env->FindClass("java/lang/Long"); - CHECK(clazz != NULL); + ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long")); + CHECK(clazz.get() != NULL); - jmethodID longConstructID = env->GetMethodID(clazz, "<init>", "(J)V"); + jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V"); CHECK(longConstructID != NULL); - return env->NewObject(clazz, longConstructID, value); + return env->NewObject(clazz.get(), longConstructID, value); } static jobject makeFloatObject(JNIEnv *env, float value) { - jclass clazz = env->FindClass("java/lang/Float"); - CHECK(clazz != NULL); + ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float")); + CHECK(clazz.get() != NULL); - jmethodID floatConstructID = env->GetMethodID(clazz, "<init>", "(F)V"); + jmethodID floatConstructID = + env->GetMethodID(clazz.get(), "<init>", "(F)V"); CHECK(floatConstructID != NULL); - return env->NewObject(clazz, floatConstructID, value); + return env->NewObject(clazz.get(), floatConstructID, value); } static jobject makeByteBufferObject( @@ -110,15 +114,16 @@ static jobject makeByteBufferObject( jbyteArray byteArrayObj = env->NewByteArray(size); env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data); - jclass clazz = env->FindClass("java/nio/ByteBuffer"); - CHECK(clazz != NULL); + ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer")); + CHECK(clazz.get() != NULL); jmethodID byteBufWrapID = - env->GetStaticMethodID(clazz, "wrap", "([B)Ljava/nio/ByteBuffer;"); + env->GetStaticMethodID( + clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;"); CHECK(byteBufWrapID != NULL); jobject byteBufObj = env->CallStaticObjectMethod( - clazz, byteBufWrapID, byteArrayObj); + clazz.get(), byteBufWrapID, byteArrayObj); env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL; @@ -140,14 +145,15 @@ static void SetMapInt32( status_t ConvertMessageToMap( JNIEnv *env, const sp<AMessage> &msg, jobject *map) { - jclass hashMapClazz = env->FindClass("java/util/HashMap"); + ScopedLocalRef<jclass> hashMapClazz( + env, env->FindClass("java/util/HashMap")); - if (hashMapClazz == NULL) { + if (hashMapClazz.get() == NULL) { return -EINVAL; } jmethodID hashMapConstructID = - env->GetMethodID(hashMapClazz, "<init>", "()V"); + env->GetMethodID(hashMapClazz.get(), "<init>", "()V"); if (hashMapConstructID == NULL) { return -EINVAL; @@ -155,7 +161,7 @@ status_t ConvertMessageToMap( jmethodID hashMapPutID = env->GetMethodID( - hashMapClazz, + hashMapClazz.get(), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); @@ -163,7 +169,7 @@ status_t ConvertMessageToMap( return -EINVAL; } - jobject hashMap = env->NewObject(hashMapClazz, hashMapConstructID); + jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID); for (size_t i = 0; i < msg->countEntries(); ++i) { AMessage::Type valueType; @@ -276,17 +282,16 @@ status_t ConvertMessageToMap( status_t ConvertKeyValueArraysToMessage( JNIEnv *env, jobjectArray keys, jobjectArray values, sp<AMessage> *out) { - jclass stringClass = env->FindClass("java/lang/String"); - CHECK(stringClass != NULL); - - jclass integerClass = env->FindClass("java/lang/Integer"); - CHECK(integerClass != NULL); - - jclass floatClass = env->FindClass("java/lang/Float"); - CHECK(floatClass != NULL); - - jclass byteBufClass = env->FindClass("java/nio/ByteBuffer"); - CHECK(byteBufClass != NULL); + ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String")); + CHECK(stringClass.get() != NULL); + ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer")); + CHECK(integerClass.get() != NULL); + ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long")); + CHECK(longClass.get() != NULL); + ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float")); + CHECK(floatClass.get() != NULL); + ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer")); + CHECK(byteBufClass.get() != NULL); sp<AMessage> msg = new AMessage; @@ -309,7 +314,7 @@ status_t ConvertKeyValueArraysToMessage( for (jsize i = 0; i < numEntries; ++i) { jobject keyObj = env->GetObjectArrayElement(keys, i); - if (!env->IsInstanceOf(keyObj, stringClass)) { + if (!env->IsInstanceOf(keyObj, stringClass.get())) { return -EINVAL; } @@ -326,7 +331,7 @@ status_t ConvertKeyValueArraysToMessage( jobject valueObj = env->GetObjectArrayElement(values, i); - if (env->IsInstanceOf(valueObj, stringClass)) { + if (env->IsInstanceOf(valueObj, stringClass.get())) { const char *value = env->GetStringUTFChars((jstring)valueObj, NULL); if (value == NULL) { @@ -337,29 +342,37 @@ status_t ConvertKeyValueArraysToMessage( env->ReleaseStringUTFChars((jstring)valueObj, value); value = NULL; - } else if (env->IsInstanceOf(valueObj, integerClass)) { + } else if (env->IsInstanceOf(valueObj, integerClass.get())) { jmethodID intValueID = - env->GetMethodID(integerClass, "intValue", "()I"); + env->GetMethodID(integerClass.get(), "intValue", "()I"); CHECK(intValueID != NULL); jint value = env->CallIntMethod(valueObj, intValueID); msg->setInt32(key.c_str(), value); - } else if (env->IsInstanceOf(valueObj, floatClass)) { + } else if (env->IsInstanceOf(valueObj, longClass.get())) { + jmethodID longValueID = + env->GetMethodID(longClass.get(), "longValue", "()J"); + CHECK(longValueID != NULL); + + jlong value = env->CallLongMethod(valueObj, longValueID); + + msg->setInt64(key.c_str(), value); + } else if (env->IsInstanceOf(valueObj, floatClass.get())) { jmethodID floatValueID = - env->GetMethodID(floatClass, "floatValue", "()F"); + env->GetMethodID(floatClass.get(), "floatValue", "()F"); CHECK(floatValueID != NULL); jfloat value = env->CallFloatMethod(valueObj, floatValueID); msg->setFloat(key.c_str(), value); - } else if (env->IsInstanceOf(valueObj, byteBufClass)) { + } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) { jmethodID positionID = - env->GetMethodID(byteBufClass, "position", "()I"); + env->GetMethodID(byteBufClass.get(), "position", "()I"); CHECK(positionID != NULL); jmethodID limitID = - env->GetMethodID(byteBufClass, "limit", "()I"); + env->GetMethodID(byteBufClass.get(), "limit", "()I"); CHECK(limitID != NULL); jint position = env->CallIntMethod(valueObj, positionID); @@ -375,7 +388,7 @@ status_t ConvertKeyValueArraysToMessage( buffer->size()); } else { jmethodID arrayID = - env->GetMethodID(byteBufClass, "array", "()[B"); + env->GetMethodID(byteBufClass.get(), "array", "()[B"); CHECK(arrayID != NULL); jbyteArray byteArray = diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index fbd5d21..77c7966 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -26,6 +26,7 @@ #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" #include "MtpDatabase.h" #include "MtpDataPacket.h" diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index 113784e..b61b66c 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -28,6 +28,7 @@ #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" #include "private/android_filesystem_config.h" #include "MtpTypes.h" diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp index 4d77cfd..40cd06b 100644 --- a/media/jni/audioeffect/android_media_Visualizer.cpp +++ b/media/jni/audioeffect/android_media_Visualizer.cpp @@ -43,6 +43,8 @@ using namespace android; // ---------------------------------------------------------------------------- static const char* const kClassPathName = "android/media/audiofx/Visualizer"; +static const char* const kClassPeakRmsPathName = + "android/media/audiofx/Visualizer$MeasurementPeakRms"; struct fields_t { // these fields provide access from C++ to the... @@ -50,6 +52,8 @@ struct fields_t { 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 + jfieldID fidPeak; // to access Visualizer.MeasurementPeakRms.mPeak + jfieldID fidRms; // to access Visualizer.MeasurementPeakRms.mRms }; static fields_t fields; @@ -257,6 +261,14 @@ android_media_visualizer_native_init(JNIEnv *env) fields.clazzEffect = (jclass)env->NewGlobalRef(clazz); + // Get the Visualizer.MeasurementPeakRms class + clazz = env->FindClass(kClassPeakRmsPathName); + if (clazz == NULL) { + ALOGE("Can't find %s", kClassPeakRmsPathName); + return; + } + jclass clazzMeasurementPeakRms = (jclass)env->NewGlobalRef(clazz); + // Get the postEvent method fields.midPostNativeEvent = env->GetStaticMethodID( fields.clazzEffect, @@ -283,7 +295,24 @@ android_media_visualizer_native_init(JNIEnv *env) ALOGE("Can't find Visualizer.%s", "mJniData"); return; } + // fidPeak + fields.fidPeak = env->GetFieldID( + clazzMeasurementPeakRms, + "mPeak", "I"); + if (fields.fidPeak == NULL) { + ALOGE("Can't find Visualizer.MeasurementPeakRms.%s", "mPeak"); + return; + } + // fidRms + fields.fidRms = env->GetFieldID( + clazzMeasurementPeakRms, + "mRms", "I"); + if (fields.fidRms == NULL) { + ALOGE("Can't find Visualizer.MeasurementPeakRms.%s", "mPeak"); + return; + } + env->DeleteGlobalRef(clazzMeasurementPeakRms); } static void android_media_visualizer_effect_callback(int32_t event, @@ -513,6 +542,26 @@ android_media_visualizer_native_getScalingMode(JNIEnv *env, jobject thiz) } static jint +android_media_visualizer_native_setMeasurementMode(JNIEnv *env, jobject thiz, jint mode) +{ + Visualizer* lpVisualizer = getVisualizer(env, thiz); + if (lpVisualizer == NULL) { + return VISUALIZER_ERROR_NO_INIT; + } + return translateError(lpVisualizer->setMeasurementMode(mode)); +} + +static jint +android_media_visualizer_native_getMeasurementMode(JNIEnv *env, jobject thiz) +{ + Visualizer* lpVisualizer = getVisualizer(env, thiz); + if (lpVisualizer == NULL) { + return MEASUREMENT_MODE_NONE; + } + return lpVisualizer->getMeasurementMode(); +} + +static jint android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz) { Visualizer* lpVisualizer = getVisualizer(env, thiz); @@ -560,6 +609,25 @@ android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFf } static jint +android_media_visualizer_native_getPeakRms(JNIEnv *env, jobject thiz, jobject jPeakRmsObj) +{ + Visualizer* lpVisualizer = getVisualizer(env, thiz); + if (lpVisualizer == NULL) { + return VISUALIZER_ERROR_NO_INIT; + } + int32_t measurements[2]; + jint status = translateError( + lpVisualizer->getIntMeasurements(MEASUREMENT_MODE_PEAK_RMS, + 2, measurements)); + if (status == VISUALIZER_SUCCESS) { + // measurement worked, write the values to the java object + env->SetIntField(jPeakRmsObj, fields.fidPeak, measurements[MEASUREMENT_IDX_PEAK]); + env->SetIntField(jPeakRmsObj, fields.fidRms, measurements[MEASUREMENT_IDX_RMS]); + } + return status; +} + +static jint android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft) { Visualizer* lpVisualizer = getVisualizer(env, thiz); @@ -606,9 +674,13 @@ static JNINativeMethod gMethods[] = { {"native_getCaptureSize", "()I", (void *)android_media_visualizer_native_getCaptureSize}, {"native_setScalingMode", "(I)I", (void *)android_media_visualizer_native_setScalingMode}, {"native_getScalingMode", "()I", (void *)android_media_visualizer_native_getScalingMode}, + {"native_setMeasurementMode","(I)I", (void *)android_media_visualizer_native_setMeasurementMode}, + {"native_getMeasurementMode","()I", (void *)android_media_visualizer_native_getMeasurementMode}, {"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_getPeakRms", "(Landroid/media/audiofx/Visualizer$MeasurementPeakRms;)I", + (void *)android_media_visualizer_native_getPeakRms}, {"native_setPeriodicCapture","(IZZ)I",(void *)android_media_setPeriodicCapture}, }; diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp index 4982a47..d8099dd 100644 --- a/media/jni/mediaeditor/VideoEditorClasses.cpp +++ b/media/jni/mediaeditor/VideoEditorClasses.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define LOG_TAG "VideoEditorClasses" #include <VideoEditorClasses.h> #include <VideoEditorJava.h> diff --git a/media/jni/mediaeditor/VideoEditorJava.cpp b/media/jni/mediaeditor/VideoEditorJava.cpp index bcf9099..fde0fb5 100644 --- a/media/jni/mediaeditor/VideoEditorJava.cpp +++ b/media/jni/mediaeditor/VideoEditorJava.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "VideoEditorJava" + #include <VideoEditorClasses.h> #include <VideoEditorJava.h> #include <VideoEditorLogging.h> diff --git a/media/jni/mediaeditor/VideoEditorLogging.h b/media/jni/mediaeditor/VideoEditorLogging.h index 479d8b6..1f1228a 100644 --- a/media/jni/mediaeditor/VideoEditorLogging.h +++ b/media/jni/mediaeditor/VideoEditorLogging.h @@ -17,6 +17,16 @@ #ifndef VIDEO_EDITOR_LOGGING_H #define VIDEO_EDITOR_LOGGING_H +#ifndef LOG_TAG +#error "No LOG_TAG defined!" +#endif + +/* + * This file is used as a proxy for cutils/log.h. Include cutils/log.h here to + * avoid relying on import ordering. + */ +#include <cutils/log.h> + //#define VIDEOEDIT_LOGGING_ENABLED #define VIDEOEDIT_LOG_INDENTATION (3) diff --git a/media/jni/mediaeditor/VideoEditorOsal.cpp b/media/jni/mediaeditor/VideoEditorOsal.cpp index a8c08ac..c12b1f5 100644 --- a/media/jni/mediaeditor/VideoEditorOsal.cpp +++ b/media/jni/mediaeditor/VideoEditorOsal.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "VideoEditorOsal" + #include <VideoEditorJava.h> #include <VideoEditorLogging.h> #include <VideoEditorOsal.h> diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp index c8fb263..2f8e357 100644 --- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp +++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "VideoEditorPropertiesMain" + #include <dlfcn.h> #include <stdio.h> #include <unistd.h> diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk index 5835b9f..ed8d7c1 100644 --- a/media/jni/soundpool/Android.mk +++ b/media/jni/soundpool/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - android_media_SoundPool.cpp + android_media_SoundPool_SoundPoolImpl.cpp LOCAL_SHARED_LIBRARIES := \ liblog \ diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp index 9658856..2604850 100644 --- a/media/jni/soundpool/android_media_SoundPool.cpp +++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp @@ -39,9 +39,9 @@ static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) { // ---------------------------------------------------------------------------- static int -android_media_SoundPool_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority) +android_media_SoundPool_SoundPoolImpl_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority) { - ALOGV("android_media_SoundPool_load_URL"); + ALOGV("android_media_SoundPool_SoundPoolImpl_load_URL"); SoundPool *ap = MusterSoundPool(env, thiz); if (path == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); @@ -54,10 +54,10 @@ android_media_SoundPool_load_URL(JNIEnv *env, jobject thiz, jstring path, jint p } static int -android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor, +android_media_SoundPool_SoundPoolImpl_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length, jint priority) { - ALOGV("android_media_SoundPool_load_FD"); + ALOGV("android_media_SoundPool_SoundPoolImpl_load_FD"); SoundPool *ap = MusterSoundPool(env, thiz); if (ap == NULL) return 0; return ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor), @@ -65,104 +65,104 @@ android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescripto } static bool -android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) { - ALOGV("android_media_SoundPool_unload\n"); +android_media_SoundPool_SoundPoolImpl_unload(JNIEnv *env, jobject thiz, jint sampleID) { + ALOGV("android_media_SoundPool_SoundPoolImpl_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, +android_media_SoundPool_SoundPoolImpl_play(JNIEnv *env, jobject thiz, jint sampleID, jfloat leftVolume, jfloat rightVolume, jint priority, jint loop, jfloat rate) { - ALOGV("android_media_SoundPool_play\n"); + ALOGV("android_media_SoundPool_SoundPoolImpl_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) +android_media_SoundPool_SoundPoolImpl_pause(JNIEnv *env, jobject thiz, jint channelID) { - ALOGV("android_media_SoundPool_pause"); + ALOGV("android_media_SoundPool_SoundPoolImpl_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) +android_media_SoundPool_SoundPoolImpl_resume(JNIEnv *env, jobject thiz, jint channelID) { - ALOGV("android_media_SoundPool_resume"); + ALOGV("android_media_SoundPool_SoundPoolImpl_resume"); SoundPool *ap = MusterSoundPool(env, thiz); if (ap == NULL) return; ap->resume(channelID); } static void -android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz) +android_media_SoundPool_SoundPoolImpl_autoPause(JNIEnv *env, jobject thiz) { - ALOGV("android_media_SoundPool_autoPause"); + ALOGV("android_media_SoundPool_SoundPoolImpl_autoPause"); SoundPool *ap = MusterSoundPool(env, thiz); if (ap == NULL) return; ap->autoPause(); } static void -android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz) +android_media_SoundPool_SoundPoolImpl_autoResume(JNIEnv *env, jobject thiz) { - ALOGV("android_media_SoundPool_autoResume"); + ALOGV("android_media_SoundPool_SoundPoolImpl_autoResume"); SoundPool *ap = MusterSoundPool(env, thiz); if (ap == NULL) return; ap->autoResume(); } static void -android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID) +android_media_SoundPool_SoundPoolImpl_stop(JNIEnv *env, jobject thiz, jint channelID) { - ALOGV("android_media_SoundPool_stop"); + ALOGV("android_media_SoundPool_SoundPoolImpl_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, +android_media_SoundPool_SoundPoolImpl_setVolume(JNIEnv *env, jobject thiz, jint channelID, float leftVolume, float rightVolume) { - ALOGV("android_media_SoundPool_setVolume"); + ALOGV("android_media_SoundPool_SoundPoolImpl_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, +android_media_SoundPool_SoundPoolImpl_setPriority(JNIEnv *env, jobject thiz, jint channelID, int priority) { - ALOGV("android_media_SoundPool_setPriority"); + ALOGV("android_media_SoundPool_SoundPoolImpl_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, +android_media_SoundPool_SoundPoolImpl_setLoop(JNIEnv *env, jobject thiz, jint channelID, int loop) { - ALOGV("android_media_SoundPool_setLoop"); + ALOGV("android_media_SoundPool_SoundPoolImpl_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, +android_media_SoundPool_SoundPoolImpl_setRate(JNIEnv *env, jobject thiz, jint channelID, float rate) { - ALOGV("android_media_SoundPool_setRate"); + ALOGV("android_media_SoundPool_SoundPoolImpl_setRate"); SoundPool *ap = MusterSoundPool(env, thiz); if (ap == NULL) return; ap->setRate(channelID, rate); @@ -176,9 +176,9 @@ static void android_media_callback(SoundPoolEvent event, SoundPool* soundPool, v } static jint -android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality) +android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality) { - ALOGV("android_media_SoundPool_native_setup"); + ALOGV("android_media_SoundPool_SoundPoolImpl_native_setup"); SoundPool *ap = new SoundPool(maxChannels, (audio_stream_type_t) streamType, srcQuality); if (ap == NULL) { return -1; @@ -194,9 +194,9 @@ android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, } static void -android_media_SoundPool_release(JNIEnv *env, jobject thiz) +android_media_SoundPool_SoundPoolImpl_release(JNIEnv *env, jobject thiz) { - ALOGV("android_media_SoundPool_release"); + ALOGV("android_media_SoundPool_SoundPoolImpl_release"); SoundPool *ap = MusterSoundPool(env, thiz); if (ap != NULL) { @@ -219,67 +219,67 @@ android_media_SoundPool_release(JNIEnv *env, jobject thiz) static JNINativeMethod gMethods[] = { { "_load", "(Ljava/lang/String;I)I", - (void *)android_media_SoundPool_load_URL + (void *)android_media_SoundPool_SoundPoolImpl_load_URL }, { "_load", "(Ljava/io/FileDescriptor;JJI)I", - (void *)android_media_SoundPool_load_FD + (void *)android_media_SoundPool_SoundPoolImpl_load_FD }, { "unload", "(I)Z", - (void *)android_media_SoundPool_unload + (void *)android_media_SoundPool_SoundPoolImpl_unload }, { "play", "(IFFIIF)I", - (void *)android_media_SoundPool_play + (void *)android_media_SoundPool_SoundPoolImpl_play }, { "pause", "(I)V", - (void *)android_media_SoundPool_pause + (void *)android_media_SoundPool_SoundPoolImpl_pause }, { "resume", "(I)V", - (void *)android_media_SoundPool_resume + (void *)android_media_SoundPool_SoundPoolImpl_resume }, { "autoPause", "()V", - (void *)android_media_SoundPool_autoPause + (void *)android_media_SoundPool_SoundPoolImpl_autoPause }, { "autoResume", "()V", - (void *)android_media_SoundPool_autoResume + (void *)android_media_SoundPool_SoundPoolImpl_autoResume }, { "stop", "(I)V", - (void *)android_media_SoundPool_stop + (void *)android_media_SoundPool_SoundPoolImpl_stop }, { "setVolume", "(IFF)V", - (void *)android_media_SoundPool_setVolume + (void *)android_media_SoundPool_SoundPoolImpl_setVolume }, { "setPriority", "(II)V", - (void *)android_media_SoundPool_setPriority + (void *)android_media_SoundPool_SoundPoolImpl_setPriority }, { "setLoop", "(II)V", - (void *)android_media_SoundPool_setLoop + (void *)android_media_SoundPool_SoundPoolImpl_setLoop }, { "setRate", "(IF)V", - (void *)android_media_SoundPool_setRate + (void *)android_media_SoundPool_SoundPoolImpl_setRate }, { "native_setup", "(Ljava/lang/Object;III)I", - (void*)android_media_SoundPool_native_setup + (void*)android_media_SoundPool_SoundPoolImpl_native_setup }, { "release", "()V", - (void*)android_media_SoundPool_release + (void*)android_media_SoundPool_SoundPoolImpl_release } }; -static const char* const kClassPathName = "android/media/SoundPool"; +static const char* const kClassPathName = "android/media/SoundPool$SoundPoolImpl"; jint JNI_OnLoad(JavaVM* vm, void* reserved) { @@ -301,14 +301,14 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "I"); if (fields.mNativeContext == NULL) { - ALOGE("Can't find SoundPool.mNativeContext"); + ALOGE("Can't find SoundPoolImpl.mNativeContext"); goto bail; } fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V"); if (fields.mPostEvent == NULL) { - ALOGE("Can't find android/media/SoundPool.postEventFromNative"); + ALOGE("Can't find android/media/SoundPoolImpl.postEventFromNative"); goto bail; } |