diff options
| -rw-r--r-- | media/java/android/media/ImageReader.java | 52 | ||||
| -rw-r--r-- | media/java/android/media/ImageWriter.java | 6 | ||||
| -rw-r--r-- | media/jni/android_media_ImageReader.cpp | 1100 | ||||
| -rw-r--r-- | media/jni/android_media_ImageWriter.cpp | 351 | ||||
| -rw-r--r-- | media/jni/android_media_Utils.cpp | 388 | ||||
| -rw-r--r-- | media/jni/android_media_Utils.h | 29 |
6 files changed, 761 insertions, 1165 deletions
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index c08f4bf0eb19..81cc03561d3f 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -335,7 +335,6 @@ public class ImageReader implements AutoCloseable { switch (status) { case ACQUIRE_SUCCESS: - si.createSurfacePlanes(); si.mIsImageValid = true; case ACQUIRE_NO_BUFS: case ACQUIRE_MAX_IMAGES: @@ -693,7 +692,7 @@ public class ImageReader implements AutoCloseable { width = ImageReader.this.getWidth(); break; default: - width = nativeGetWidth(mFormat); + width = nativeGetWidth(); } return width; } @@ -709,7 +708,7 @@ public class ImageReader implements AutoCloseable { height = ImageReader.this.getHeight(); break; default: - height = nativeGetHeight(mFormat); + height = nativeGetHeight(); } return height; } @@ -729,6 +728,10 @@ public class ImageReader implements AutoCloseable { @Override public Plane[] getPlanes() { throwISEIfImageIsInvalid(); + + if (mPlanes == null) { + mPlanes = nativeCreatePlanes(ImageReader.this.mNumPlanes, ImageReader.this.mFormat); + } // Shallow copy is fine. return mPlanes.clone(); } @@ -766,7 +769,8 @@ public class ImageReader implements AutoCloseable { } private void clearSurfacePlanes() { - if (mIsImageValid) { + // Image#getPlanes may not be called before the image is closed. + if (mIsImageValid && mPlanes != null) { for (int i = 0; i < mPlanes.length; i++) { if (mPlanes[i] != null) { mPlanes[i].clearBuffer(); @@ -776,32 +780,25 @@ public class ImageReader implements AutoCloseable { } } - private void createSurfacePlanes() { - mPlanes = new SurfacePlane[ImageReader.this.mNumPlanes]; - for (int i = 0; i < ImageReader.this.mNumPlanes; i++) { - mPlanes[i] = nativeCreatePlane(i, ImageReader.this.mFormat); - } - } private class SurfacePlane extends android.media.Image.Plane { - // SurfacePlane instance is created by native code when a new SurfaceImage is created - private SurfacePlane(int index, int rowStride, int pixelStride) { - mIndex = index; + // SurfacePlane instance is created by native code when SurfaceImage#getPlanes() is + // called + private SurfacePlane(int rowStride, int pixelStride, ByteBuffer buffer) { mRowStride = rowStride; mPixelStride = pixelStride; + mBuffer = buffer; + /** + * Set the byteBuffer order according to host endianness (native + * order), otherwise, the byteBuffer order defaults to + * ByteOrder.BIG_ENDIAN. + */ + mBuffer.order(ByteOrder.nativeOrder()); } @Override public ByteBuffer getBuffer() { - SurfaceImage.this.throwISEIfImageIsInvalid(); - if (mBuffer != null) { - return mBuffer; - } else { - mBuffer = SurfaceImage.this.nativeImageGetBuffer(mIndex, - ImageReader.this.mFormat); - // Set the byteBuffer order according to host endianness (native order), - // otherwise, the byteBuffer order defaults to ByteOrder.BIG_ENDIAN. - return mBuffer.order(ByteOrder.nativeOrder()); - } + throwISEIfImageIsInvalid(); + return mBuffer; } @Override @@ -837,7 +834,6 @@ public class ImageReader implements AutoCloseable { mBuffer = null; } - final private int mIndex; final private int mPixelStride; final private int mRowStride; @@ -860,10 +856,10 @@ public class ImageReader implements AutoCloseable { // If this image is detached from the ImageReader. private AtomicBoolean mIsDetached = new AtomicBoolean(false); - private synchronized native ByteBuffer nativeImageGetBuffer(int idx, int readerFormat); - private synchronized native SurfacePlane nativeCreatePlane(int idx, int readerFormat); - private synchronized native int nativeGetWidth(int format); - private synchronized native int nativeGetHeight(int format); + private synchronized native SurfacePlane[] nativeCreatePlanes(int numPlanes, + int readerFormat); + private synchronized native int nativeGetWidth(); + private synchronized native int nativeGetHeight(); private synchronized native int nativeGetFormat(int readerFormat); } diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index 851d43674458..83a4f17ffccb 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -748,8 +748,8 @@ public class ImageWriter implements AutoCloseable { final private int mPixelStride; final private int mRowStride; - // SurfacePlane instance is created by native code when a new - // SurfaceImage is created + // SurfacePlane instance is created by native code when SurfaceImage#getPlanes() is + // called private SurfacePlane(int rowStride, int pixelStride, ByteBuffer buffer) { mRowStride = rowStride; mPixelStride = pixelStride; @@ -795,7 +795,7 @@ public class ImageWriter implements AutoCloseable { } - // this will create the SurfacePlane object and fill the information + // Create the SurfacePlane object and fill the information private synchronized native SurfacePlane[] nativeCreatePlanes(int numPlanes, int writerFmt); private synchronized native int nativeGetWidth(); diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 9e90a19f00cc..c3993ae20183 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -16,6 +16,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "ImageReader_JNI" +#include "android_media_Utils.h" #include <utils/Log.h> #include <utils/misc.h> #include <utils/List.h> @@ -23,7 +24,6 @@ #include <cstdio> -#include <gui/CpuConsumer.h> #include <gui/BufferItemConsumer.h> #include <gui/Surface.h> #include <camera3.h> @@ -37,8 +37,6 @@ #include <stdint.h> #include <inttypes.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 "mNativeBuffer" #define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID "mTimestamp" @@ -47,9 +45,6 @@ using namespace android; -enum { - IMAGE_READER_MAX_NUM_PLANES = 3, -}; enum { ACQUIRE_SUCCESS = 0, @@ -65,6 +60,7 @@ static struct { static struct { jfieldID mNativeBuffer; jfieldID mTimestamp; + jfieldID mPlanes; } gSurfaceImageClassInfo; static struct { @@ -89,21 +85,12 @@ public: virtual void onFrameAvailable(const BufferItem& item); - CpuConsumer::LockedBuffer* getLockedBuffer(); - void returnLockedBuffer(CpuConsumer::LockedBuffer* buffer); - - BufferItem* getOpaqueBuffer(); - void returnOpaqueBuffer(BufferItem* buffer); + BufferItem* getBufferItem(); + void returnBufferItem(BufferItem* buffer); - void setCpuConsumer(const sp<CpuConsumer>& consumer) { mConsumer = consumer; } - CpuConsumer* getCpuConsumer() { return mConsumer.get(); } - void setOpaqueConsumer(const sp<BufferItemConsumer>& consumer) { mOpaqueConsumer = consumer; } - BufferItemConsumer* getOpaqueConsumer() { return mOpaqueConsumer.get(); } - // This is the only opaque format exposed in the ImageFormat public API. - // Note that we do support CPU access for HAL_PIXEL_FORMAT_RAW_OPAQUE - // (ImageFormat#RAW_PRIVATE) so it doesn't count as opaque here. - bool isOpaque() { return mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; } + void setBufferConsumer(const sp<BufferItemConsumer>& consumer) { mConsumer = consumer; } + BufferItemConsumer* getBufferConsumer() { return mConsumer.get(); } void setProducer(const sp<IGraphicBufferProducer>& producer) { mProducer = producer; } IGraphicBufferProducer* getProducer() { return mProducer.get(); } @@ -124,10 +111,8 @@ private: static JNIEnv* getJNIEnv(bool* needsDetach); static void detachJNI(); - List<CpuConsumer::LockedBuffer*> mBuffers; - List<BufferItem*> mOpaqueBuffers; - sp<CpuConsumer> mConsumer; - sp<BufferItemConsumer> mOpaqueConsumer; + List<BufferItem*> mBuffers; + sp<BufferItemConsumer> mConsumer; sp<IGraphicBufferProducer> mProducer; jobject mWeakThiz; jclass mClazz; @@ -140,12 +125,14 @@ private: JNIImageReaderContext::JNIImageReaderContext(JNIEnv* env, jobject weakThiz, jclass clazz, int maxImages) : mWeakThiz(env->NewGlobalRef(weakThiz)), - mClazz((jclass)env->NewGlobalRef(clazz)) { + mClazz((jclass)env->NewGlobalRef(clazz)), + mFormat(0), + mDataSpace(HAL_DATASPACE_UNKNOWN), + mWidth(-1), + mHeight(-1) { for (int i = 0; i < maxImages; i++) { - CpuConsumer::LockedBuffer *buffer = new CpuConsumer::LockedBuffer; - BufferItem* opaqueBuffer = new BufferItem; + BufferItem* buffer = new BufferItem; mBuffers.push_back(buffer); - mOpaqueBuffers.push_back(opaqueBuffer); } } @@ -174,36 +161,21 @@ void JNIImageReaderContext::detachJNI() { } } -CpuConsumer::LockedBuffer* JNIImageReaderContext::getLockedBuffer() { +BufferItem* JNIImageReaderContext::getBufferItem() { 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; + // Return a BufferItem pointer and remove it from the list + List<BufferItem*>::iterator it = mBuffers.begin(); + BufferItem* buffer = *it; mBuffers.erase(it); return buffer; } -void JNIImageReaderContext::returnLockedBuffer(CpuConsumer::LockedBuffer* buffer) { +void JNIImageReaderContext::returnBufferItem(BufferItem* buffer) { mBuffers.push_back(buffer); } -BufferItem* JNIImageReaderContext::getOpaqueBuffer() { - if (mOpaqueBuffers.empty()) { - return NULL; - } - // Return an opaque buffer pointer and remove it from the list - List<BufferItem*>::iterator it = mOpaqueBuffers.begin(); - BufferItem* buffer = *it; - mOpaqueBuffers.erase(it); - return buffer; -} - -void JNIImageReaderContext::returnOpaqueBuffer(BufferItem* buffer) { - mOpaqueBuffers.push_back(buffer); -} - JNIImageReaderContext::~JNIImageReaderContext() { bool needsDetach = false; JNIEnv* env = getJNIEnv(&needsDetach); @@ -217,25 +189,15 @@ JNIImageReaderContext::~JNIImageReaderContext() { detachJNI(); } - // Delete LockedBuffers - for (List<CpuConsumer::LockedBuffer *>::iterator it = mBuffers.begin(); + // Delete buffer items. + for (List<BufferItem *>::iterator it = mBuffers.begin(); it != mBuffers.end(); it++) { delete *it; } - // Delete opaque buffers - for (List<BufferItem *>::iterator it = mOpaqueBuffers.begin(); - it != mOpaqueBuffers.end(); it++) { - delete *it; - } - - mBuffers.clear(); if (mConsumer != 0) { mConsumer.clear(); } - if (mOpaqueConsumer != 0) { - mOpaqueConsumer.clear(); - } } void JNIImageReaderContext::onFrameAvailable(const BufferItem& /*item*/) @@ -257,11 +219,6 @@ void JNIImageReaderContext::onFrameAvailable(const BufferItem& /*item*/) extern "C" { -static bool isFormatOpaque(int format) { - // Only treat IMPLEMENTATION_DEFINED as an opaque format for now. - return format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; -} - static JNIImageReaderContext* ImageReader_getContext(JNIEnv* env, jobject thiz) { JNIImageReaderContext *ctx; @@ -270,24 +227,6 @@ static JNIImageReaderContext* ImageReader_getContext(JNIEnv* env, jobject thiz) 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; - } - - if (ctx->isOpaque()) { - jniThrowException(env, "java/lang/IllegalStateException", - "Opaque ImageReader doesn't support this method"); - return NULL; - } - - return ctx->getCpuConsumer(); -} - static IGraphicBufferProducer* ImageReader_getProducer(JNIEnv* env, jobject thiz) { ALOGV("%s:", __FUNCTION__); @@ -315,411 +254,7 @@ static void ImageReader_setNativeContext(JNIEnv* env, reinterpret_cast<jlong>(ctx.get())); } -static CpuConsumer::LockedBuffer* Image_getLockedBuffer(JNIEnv* env, jobject image) -{ - return reinterpret_cast<CpuConsumer::LockedBuffer*>( - env->GetLongField(image, gSurfaceImageClassInfo.mNativeBuffer)); -} - -static void Image_setBuffer(JNIEnv* env, jobject thiz, - const CpuConsumer::LockedBuffer* buffer) -{ - env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer, reinterpret_cast<jlong>(buffer)); -} - -static void Image_setOpaqueBuffer(JNIEnv* env, jobject thiz, - const BufferItem* buffer) -{ - env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer, reinterpret_cast<jlong>(buffer)); -} - -static uint32_t Image_getJpegSize(CpuConsumer::LockedBuffer* buffer, bool usingRGBAOverride) -{ - ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!"); - uint32_t size = 0; - uint32_t width = buffer->width; - uint8_t* jpegBuffer = buffer->data; - - if (usingRGBAOverride) { - width = (buffer->width + buffer->stride * (buffer->height - 1)) * 4; - } - - // 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) { - /* - * This is a problem because not including the JPEG header - * means that in certain rare situations a regular JPEG blob - * will be misidentified as having a header, in which case - * we will get a garbage size value. - */ - ALOGW("%s: No JPEG header detected, defaulting to size=width=%d", - __FUNCTION__, width); - size = width; - } - - return size; -} - -static bool usingRGBAToJpegOverride(int32_t bufferFormat, int32_t readerCtxFormat) { - return readerCtxFormat == HAL_PIXEL_FORMAT_BLOB && bufferFormat == HAL_PIXEL_FORMAT_RGBA_8888; -} - -static int32_t applyFormatOverrides(int32_t bufferFormat, int32_t readerCtxFormat) -{ - // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW - // write limitations for some platforms (b/17379185). - if (usingRGBAToJpegOverride(bufferFormat, readerCtxFormat)) { - return HAL_PIXEL_FORMAT_BLOB; - } - return bufferFormat; -} - -static void Image_getLockedBufferInfo(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx, - uint8_t **base, uint32_t *size, int32_t readerFormat) -{ - 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->flexFormat; - - bool usingRGBAOverride = usingRGBAToJpegOverride(fmt, readerFormat); - fmt = applyFormatOverrides(fmt, readerFormat); - switch (fmt) { - case HAL_PIXEL_FORMAT_YCbCr_420_888: - pData = - (idx == 0) ? - buffer->data : - (idx == 1) ? - buffer->dataCb : - buffer->dataCr; - // only map until last pixel - if (idx == 0) { - dataSize = buffer->stride * (buffer->height - 1) + buffer->width; - } else { - dataSize = buffer->chromaStride * (buffer->height / 2 - 1) + - buffer->chromaStep * (buffer->width / 2 - 1) + 1; - } - break; - // NV21 - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - cr = buffer->data + (buffer->stride * buffer->height); - cb = cr + 1; - // only map until last pixel - ySize = buffer->width * (buffer->height - 1) + buffer->width; - cSize = buffer->width * (buffer->height / 2 - 1) + buffer->width - 1; - - 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: - bytesPerPixel = 2; - // 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 * bytesPerPixel; - 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 one but got %d", buffer->height); - - pData = buffer->data; - dataSize = Image_getJpegSize(buffer, usingRGBAOverride); - break; - case HAL_PIXEL_FORMAT_RAW16: - // Single plane 16bpp bayer data. - bytesPerPixel = 2; - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - pData = buffer->data; - dataSize = buffer->stride * buffer->height * bytesPerPixel; - break; - case HAL_PIXEL_FORMAT_RAW_OPAQUE: - // Used for RAW_OPAQUE data, height must be 1, width == size, single plane. - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - ALOG_ASSERT(buffer->height == 1, - "RAW_PRIVATE should has height value one but got %d", buffer->height); - pData = buffer->data; - dataSize = buffer->width; - break; - case HAL_PIXEL_FORMAT_RAW10: - // Single plane 10bpp bayer data. - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - LOG_ALWAYS_FATAL_IF(buffer->width % 4, - "Width is not multiple of 4 %d", buffer->width); - LOG_ALWAYS_FATAL_IF(buffer->height % 2, - "Height is not even %d", buffer->height); - LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 10 / 8), - "stride (%d) should be at least %d", - buffer->stride, buffer->width * 10 / 8); - pData = buffer->data; - dataSize = buffer->stride * buffer->height; - break; - case HAL_PIXEL_FORMAT_RAW12: - // Single plane 10bpp bayer data. - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - LOG_ALWAYS_FATAL_IF(buffer->width % 4, - "Width is not multiple of 4 %d", buffer->width); - LOG_ALWAYS_FATAL_IF(buffer->height % 2, - "Height is not even %d", buffer->height); - LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 12 / 8), - "stride (%d) should be at least %d", - buffer->stride, buffer->width * 12 / 8); - pData = buffer->data; - dataSize = buffer->stride * 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, - int32_t halReaderFormat) -{ - 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->flexFormat; - - fmt = applyFormatOverrides(fmt, halReaderFormat); - - 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 = 1; - break; - case HAL_PIXEL_FORMAT_YV12: - pixelStride = 1; - break; - case HAL_PIXEL_FORMAT_BLOB: - case HAL_PIXEL_FORMAT_RAW10: - case HAL_PIXEL_FORMAT_RAW12: - // Blob is used for JPEG data, RAW10 and RAW12 is used for 10-bit and 12-bit raw data, - // those are single plane data with pixel stride 0 since they don't really have a - // well defined pixel stride - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - pixelStride = 0; - break; - case HAL_PIXEL_FORMAT_Y16: - case HAL_PIXEL_FORMAT_RAW16: - 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; - case HAL_PIXEL_FORMAT_RAW_OPAQUE: - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - pixelStride = 0; // RAW OPAQUE doesn't have pixel stride - 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, - int32_t halReaderFormat) -{ - 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->flexFormat; - - fmt = applyFormatOverrides(fmt, halReaderFormat); - - 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: - // Blob is used for JPEG data. It is single plane and has 0 row stride and - // 0 pixel stride - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - rowStride = 0; - break; - case HAL_PIXEL_FORMAT_RAW10: - case HAL_PIXEL_FORMAT_RAW12: - // RAW10 and RAW12 are used for 10-bit and 12-bit raw data, they are single plane - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - rowStride = buffer->stride; - 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_RAW16: - // 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; - case HAL_PIXEL_FORMAT_RAW_OPAQUE: - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - rowStride = 0; // RAW OPAQUE doesn't have row stride - break; - default: - ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt); - jniThrowException(env, "java/lang/UnsupportedOperationException", - "unsupported buffer format"); - break; - } - - return rowStride; -} - -static int Image_getBufferWidth(CpuConsumer::LockedBuffer* buffer) { - if (buffer == NULL) return -1; - - if (!buffer->crop.isEmpty()) { - return buffer->crop.getWidth(); - } - return buffer->width; -} - -static int Image_getBufferHeight(CpuConsumer::LockedBuffer* buffer) { - if (buffer == NULL) return -1; - - if (!buffer->crop.isEmpty()) { - return buffer->crop.getHeight(); - } - return buffer->height; -} - -// --------------------------Methods for opaque Image and ImageReader---------- - -static BufferItemConsumer* ImageReader_getOpaqueConsumer(JNIEnv* env, jobject thiz) +static BufferItemConsumer* ImageReader_getBufferConsumer(JNIEnv* env, jobject thiz) { ALOGV("%s:", __FUNCTION__); JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz); @@ -728,40 +263,21 @@ static BufferItemConsumer* ImageReader_getOpaqueConsumer(JNIEnv* env, jobject th return NULL; } - if (!ctx->isOpaque()) { - jniThrowException(env, "java/lang/IllegalStateException", - "Non-opaque ImageReader doesn't support this method"); - } + return ctx->getBufferConsumer(); +} - return ctx->getOpaqueConsumer(); +static void Image_setBufferItem(JNIEnv* env, jobject thiz, + const BufferItem* buffer) +{ + env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer, reinterpret_cast<jlong>(buffer)); } -static BufferItem* Image_getOpaqueBuffer(JNIEnv* env, jobject image) +static BufferItem* Image_getBufferItem(JNIEnv* env, jobject image) { return reinterpret_cast<BufferItem*>( env->GetLongField(image, gSurfaceImageClassInfo.mNativeBuffer)); } -static int Image_getOpaqueBufferWidth(BufferItem* buffer) { - if (buffer == NULL) return -1; - - if (!buffer->mCrop.isEmpty()) { - return buffer->mCrop.getWidth(); - } - return buffer->mGraphicBuffer->getWidth(); -} - -static int Image_getOpaqueBufferHeight(BufferItem* buffer) { - if (buffer == NULL) return -1; - - if (!buffer->mCrop.isEmpty()) { - return buffer->mCrop.getHeight(); - } - - return buffer->mGraphicBuffer->getHeight(); -} - - // ---------------------------------------------------------------------------- @@ -784,6 +300,11 @@ static void ImageReader_classInit(JNIEnv* env, jclass clazz) "can't find android/graphics/ImageReader.%s", ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID); + gSurfaceImageClassInfo.mPlanes = env->GetFieldID( + imageClazz, "mPlanes", "[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;"); + LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mPlanes == NULL, + "can't find android/media/ImageReader$ReaderSurfaceImage.mPlanes"); + gImageReaderClassInfo.mNativeContext = env->GetFieldID( clazz, ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID, "J"); LOG_ALWAYS_FATAL_IF(gImageReaderClassInfo.mNativeContext == NULL, @@ -800,7 +321,7 @@ static void ImageReader_classInit(JNIEnv* env, jclass clazz) // 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"); + "(Landroid/media/ImageReader$SurfaceImage;IILjava/nio/ByteBuffer;)V"); LOG_ALWAYS_FATAL_IF(gSurfacePlaneClassInfo.ctor == NULL, "Can not find SurfacePlane constructor"); } @@ -831,81 +352,52 @@ static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, sp<IGraphicBufferProducer> gbProducer; sp<IGraphicBufferConsumer> gbConsumer; BufferQueue::createBufferQueue(&gbProducer, &gbConsumer); - sp<ConsumerBase> consumer; - sp<CpuConsumer> cpuConsumer; - sp<BufferItemConsumer> opaqueConsumer; + sp<BufferItemConsumer> bufferConsumer; String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d", width, height, format, maxImages, getpid(), createProcessUniqueId()); + uint32_t consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN; + if (isFormatOpaque(nativeFormat)) { // Use the SW_READ_NEVER usage to tell producer that this format is not for preview or video // encoding. The only possibility will be ZSL output. - opaqueConsumer = - new BufferItemConsumer(gbConsumer, GRALLOC_USAGE_SW_READ_NEVER, maxImages, - /*controlledByApp*/true); - if (opaqueConsumer == NULL) { - jniThrowRuntimeException(env, "Failed to allocate native opaque consumer"); - return; - } - ctx->setOpaqueConsumer(opaqueConsumer); - opaqueConsumer->setName(consumerName); - consumer = opaqueConsumer; - } else { - cpuConsumer = new CpuConsumer(gbConsumer, maxImages, /*controlledByApp*/true); - // TODO: throw dvm exOutOfMemoryError? - if (cpuConsumer == NULL) { - jniThrowRuntimeException(env, "Failed to allocate native CpuConsumer"); - return; - } - ctx->setCpuConsumer(cpuConsumer); - cpuConsumer->setName(consumerName); - consumer = cpuConsumer; + consumerUsage = GRALLOC_USAGE_SW_READ_NEVER; } + bufferConsumer = new BufferItemConsumer(gbConsumer, consumerUsage, maxImages, + /*controlledByApp*/true); + if (bufferConsumer == nullptr) { + jniThrowExceptionFmt(env, "java/lang/RuntimeException", + "Failed to allocate native buffer consumer for format 0x%x", nativeFormat); + return; + } + ctx->setBufferConsumer(bufferConsumer); + bufferConsumer->setName(consumerName); ctx->setProducer(gbProducer); - consumer->setFrameAvailableListener(ctx); + bufferConsumer->setFrameAvailableListener(ctx); ImageReader_setNativeContext(env, thiz, ctx); ctx->setBufferFormat(nativeFormat); ctx->setBufferDataspace(nativeDataspace); ctx->setBufferWidth(width); ctx->setBufferHeight(height); - // Set the width/height/format/dataspace to the CpuConsumer - // TODO: below code can be simplified once b/19977701 is fixed. - if (isFormatOpaque(nativeFormat)) { - res = opaqueConsumer->setDefaultBufferSize(width, height); - if (res != OK) { - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to set opaque consumer buffer size"); - return; - } - res = opaqueConsumer->setDefaultBufferFormat(nativeFormat); - if (res != OK) { - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to set opaque consumer buffer format"); - } - res = opaqueConsumer->setDefaultBufferDataSpace(nativeDataspace); - if (res != OK) { - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to set opaque consumer buffer dataSpace"); - } - } else { - res = cpuConsumer->setDefaultBufferSize(width, height); - if (res != OK) { - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to set CpuConsumer buffer size"); - return; - } - res = cpuConsumer->setDefaultBufferFormat(nativeFormat); - if (res != OK) { - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to set CpuConsumer buffer format"); - } - res = cpuConsumer->setDefaultBufferDataSpace(nativeDataspace); - if (res != OK) { - jniThrowException(env, "java/lang/IllegalStateException", - "Failed to set CpuConsumer buffer dataSpace"); - } + // Set the width/height/format/dataspace to the bufferConsumer. + res = bufferConsumer->setDefaultBufferSize(width, height); + if (res != OK) { + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Failed to set buffer consumer default size (%dx%d) for format 0x%x", + width, height, nativeFormat); + return; + } + res = bufferConsumer->setDefaultBufferFormat(nativeFormat); + if (res != OK) { + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Failed to set buffer consumer default format 0x%x", nativeFormat); + } + res = bufferConsumer->setDefaultBufferDataSpace(nativeDataspace); + if (res != OK) { + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Failed to set buffer consumer default dataSpace 0x%x", nativeDataspace); } } @@ -919,12 +411,8 @@ static void ImageReader_close(JNIEnv* env, jobject thiz) return; } - ConsumerBase* consumer = NULL; - if (ctx->isOpaque()) { - consumer = ImageReader_getOpaqueConsumer(env, thiz); - } else { - consumer = ImageReader_getCpuConsumer(env, thiz); - } + BufferItemConsumer* consumer = NULL; + consumer = ImageReader_getBufferConsumer(env, thiz); if (consumer != NULL) { consumer->abandon(); @@ -933,6 +421,39 @@ static void ImageReader_close(JNIEnv* env, jobject thiz) ImageReader_setNativeContext(env, thiz, NULL); } +static sp<Fence> Image_unlockIfLocked(JNIEnv* env, jobject image) { + ALOGV("%s", __FUNCTION__); + BufferItem* buffer = Image_getBufferItem(env, image); + if (buffer == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Image is not initialized"); + return Fence::NO_FENCE; + } + + // Is locked? + bool wasBufferLocked = false; + jobject planes = NULL; + if (!isFormatOpaque(buffer->mGraphicBuffer->getPixelFormat())) { + planes = env->GetObjectField(image, gSurfaceImageClassInfo.mPlanes); + } + wasBufferLocked = (planes != NULL); + if (wasBufferLocked) { + status_t res = OK; + int fenceFd = -1; + if (wasBufferLocked) { + res = buffer->mGraphicBuffer->unlockAsync(&fenceFd); + if (res != OK) { + jniThrowRuntimeException(env, "unlock buffer failed"); + return Fence::NO_FENCE; + } + } + sp<Fence> releaseFence = new Fence(fenceFd); + return releaseFence; + ALOGV("Successfully unlocked the image"); + } + return Fence::NO_FENCE; +} + static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image) { ALOGV("%s:", __FUNCTION__); @@ -942,174 +463,124 @@ static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image) return; } - if (ctx->isOpaque()) { - BufferItemConsumer* opaqueConsumer = ctx->getOpaqueConsumer(); - BufferItem* opaqueBuffer = Image_getOpaqueBuffer(env, image); - opaqueConsumer->releaseBuffer(*opaqueBuffer); // Not using fence for now. - Image_setOpaqueBuffer(env, image, NULL); - ctx->returnOpaqueBuffer(opaqueBuffer); - ALOGV("%s: Opaque Image has been released", __FUNCTION__); - } else { - CpuConsumer* consumer = ctx->getCpuConsumer(); - CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, image); - if (!buffer) { - // Release an already closed image is harmless. - return; - } - consumer->unlockBuffer(*buffer); - Image_setBuffer(env, image, NULL); - ctx->returnLockedBuffer(buffer); - ALOGV("%s: Image (format: 0x%x) has been released", __FUNCTION__, ctx->getBufferFormat()); + BufferItemConsumer* bufferConsumer = ctx->getBufferConsumer(); + BufferItem* buffer = Image_getBufferItem(env, image); + if (buffer == nullptr) { + // Release an already closed image is harmless. + return; } + + sp<Fence> releaseFence = Image_unlockIfLocked(env, image); + bufferConsumer->releaseBuffer(*buffer, releaseFence); + Image_setBufferItem(env, image, NULL); + ctx->returnBufferItem(buffer); + ALOGV("%s: Image (format: 0x%x) has been released", __FUNCTION__, ctx->getBufferFormat()); } -static jint ImageReader_opaqueImageSetup(JNIEnv* env, JNIImageReaderContext* ctx, jobject image) { +static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) { ALOGV("%s:", __FUNCTION__); - if (ctx == NULL || !ctx->isOpaque()) { - jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); + JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); + if (ctx == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "ImageReader is not initialized or was already closed"); return -1; } - BufferItemConsumer* opaqueConsumer = ctx->getOpaqueConsumer(); - BufferItem* buffer = ctx->getOpaqueBuffer(); + BufferItemConsumer* bufferConsumer = ctx->getBufferConsumer(); + BufferItem* buffer = ctx->getBufferItem(); if (buffer == NULL) { ALOGW("Unable to acquire a buffer item, very likely client tried to acquire more than" " maxImages buffers"); return ACQUIRE_MAX_IMAGES; } - status_t res = opaqueConsumer->acquireBuffer(buffer, 0); + status_t res = bufferConsumer->acquireBuffer(buffer, 0); if (res != OK) { - ctx->returnOpaqueBuffer(buffer); - if (res == INVALID_OPERATION) { - // Max number of images were already acquired. - ALOGE("%s: Max number of buffers allowed are already acquired : %s (%d)", - __FUNCTION__, strerror(-res), res); - return ACQUIRE_MAX_IMAGES; - } else { - ALOGE("%s: Acquire image failed with error: %s (%d)", - __FUNCTION__, strerror(-res), res); - return ACQUIRE_NO_BUFFERS; - } - } - - // Set SurfaceImage instance member variables - Image_setOpaqueBuffer(env, image, buffer); - env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp, - static_cast<jlong>(buffer->mTimestamp)); - - return ACQUIRE_SUCCESS; -} - -static jint ImageReader_lockedImageSetup(JNIEnv* env, JNIImageReaderContext* ctx, jobject image) { - 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) { - ctx->returnLockedBuffer(buffer); - if (res != BAD_VALUE /*no buffers*/) { - if (res == NOT_ENOUGH_DATA) { + ctx->returnBufferItem(buffer); + if (res != BufferQueue::NO_BUFFER_AVAILABLE) { + if (res == INVALID_OPERATION) { + // Max number of images were already acquired. + ALOGE("%s: Max number of buffers allowed are already acquired : %s (%d)", + __FUNCTION__, strerror(-res), res); 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); + ALOGE("%s: Acquire image failed with some unknown error: %s (%d)", + __FUNCTION__, strerror(-res), res); + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Unknown error (%d) when we tried to acquire an image.", + res); + return ACQUIRE_NO_BUFFERS; } } + // This isn't really an error case, as the application may acquire buffer at any time. return ACQUIRE_NO_BUFFERS; } - if (buffer->flexFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP) { - jniThrowException(env, "java/lang/UnsupportedOperationException", - "NV21 format is not supported by ImageReader"); - return -1; - } + // Add some extra checks for non-opaque formats. + if (!isFormatOpaque(ctx->getBufferFormat())) { + // Check if the left-top corner of the crop rect is origin, we currently assume this point is + // zero, will revisit this once this assumption turns out problematic. + Point lt = buffer->mCrop.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 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. + int outputWidth = getBufferWidth(buffer); + int outputHeight = getBufferHeight(buffer); + + int imgReaderFmt = ctx->getBufferFormat(); + int imageReaderWidth = ctx->getBufferWidth(); + int imageReaderHeight = ctx->getBufferHeight(); + int bufferFormat = buffer->mGraphicBuffer->getPixelFormat(); + if ((bufferFormat != HAL_PIXEL_FORMAT_BLOB) && (imgReaderFmt != HAL_PIXEL_FORMAT_BLOB) && + (imageReaderWidth != outputWidth || imageReaderHeight != outputHeight)) { + ALOGV("%s: Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", + __FUNCTION__, outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); + } + if (imgReaderFmt != bufferFormat) { + if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && + isPossiblyYUV(bufferFormat)) { + // Treat formats that are compatible with flexible YUV + // (HAL_PIXEL_FORMAT_YCbCr_420_888) as HAL_PIXEL_FORMAT_YCbCr_420_888. + ALOGV("%s: Treat buffer format to 0x%x as HAL_PIXEL_FORMAT_YCbCr_420_888", + __FUNCTION__, bufferFormat); + } else if (imgReaderFmt == HAL_PIXEL_FORMAT_BLOB && + bufferFormat == HAL_PIXEL_FORMAT_RGBA_8888) { + // Using HAL_PIXEL_FORMAT_RGBA_8888 Gralloc buffers containing JPEGs to get around + // SW write limitations for (b/17379185). + ALOGV("%s: Receiving JPEG in HAL_PIXEL_FORMAT_RGBA_8888 buffer.", __FUNCTION__); + } else { + // Return the buffer to the queue. No need to provide fence, as this buffer wasn't + // used anywhere yet. + bufferConsumer->releaseBuffer(*buffer); + ctx->returnBufferItem(buffer); + + // Throw exception + ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x", + bufferFormat, ctx->getBufferFormat()); + String8 msg; + msg.appendFormat("The producer output buffer format 0x%x doesn't " + "match the ImageReader's configured buffer format 0x%x.", + bufferFormat, ctx->getBufferFormat()); + jniThrowException(env, "java/lang/UnsupportedOperationException", + msg.string()); + return -1; + } + } - // Check if the producer buffer configurations match what ImageReader configured. - int outputWidth = Image_getBufferWidth(buffer); - int outputHeight = Image_getBufferHeight(buffer); - - int imgReaderFmt = ctx->getBufferFormat(); - int imageReaderWidth = ctx->getBufferWidth(); - int imageReaderHeight = ctx->getBufferHeight(); - if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) && (imgReaderFmt != HAL_PIXEL_FORMAT_BLOB) && - (imageReaderWidth != outputWidth || imageReaderHeight != outputHeight)) { - ALOGV("%s: Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", - __FUNCTION__, outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); } - int bufFmt = buffer->format; - if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888) { - bufFmt = buffer->flexFormat; - } - if (imgReaderFmt != bufFmt) { - if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && (bufFmt == - HAL_PIXEL_FORMAT_YCrCb_420_SP || bufFmt == HAL_PIXEL_FORMAT_YV12)) { - // Special casing for when producer switches to a format compatible with flexible YUV - // (HAL_PIXEL_FORMAT_YCbCr_420_888). - ctx->setBufferFormat(bufFmt); - ALOGD("%s: Overriding buffer format YUV_420_888 to %x.", __FUNCTION__, bufFmt); - } else if (imgReaderFmt == HAL_PIXEL_FORMAT_BLOB && bufFmt == HAL_PIXEL_FORMAT_RGBA_8888) { - // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW - // write limitations for (b/17379185). - ALOGD("%s: Receiving JPEG in HAL_PIXEL_FORMAT_RGBA_8888 buffer.", __FUNCTION__); - } else { - // 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.", - bufFmt, ctx->getBufferFormat()); - jniThrowException(env, "java/lang/UnsupportedOperationException", - msg.string()); - return -1; - } - } // Set SurfaceImage instance member variables - Image_setBuffer(env, image, buffer); + Image_setBufferItem(env, image, buffer); env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp, - static_cast<jlong>(buffer->timestamp)); + static_cast<jlong>(buffer->mTimestamp)); return ACQUIRE_SUCCESS; } -static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) { - ALOGV("%s:", __FUNCTION__); - JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); - if (ctx == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", - "ImageReader is not initialized or was already closed"); - return -1; - } - - if (ctx->isOpaque()) { - return ImageReader_opaqueImageSetup(env, ctx, image); - } else { - return ImageReader_lockedImageSetup(env, ctx, image); - } -} - static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) { ALOGV("%s:", __FUNCTION__); JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); @@ -1118,29 +589,23 @@ static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) { return -1; } - status_t res = OK; - if (!ctx->isOpaque()) { - // TODO: Non-Opaque format detach is not implemented yet. - jniThrowRuntimeException(env, - "nativeDetachImage is not implemented yet for non-opaque format !!!"); - return -1; - } - - BufferItemConsumer* opaqueConsumer = ctx->getOpaqueConsumer(); - BufferItem* opaqueBuffer = Image_getOpaqueBuffer(env, image); - if (!opaqueBuffer) { + BufferItemConsumer* bufferConsumer = ctx->getBufferConsumer(); + BufferItem* buffer = Image_getBufferItem(env, image); + if (!buffer) { ALOGE( - "Opaque Image already released and can not be detached from ImageReader!!!"); + "Image already released and can not be detached from ImageReader!!!"); jniThrowException(env, "java/lang/IllegalStateException", - "Opaque Image detach from ImageReader failed: buffer was already released"); + "Image detach from ImageReader failed: buffer was already released"); return -1; } - res = opaqueConsumer->detachBuffer(opaqueBuffer->mSlot); + status_t res = OK; + Image_unlockIfLocked(env, image); + res = bufferConsumer->detachBuffer(buffer->mSlot); if (res != OK) { - ALOGE("Opaque Image detach failed: %s (%d)!!!", strerror(-res), res); + ALOGE("Image detach failed: %s (%d)!!!", strerror(-res), res); jniThrowRuntimeException(env, - "nativeDetachImage failed for opaque image!!!"); + "nativeDetachImage failed for image!!!"); return res; } return OK; @@ -1152,7 +617,7 @@ static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) IGraphicBufferProducer* gbp = ImageReader_getProducer(env, thiz); if (gbp == NULL) { - jniThrowRuntimeException(env, "CpuConsumer is uninitialized"); + jniThrowRuntimeException(env, "Buffer consumer is uninitialized"); return NULL; } @@ -1160,98 +625,115 @@ static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) return android_view_Surface_createFromIGraphicBufferProducer(env, gbp); } -static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx, int readerFormat) -{ - int rowStride, pixelStride; - PublicFormat publicReaderFormat = static_cast<PublicFormat>(readerFormat); - int halReaderFormat = android_view_Surface_mapPublicFormatToHalFormat( - publicReaderFormat); - - ALOGV("%s: buffer index: %d", __FUNCTION__, idx); - if (isFormatOpaque(halReaderFormat)) { +static void Image_getLockedImage(JNIEnv* env, jobject thiz, LockedImage *image) { + ALOGV("%s", __FUNCTION__); + BufferItem* buffer = Image_getBufferItem(env, thiz); + if (buffer == NULL) { jniThrowException(env, "java/lang/IllegalStateException", - "Opaque images from Opaque ImageReader do not have any planes"); - return NULL; + "Image is not initialized"); + return; } - CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); - - ALOG_ASSERT(buffer != NULL); - if (buffer == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", "Image was released"); + status_t res = lockImageFromBuffer(buffer, + GRALLOC_USAGE_SW_READ_OFTEN, buffer->mFence->dup(), image); + if (res != OK) { + jniThrowExceptionFmt(env, "java/lang/RuntimeException", + "lock buffer failed for format 0x%x", + buffer->mGraphicBuffer->getPixelFormat()); + return; } - rowStride = Image_imageGetRowStride(env, buffer, idx, halReaderFormat); - pixelStride = Image_imageGetPixelStride(env, buffer, idx, halReaderFormat); + // Carry over some fields from BufferItem. + image->crop = buffer->mCrop; + image->transform = buffer->mTransform; + image->scalingMode = buffer->mScalingMode; + image->timestamp = buffer->mTimestamp; + image->dataSpace = buffer->mDataSpace; + image->frameNumber = buffer->mFrameNumber; + + ALOGV("%s: Successfully locked the image", __FUNCTION__); + // crop, transform, scalingMode, timestamp, and frameNumber should be set by producer, + // and we don't set them here. +} - jobject surfPlaneObj = env->NewObject(gSurfacePlaneClassInfo.clazz, - gSurfacePlaneClassInfo.ctor, thiz, idx, rowStride, pixelStride); +static void Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx, + int32_t writerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) { + ALOGV("%s", __FUNCTION__); - return surfPlaneObj; + status_t res = getLockedImageInfo(buffer, idx, writerFormat, base, size, + pixelStride, rowStride); + if (res != OK) { + jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", + "Pixel format: 0x%x is unsupported", buffer->flexFormat); + } } -static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx, int readerFormat) +static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, + int numPlanes, int readerFormat) { - uint8_t *base = NULL; - uint32_t size = 0; - jobject byteBuffer; - PublicFormat readerPublicFormat = static_cast<PublicFormat>(readerFormat); - int readerHalFormat = android_view_Surface_mapPublicFormatToHalFormat( - readerPublicFormat); + ALOGV("%s: create SurfacePlane array with size %d", __FUNCTION__, numPlanes); + int rowStride = 0; + int pixelStride = 0; + uint8_t *pData = NULL; + uint32_t dataSize = 0; + jobject byteBuffer = NULL; - ALOGV("%s: buffer index: %d", __FUNCTION__, idx); + PublicFormat publicReaderFormat = static_cast<PublicFormat>(readerFormat); + int halReaderFormat = android_view_Surface_mapPublicFormatToHalFormat( + publicReaderFormat); - if (isFormatOpaque(readerHalFormat)) { - jniThrowException(env, "java/lang/IllegalStateException", - "Opaque images from Opaque ImageReader do not have any plane"); + if (isFormatOpaque(halReaderFormat) && numPlanes > 0) { + String8 msg; + msg.appendFormat("Format 0x%x is opaque, thus not writable, the number of planes (%d)" + " must be 0", halReaderFormat, numPlanes); + jniThrowException(env, "java/lang/IllegalArgumentException", msg.string()); return NULL; } - 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, readerHalFormat); - - if (size > static_cast<uint32_t>(INT32_MAX)) { - // Byte buffer have 'int capacity', so check the range - jniThrowExceptionFmt(env, "java/lang/IllegalStateException", - "Size too large for bytebuffer capacity %" PRIu32, size); + jobjectArray surfacePlanes = env->NewObjectArray(numPlanes, gSurfacePlaneClassInfo.clazz, + /*initial_element*/NULL); + if (surfacePlanes == NULL) { + jniThrowRuntimeException(env, "Failed to create SurfacePlane arrays," + " probably out of memory"); return NULL; } + if (isFormatOpaque(halReaderFormat)) { + // Return 0 element surface array. + return surfacePlanes; + } + + LockedImage lockedImg = LockedImage(); + Image_getLockedImage(env, thiz, &lockedImg); + // Create all SurfacePlanes + for (int i = 0; i < numPlanes; i++) { + Image_getLockedImageInfo(env, &lockedImg, i, halReaderFormat, + &pData, &dataSize, &pixelStride, &rowStride); + byteBuffer = env->NewDirectByteBuffer(pData, dataSize); + if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) { + jniThrowException(env, "java/lang/IllegalStateException", + "Failed to allocate ByteBuffer"); + return NULL; + } - byteBuffer = env->NewDirectByteBuffer(base, size); - // TODO: throw dvm exOutOfMemoryError? - if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) { - jniThrowException(env, "java/lang/IllegalStateException", "Failed to allocate ByteBuffer"); + // Finally, create this SurfacePlane. + jobject surfacePlane = env->NewObject(gSurfacePlaneClassInfo.clazz, + gSurfacePlaneClassInfo.ctor, thiz, rowStride, pixelStride, byteBuffer); + env->SetObjectArrayElement(surfacePlanes, i, surfacePlane); } - return byteBuffer; + return surfacePlanes; } -static jint Image_getWidth(JNIEnv* env, jobject thiz, jint format) +static jint Image_getWidth(JNIEnv* env, jobject thiz) { - if (isFormatOpaque(format)) { - BufferItem* opaqueBuffer = Image_getOpaqueBuffer(env, thiz); - return Image_getOpaqueBufferWidth(opaqueBuffer); - } else { - CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); - return Image_getBufferWidth(buffer); - } + BufferItem* buffer = Image_getBufferItem(env, thiz); + return getBufferWidth(buffer); } -static jint Image_getHeight(JNIEnv* env, jobject thiz, jint format) +static jint Image_getHeight(JNIEnv* env, jobject thiz) { - if (isFormatOpaque(format)) { - BufferItem* opaqueBuffer = Image_getOpaqueBuffer(env, thiz); - return Image_getOpaqueBufferHeight(opaqueBuffer); - } else { - CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); - return Image_getBufferHeight(buffer); - } + BufferItem* buffer = Image_getBufferItem(env, thiz); + return getBufferHeight(buffer); } static jint Image_getFormat(JNIEnv* env, jobject thiz, jint readerFormat) @@ -1260,20 +742,21 @@ static jint Image_getFormat(JNIEnv* env, jobject thiz, jint readerFormat) // Assuming opaque reader produce opaque images. return static_cast<jint>(PublicFormat::PRIVATE); } else { - CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); + BufferItem* buffer = Image_getBufferItem(env, thiz); int readerHalFormat = android_view_Surface_mapPublicFormatToHalFormat( static_cast<PublicFormat>(readerFormat)); - int32_t fmt = applyFormatOverrides(buffer->flexFormat, readerHalFormat); + int32_t fmt = applyFormatOverrides( + buffer->mGraphicBuffer->getPixelFormat(), readerHalFormat); // Override the image format to HAL_PIXEL_FORMAT_YCbCr_420_888 if the actual format is // NV21 or YV12. This could only happen when the Gralloc HAL version is v0.1 thus doesn't // support lockycbcr(), the CpuConsumer need to use the lock() method in the // lockNextBuffer() call. For Gralloc HAL v0.2 or newer, this format should already be // overridden to HAL_PIXEL_FORMAT_YCbCr_420_888 for the flexible YUV compatible formats. - if (fmt == HAL_PIXEL_FORMAT_YCrCb_420_SP || fmt == HAL_PIXEL_FORMAT_YV12) { + if (isPossiblyYUV(fmt)) { fmt = HAL_PIXEL_FORMAT_YCbCr_420_888; } PublicFormat publicFmt = android_view_Surface_mapHalFormatDataspaceToPublicFormat( - fmt, buffer->dataSpace); + fmt, buffer->mDataSpace); return static_cast<jint>(publicFmt); } } @@ -1293,11 +776,10 @@ static const JNINativeMethod gImageReaderMethods[] = { }; static const JNINativeMethod gImageMethods[] = { - {"nativeImageGetBuffer", "(II)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer }, - {"nativeCreatePlane", "(II)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;", - (void*)Image_createSurfacePlane }, - {"nativeGetWidth", "(I)I", (void*)Image_getWidth }, - {"nativeGetHeight", "(I)I", (void*)Image_getHeight }, + {"nativeCreatePlanes", "(II)[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;", + (void*)Image_createSurfacePlanes }, + {"nativeGetWidth", "()I", (void*)Image_getWidth }, + {"nativeGetHeight", "()I", (void*)Image_getHeight }, {"nativeGetFormat", "(I)I", (void*)Image_getFormat }, }; diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index f50da8547af2..d5d9fc94bf29 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -16,34 +16,28 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "ImageWriter_JNI" +#include "android_media_Utils.h" + #include <utils/Log.h> #include <utils/String8.h> #include <gui/IProducerListener.h> #include <gui/Surface.h> -#include <gui/CpuConsumer.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_view_Surface.h> #include <camera3.h> - #include <jni.h> #include <JNIHelp.h> #include <stdint.h> #include <inttypes.h> -#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) ) - #define IMAGE_BUFFER_JNI_ID "mNativeBuffer" // ---------------------------------------------------------------------------- using namespace android; -enum { - IMAGE_WRITER_MAX_NUM_PLANES = 3, -}; - static struct { jmethodID postEventFromNative; jfieldID mWriterFormat; @@ -60,8 +54,6 @@ static struct { jmethodID ctor; } gSurfacePlaneClassInfo; -typedef CpuConsumer::LockedBuffer LockedImage; - // ---------------------------------------------------------------------------- class JNIImageWriterContext : public BnProducerListener { @@ -181,13 +173,11 @@ extern "C" { // -------------------------------Private method declarations-------------- -static bool isPossiblyYUV(PixelFormat format); static void Image_setNativeContext(JNIEnv* env, jobject thiz, sp<GraphicBuffer> buffer, int fenceFd); static void Image_getNativeContext(JNIEnv* env, jobject thiz, GraphicBuffer** buffer, int* fenceFd); static void Image_unlockIfLocked(JNIEnv* env, jobject thiz); -static bool isFormatOpaque(int format); // --------------------------ImageWriter methods--------------------------------------- @@ -672,28 +662,6 @@ static jint Image_getHeight(JNIEnv* env, jobject thiz) { return buffer->getHeight(); } -// 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; - 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); - - // Translate the JPEG to BLOB for camera purpose. - if (format == jpegFormat) { - format = HAL_PIXEL_FORMAT_BLOB; - } - - return format; -} - static jint Image_getFormat(JNIEnv* env, jobject thiz) { ALOGV("%s", __FUNCTION__); GraphicBuffer* buffer; @@ -704,7 +672,10 @@ static jint Image_getFormat(JNIEnv* env, jobject thiz) { return 0; } - return Image_getPixelFormat(env, buffer->getPixelFormat()); + // ImageWriter doesn't support data space yet, assuming it is unknown. + PublicFormat publicFmt = android_view_Surface_mapHalFormatDataspaceToPublicFormat( + buffer->getPixelFormat(), HAL_DATASPACE_UNKNOWN); + return static_cast<jint>(publicFmt); } static void Image_setFenceFd(JNIEnv* env, jobject thiz, int fenceFd) { @@ -723,272 +694,34 @@ static void Image_getLockedImage(JNIEnv* env, jobject thiz, LockedImage *image) return; } - void* pData = NULL; - android_ycbcr ycbcr = android_ycbcr(); - status_t res; - int format = Image_getFormat(env, thiz); - int flexFormat = format; - if (isPossiblyYUV(format)) { - // ImageWriter doesn't use crop by itself, app sets it, use the no crop version. - res = buffer->lockAsyncYCbCr(GRALLOC_USAGE_SW_WRITE_OFTEN, &ycbcr, fenceFd); - // Clear the fenceFd as it is already consumed by lock call. - Image_setFenceFd(env, thiz, /*fenceFd*/-1); - if (res != OK) { - jniThrowRuntimeException(env, "lockAsyncYCbCr failed for YUV buffer"); - return; - } - pData = ycbcr.y; - flexFormat = HAL_PIXEL_FORMAT_YCbCr_420_888; - } - - // lockAsyncYCbCr for YUV is unsuccessful. - if (pData == NULL) { - res = buffer->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, &pData, fenceFd); - if (res != OK) { - jniThrowRuntimeException(env, "lockAsync failed"); - return; - } + // ImageWriter doesn't use crop by itself, app sets it, use the no crop version. + const Rect noCrop(buffer->width, buffer->height); + status_t res = lockImageFromBuffer( + buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, noCrop, fenceFd, image); + // Clear the fenceFd as it is already consumed by lock call. + Image_setFenceFd(env, thiz, /*fenceFd*/-1); + if (res != OK) { + jniThrowExceptionFmt(env, "java/lang/RuntimeException", + "lock buffer failed for format 0x%x", + buffer->getPixelFormat()); + return; } - image->data = reinterpret_cast<uint8_t*>(pData); - image->width = buffer->getWidth(); - image->height = buffer->getHeight(); - image->format = format; - image->flexFormat = flexFormat; - image->stride = (ycbcr.y != NULL) ? static_cast<uint32_t>(ycbcr.ystride) : buffer->getStride(); - - image->dataCb = reinterpret_cast<uint8_t*>(ycbcr.cb); - image->dataCr = reinterpret_cast<uint8_t*>(ycbcr.cr); - image->chromaStride = static_cast<uint32_t>(ycbcr.cstride); - image->chromaStep = static_cast<uint32_t>(ycbcr.chroma_step); - ALOGV("Successfully locked the image"); + ALOGV("%s: Successfully locked the image", __FUNCTION__); // crop, transform, scalingMode, timestamp, and frameNumber should be set by producer, // and we don't set them here. } -static bool usingRGBAToJpegOverride(int32_t bufferFormat, int32_t writerCtxFormat) { - return writerCtxFormat == HAL_PIXEL_FORMAT_BLOB && bufferFormat == HAL_PIXEL_FORMAT_RGBA_8888; -} - -static int32_t applyFormatOverrides(int32_t bufferFormat, int32_t writerCtxFormat) -{ - // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW - // write limitations for some platforms (b/17379185). - if (usingRGBAToJpegOverride(bufferFormat, writerCtxFormat)) { - return HAL_PIXEL_FORMAT_BLOB; - } - return bufferFormat; -} - -static uint32_t Image_getJpegSize(LockedImage* buffer, bool usingRGBAOverride) { - ALOGV("%s", __FUNCTION__); - ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!"); - uint32_t size = 0; - uint32_t width = buffer->width; - uint8_t* jpegBuffer = buffer->data; - - if (usingRGBAOverride) { - width = (buffer->width + buffer->stride * (buffer->height - 1)) * 4; - } - - // 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) { - /* - * This is a problem because not including the JPEG header - * means that in certain rare situations a regular JPEG blob - * will be misidentified as having a header, in which case - * we will get a garbage size value. - */ - ALOGW("%s: No JPEG header detected, defaulting to size=width=%d", - __FUNCTION__, width); - size = width; - } - - return size; -} - static void Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx, int32_t writerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) { ALOGV("%s", __FUNCTION__); - ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!"); - ALOG_ASSERT(base != NULL, "base is NULL!!!"); - ALOG_ASSERT(size != NULL, "size is NULL!!!"); - ALOG_ASSERT(pixelStride != NULL, "pixelStride is NULL!!!"); - ALOG_ASSERT(rowStride != NULL, "rowStride is NULL!!!"); - ALOG_ASSERT((idx < IMAGE_WRITER_MAX_NUM_PLANES) && (idx >= 0)); - - ALOGV("%s: buffer: %p", __FUNCTION__, buffer); - - uint32_t dataSize, ySize, cSize, cStride; - uint32_t pStride = 0, rStride = 0; - uint8_t *cb, *cr; - uint8_t *pData = NULL; - int bytesPerPixel = 0; - - dataSize = ySize = cSize = cStride = 0; - int32_t fmt = buffer->flexFormat; - - bool usingRGBAOverride = usingRGBAToJpegOverride(fmt, writerFormat); - fmt = applyFormatOverrides(fmt, writerFormat); - switch (fmt) { - case HAL_PIXEL_FORMAT_YCbCr_420_888: - pData = - (idx == 0) ? - buffer->data : - (idx == 1) ? - buffer->dataCb : - buffer->dataCr; - // only map until last pixel - if (idx == 0) { - pStride = 1; - rStride = buffer->stride; - dataSize = buffer->stride * (buffer->height - 1) + buffer->width; - } else { - pStride = buffer->chromaStep; - rStride = buffer->chromaStride; - dataSize = buffer->chromaStride * (buffer->height / 2 - 1) + - buffer->chromaStep * (buffer->width / 2 - 1) + 1; - } - break; - // NV21 - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - cr = buffer->data + (buffer->stride * buffer->height); - cb = cr + 1; - // only map until last pixel - ySize = buffer->width * (buffer->height - 1) + buffer->width; - cSize = buffer->width * (buffer->height / 2 - 1) + buffer->width - 1; - - pData = - (idx == 0) ? - buffer->data : - (idx == 1) ? - cb: - cr; - - dataSize = (idx == 0) ? ySize : cSize; - pStride = (idx == 0) ? 1 : 2; - rStride = buffer->width; - 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; - pStride = 1; - rStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16); - 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; - pStride = 1; - rStride = buffer->stride; - break; - case HAL_PIXEL_FORMAT_Y16: - bytesPerPixel = 2; - // 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 * bytesPerPixel; - pStride = bytesPerPixel; - rStride = buffer->stride * 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, usingRGBAOverride); - pStride = bytesPerPixel; - rowStride = 0; - break; - case HAL_PIXEL_FORMAT_RAW16: - // Single plane 16bpp bayer data. - bytesPerPixel = 2; - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - pData = buffer->data; - dataSize = buffer->stride * buffer->height * bytesPerPixel; - pStride = bytesPerPixel; - rStride = buffer->stride * 2; - break; - case HAL_PIXEL_FORMAT_RAW10: - // Single plane 10bpp bayer data. - ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); - LOG_ALWAYS_FATAL_IF(buffer->width % 4, - "Width is not multiple of 4 %d", buffer->width); - LOG_ALWAYS_FATAL_IF(buffer->height % 2, - "Height is not even %d", buffer->height); - LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 10 / 8), - "stride (%d) should be at least %d", - buffer->stride, buffer->width * 10 / 8); - pData = buffer->data; - dataSize = buffer->stride * buffer->height; - pStride = 0; - rStride = buffer->stride; - 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; - pStride = bytesPerPixel; - rStride = buffer->stride * 4; - 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; - pStride = bytesPerPixel; - rStride = buffer->stride * 2; - 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; - pStride = bytesPerPixel; - rStride = buffer->stride * 3; - break; - default: - jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", - "Pixel format: 0x%x is unsupported", fmt); - break; - } - *base = pData; - *size = dataSize; - *pixelStride = pStride; - *rowStride = rStride; + status_t res = getLockedImageInfo(buffer, idx, writerFormat, base, size, + pixelStride, rowStride); + if (res != OK) { + jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", + "Pixel format: 0x%x is unsupported", buffer->flexFormat); + } } static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, @@ -1024,7 +757,8 @@ static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, Image_getLockedImage(env, thiz, &lockedImg); // Create all SurfacePlanes - writerFormat = Image_getPixelFormat(env, writerFormat); + PublicFormat publicWriterFormat = static_cast<PublicFormat>(writerFormat); + writerFormat = android_view_Surface_mapPublicFormatToHalFormat(publicWriterFormat); for (int i = 0; i < numPlanes; i++) { Image_getLockedImageInfo(env, &lockedImg, i, writerFormat, &pData, &dataSize, &pixelStride, &rowStride); @@ -1044,39 +778,6 @@ static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, return surfacePlanes; } -// -------------------------------Private convenience methods-------------------- - -static bool isFormatOpaque(int format) { - // Only treat IMPLEMENTATION_DEFINED as an opaque format for now. - return format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; -} - -static bool isPossiblyYUV(PixelFormat format) { - switch (static_cast<int>(format)) { - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - case HAL_PIXEL_FORMAT_RGB_888: - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_BGRA_8888: - case HAL_PIXEL_FORMAT_Y8: - case HAL_PIXEL_FORMAT_Y16: - case HAL_PIXEL_FORMAT_RAW16: - case HAL_PIXEL_FORMAT_RAW10: - case HAL_PIXEL_FORMAT_RAW_OPAQUE: - case HAL_PIXEL_FORMAT_BLOB: - case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: - return false; - - case HAL_PIXEL_FORMAT_YV12: - case HAL_PIXEL_FORMAT_YCbCr_420_888: - case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_I: - default: - return true; - } -} - } // extern "C" // ---------------------------------------------------------------------------- diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index 9c4f7c4b2aff..f4296617d734 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -26,6 +26,8 @@ #include <nativehelper/ScopedLocalRef.h> +#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) ) + namespace android { FileStream::FileStream(const int fd) @@ -509,5 +511,391 @@ status_t ConvertKeyValueArraysToMessage( return OK; } +// -----------Utility functions used by ImageReader/Writer JNI----------------- + +enum { + IMAGE_MAX_NUM_PLANES = 3, +}; + +bool usingRGBAToJpegOverride(int32_t imageFormat, + int32_t containerFormat) { + return containerFormat == HAL_PIXEL_FORMAT_BLOB && imageFormat == HAL_PIXEL_FORMAT_RGBA_8888; +} + +int32_t applyFormatOverrides(int32_t imageFormat, int32_t containerFormat) { + // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW + // write limitations for some platforms (b/17379185). + if (usingRGBAToJpegOverride(imageFormat, containerFormat)) { + return HAL_PIXEL_FORMAT_BLOB; + } + return containerFormat; +} + +bool isFormatOpaque(int format) { + // This is the only opaque format exposed in the ImageFormat public API. + // Note that we do support CPU access for HAL_PIXEL_FORMAT_RAW_OPAQUE + // (ImageFormat#RAW_PRIVATE) so it doesn't count as opaque here. + return format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; +} + +bool isPossiblyYUV(PixelFormat format) { + switch (static_cast<int>(format)) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGB_888: + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_Y8: + case HAL_PIXEL_FORMAT_Y16: + case HAL_PIXEL_FORMAT_RAW16: + case HAL_PIXEL_FORMAT_RAW10: + case HAL_PIXEL_FORMAT_RAW_OPAQUE: + case HAL_PIXEL_FORMAT_BLOB: + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + return false; + + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_YCbCr_420_888: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + default: + return true; + } +} + +uint32_t Image_getJpegSize(LockedImage* buffer, bool usingRGBAOverride) { + ALOGV("%s", __FUNCTION__); + LOG_ALWAYS_FATAL_IF(buffer == NULL, "Input buffer is NULL!!!"); + uint32_t size = 0; + uint32_t width = buffer->width; + uint8_t* jpegBuffer = buffer->data; + + if (usingRGBAOverride) { + width = (buffer->width + buffer->stride * (buffer->height - 1)) * 4; + } + + // 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) { + /* + * This is a problem because not including the JPEG header + * means that in certain rare situations a regular JPEG blob + * will be mis-identified as having a header, in which case + * we will get a garbage size value. + */ + ALOGW("%s: No JPEG header detected, defaulting to size=width=%d", + __FUNCTION__, width); + size = width; + } + + return size; +} + +status_t getLockedImageInfo(LockedImage* buffer, int idx, + int32_t containerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) { + ALOGV("%s", __FUNCTION__); + LOG_ALWAYS_FATAL_IF(buffer == NULL, "Input buffer is NULL!!!"); + LOG_ALWAYS_FATAL_IF(base == NULL, "base is NULL!!!"); + LOG_ALWAYS_FATAL_IF(size == NULL, "size is NULL!!!"); + LOG_ALWAYS_FATAL_IF(pixelStride == NULL, "pixelStride is NULL!!!"); + LOG_ALWAYS_FATAL_IF(rowStride == NULL, "rowStride is NULL!!!"); + LOG_ALWAYS_FATAL_IF((idx >= IMAGE_MAX_NUM_PLANES) || (idx < 0), "idx (%d) is illegal", idx); + + ALOGV("%s: buffer: %p", __FUNCTION__, buffer); + + uint32_t dataSize, ySize, cSize, cStride; + uint32_t pStride = 0, rStride = 0; + uint8_t *cb, *cr; + uint8_t *pData = NULL; + int bytesPerPixel = 0; + + dataSize = ySize = cSize = cStride = 0; + int32_t fmt = buffer->flexFormat; + + bool usingRGBAOverride = usingRGBAToJpegOverride(fmt, containerFormat); + fmt = applyFormatOverrides(fmt, containerFormat); + switch (fmt) { + case HAL_PIXEL_FORMAT_YCbCr_420_888: + pData = + (idx == 0) ? + buffer->data : + (idx == 1) ? + buffer->dataCb : + buffer->dataCr; + // only map until last pixel + if (idx == 0) { + pStride = 1; + rStride = buffer->stride; + dataSize = buffer->stride * (buffer->height - 1) + buffer->width; + } else { + pStride = buffer->chromaStep; + rStride = buffer->chromaStride; + dataSize = buffer->chromaStride * (buffer->height / 2 - 1) + + buffer->chromaStep * (buffer->width / 2 - 1) + 1; + } + break; + // NV21 + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + cr = buffer->data + (buffer->stride * buffer->height); + cb = cr + 1; + // only map until last pixel + ySize = buffer->width * (buffer->height - 1) + buffer->width; + cSize = buffer->width * (buffer->height / 2 - 1) + buffer->width - 1; + + pData = + (idx == 0) ? + buffer->data : + (idx == 1) ? + cb: + cr; + + dataSize = (idx == 0) ? ySize : cSize; + pStride = (idx == 0) ? 1 : 2; + rStride = buffer->width; + 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; + pStride = 1; + rStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16); + break; + case HAL_PIXEL_FORMAT_Y8: + // Single plane, 8bpp. + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + + pData = buffer->data; + dataSize = buffer->stride * buffer->height; + pStride = 1; + rStride = buffer->stride; + break; + case HAL_PIXEL_FORMAT_Y16: + bytesPerPixel = 2; + // Single plane, 16bpp, strides are specified in pixels, not in bytes + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + + pData = buffer->data; + dataSize = buffer->stride * buffer->height * bytesPerPixel; + pStride = bytesPerPixel; + rStride = buffer->stride * 2; + break; + case HAL_PIXEL_FORMAT_BLOB: + // Used for JPEG data, height must be 1, width == size, single plane. + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + LOG_ALWAYS_FATAL_IF(buffer->height != 1, + "BLOB format buffer should has height value %d", buffer->height); + + pData = buffer->data; + dataSize = Image_getJpegSize(buffer, usingRGBAOverride); + pStride = 0; + rStride = 0; + break; + case HAL_PIXEL_FORMAT_RAW16: + // Single plane 16bpp bayer data. + bytesPerPixel = 2; + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + pData = buffer->data; + dataSize = buffer->stride * buffer->height * bytesPerPixel; + pStride = bytesPerPixel; + rStride = buffer->stride * 2; + break; + case HAL_PIXEL_FORMAT_RAW_OPAQUE: + // Used for RAW_OPAQUE data, height must be 1, width == size, single plane. + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + LOG_ALWAYS_FATAL_IF(buffer->height != 1, + "RAW_PRIVATE should has height value one but got %d", buffer->height); + pData = buffer->data; + dataSize = buffer->width; + pStride = 0; // RAW OPAQUE doesn't have pixel stride + rStride = 0; // RAW OPAQUE doesn't have row stride + break; + case HAL_PIXEL_FORMAT_RAW10: + // Single plane 10bpp bayer data. + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + LOG_ALWAYS_FATAL_IF(buffer->width % 4, + "Width is not multiple of 4 %d", buffer->width); + LOG_ALWAYS_FATAL_IF(buffer->height % 2, + "Height is not even %d", buffer->height); + LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 10 / 8), + "stride (%d) should be at least %d", + buffer->stride, buffer->width * 10 / 8); + pData = buffer->data; + dataSize = buffer->stride * buffer->height; + pStride = 0; + rStride = buffer->stride; + break; + case HAL_PIXEL_FORMAT_RAW12: + // Single plane 10bpp bayer data. + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + LOG_ALWAYS_FATAL_IF(buffer->width % 4, + "Width is not multiple of 4 %d", buffer->width); + LOG_ALWAYS_FATAL_IF(buffer->height % 2, + "Height is not even %d", buffer->height); + LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 12 / 8), + "stride (%d) should be at least %d", + buffer->stride, buffer->width * 12 / 8); + pData = buffer->data; + dataSize = buffer->stride * buffer->height; + pStride = 0; + rStride = buffer->stride; + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + // Single plane, 32bpp. + bytesPerPixel = 4; + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + pData = buffer->data; + dataSize = buffer->stride * buffer->height * bytesPerPixel; + pStride = bytesPerPixel; + rStride = buffer->stride * 4; + break; + case HAL_PIXEL_FORMAT_RGB_565: + // Single plane, 16bpp. + bytesPerPixel = 2; + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + pData = buffer->data; + dataSize = buffer->stride * buffer->height * bytesPerPixel; + pStride = bytesPerPixel; + rStride = buffer->stride * 2; + break; + case HAL_PIXEL_FORMAT_RGB_888: + // Single plane, 24bpp. + bytesPerPixel = 3; + LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx); + pData = buffer->data; + dataSize = buffer->stride * buffer->height * bytesPerPixel; + pStride = bytesPerPixel; + rStride = buffer->stride * 3; + break; + default: + return BAD_VALUE; + } + + *base = pData; + *size = dataSize; + *pixelStride = pStride; + *rowStride = rStride; + + return OK; +} + +status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage, + const Rect& rect, int fenceFd, LockedImage* outputImage) { + ALOGV("%s: Try to lock the GraphicBuffer", __FUNCTION__); + + if (buffer == nullptr || outputImage == nullptr) { + ALOGE("Input BufferItem or output LockedImage is NULL!"); + return BAD_VALUE; + } + if (isFormatOpaque(buffer->getPixelFormat())) { + ALOGE("Opaque format buffer is not lockable!"); + return BAD_VALUE; + } + + void* pData = NULL; + android_ycbcr ycbcr = android_ycbcr(); + status_t res; + int format = buffer->getPixelFormat(); + int flexFormat = format; + if (isPossiblyYUV(format)) { + res = buffer->lockAsyncYCbCr(inUsage, rect, &ycbcr, fenceFd); + pData = ycbcr.y; + flexFormat = HAL_PIXEL_FORMAT_YCbCr_420_888; + } + + // lockAsyncYCbCr for YUV is unsuccessful. + if (pData == NULL) { + res = buffer->lockAsync(inUsage, rect, &pData, fenceFd); + if (res != OK) { + ALOGE("Lock buffer failed!"); + return res; + } + } + + outputImage->data = reinterpret_cast<uint8_t*>(pData); + outputImage->width = buffer->getWidth(); + outputImage->height = buffer->getHeight(); + outputImage->format = format; + outputImage->flexFormat = flexFormat; + outputImage->stride = + (ycbcr.y != NULL) ? static_cast<uint32_t>(ycbcr.ystride) : buffer->getStride(); + + outputImage->dataCb = reinterpret_cast<uint8_t*>(ycbcr.cb); + outputImage->dataCr = reinterpret_cast<uint8_t*>(ycbcr.cr); + outputImage->chromaStride = static_cast<uint32_t>(ycbcr.cstride); + outputImage->chromaStep = static_cast<uint32_t>(ycbcr.chroma_step); + ALOGV("%s: Successfully locked the image from the GraphicBuffer", __FUNCTION__); + // Crop, transform, scalingMode, timestamp, and frameNumber should be set by caller, + // and cann't be set them here. + return OK; +} + +status_t lockImageFromBuffer(BufferItem* bufferItem, uint32_t inUsage, + int fenceFd, LockedImage* outputImage) { + ALOGV("%s: Try to lock the BufferItem", __FUNCTION__); + if (bufferItem == nullptr || outputImage == nullptr) { + ALOGE("Input BufferItem or output LockedImage is NULL!"); + return BAD_VALUE; + } + + status_t res = lockImageFromBuffer(bufferItem->mGraphicBuffer, inUsage, bufferItem->mCrop, + fenceFd, outputImage); + if (res != OK) { + ALOGE("%s: lock graphic buffer failed", __FUNCTION__); + return res; + } + + outputImage->crop = bufferItem->mCrop; + outputImage->transform = bufferItem->mTransform; + outputImage->scalingMode = bufferItem->mScalingMode; + outputImage->timestamp = bufferItem->mTimestamp; + outputImage->dataSpace = bufferItem->mDataSpace; + outputImage->frameNumber = bufferItem->mFrameNumber; + ALOGV("%s: Successfully locked the image from the BufferItem", __FUNCTION__); + return OK; +} + +int getBufferWidth(BufferItem* buffer) { + if (buffer == NULL) return -1; + + if (!buffer->mCrop.isEmpty()) { + return buffer->mCrop.getWidth(); + } + + ALOGV("%s: buffer->mGraphicBuffer: %p", __FUNCTION__, buffer->mGraphicBuffer.get()); + return buffer->mGraphicBuffer->getWidth(); +} + +int getBufferHeight(BufferItem* buffer) { + if (buffer == NULL) return -1; + + if (!buffer->mCrop.isEmpty()) { + return buffer->mCrop.getHeight(); + } + + ALOGV("%s: buffer->mGraphicBuffer: %p", __FUNCTION__, buffer->mGraphicBuffer.get()); + return buffer->mGraphicBuffer->getHeight(); +} + } // namespace android diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h index a30e1be586f9..3791ec933cd9 100644 --- a/media/jni/android_media_Utils.h +++ b/media/jni/android_media_Utils.h @@ -25,6 +25,8 @@ #include <JNIHelp.h> #include <utils/KeyedVector.h> #include <utils/String8.h> +#include <gui/CpuConsumer.h> +#include <camera3.h> namespace android { @@ -69,6 +71,33 @@ status_t ConvertKeyValueArraysToMessage( JNIEnv *env, jobjectArray keys, jobjectArray values, sp<AMessage> *msg); +// -----------Utility functions used by ImageReader/Writer JNI----------------- + +typedef CpuConsumer::LockedBuffer LockedImage; + +bool usingRGBAToJpegOverride(int32_t imageFormat, int32_t containerFormat); + +int32_t applyFormatOverrides(int32_t imageFormat, int32_t containerFormat); + +uint32_t Image_getJpegSize(LockedImage* buffer, bool usingRGBAOverride); + +bool isFormatOpaque(int format); + +bool isPossiblyYUV(PixelFormat format); + +status_t getLockedImageInfo(LockedImage* buffer, int idx, int32_t containerFormat, + uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride); + +status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage, + const Rect& rect, int fenceFd, LockedImage* outputImage); + +status_t lockImageFromBuffer(BufferItem* bufferItem, uint32_t inUsage, + int fenceFd, LockedImage* outputImage); + +int getBufferWidth(BufferItem *buffer); + +int getBufferHeight(BufferItem *buffer); + }; // namespace android #endif // _ANDROID_MEDIA_UTILS_H_ |