NDK: AImageReader implementation
Bug: 23012001
Change-Id: I14341de141e6fc5817f397e849af35ccdb80d644
diff --git a/camera/ndk/Android.mk b/camera/ndk/Android.mk
index 38be78b..c9fcddf 100644
--- a/camera/ndk/Android.mk
+++ b/camera/ndk/Android.mk
@@ -35,7 +35,8 @@
LOCAL_C_INCLUDES := \
system/media/camera/include \
- frameworks/av/include/camera/ndk
+ frameworks/av/include/camera/ndk \
+ frameworks/av/include/ndk \
LOCAL_CFLAGS += -fvisibility=hidden -D EXPORT='__attribute__ ((visibility ("default")))'
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index f121391..fbc8d19 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -19,6 +19,8 @@
#include "ACameraMetadata.h"
#include <utils/Vector.h>
+#include <system/graphics.h>
+#include "NdkImage.h"
using namespace android;
@@ -29,6 +31,7 @@
mData(buffer), mType(type) {
if (mType == ACM_CHARACTERISTICS) {
filterUnsupportedFeatures();
+ filterStreamConfigurations();
}
// TODO: filter request/result keys
}
@@ -60,7 +63,7 @@
ACameraMetadata::filterUnsupportedFeatures() {
// Hide unsupported capabilities (reprocessing)
camera_metadata_entry entry = mData.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
- if (entry.count == 0 || entry.type != ACAMERA_TYPE_BYTE) {
+ if (entry.count == 0 || entry.type != TYPE_BYTE) {
ALOGE("%s: malformed available capability key! count %zu, type %d",
__FUNCTION__, entry.count, entry.type);
return;
@@ -75,7 +78,73 @@
}
}
mData.update(ANDROID_REQUEST_AVAILABLE_CAPABILITIES, capabilities);
- // TODO: Hide unsupported streams (input/bidirectional streams)
+}
+
+
+void
+ACameraMetadata::filterStreamConfigurations() {
+ const int STREAM_CONFIGURATION_SIZE = 4;
+ const int STREAM_FORMAT_OFFSET = 0;
+ const int STREAM_WIDTH_OFFSET = 1;
+ const int STREAM_HEIGHT_OFFSET = 2;
+ const int STREAM_IS_INPUT_OFFSET = 3;
+ camera_metadata_entry entry = mData.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ if (entry.count == 0 || entry.count % 4 || entry.type != TYPE_INT32) {
+ ALOGE("%s: malformed available stream configuration key! count %zu, type %d",
+ __FUNCTION__, entry.count, entry.type);
+ return;
+ }
+
+ Vector<int32_t> filteredStreamConfigs;
+ filteredStreamConfigs.setCapacity(entry.count);
+
+ for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
+ int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET];
+ int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET];
+ int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET];
+ int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET];
+ if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
+ // Hide input streams
+ continue;
+ }
+ // Translate HAL formats to NDK format
+ if (format == HAL_PIXEL_FORMAT_BLOB) {
+ format = AIMAGE_FORMAT_JPEG;
+ }
+ filteredStreamConfigs.push_back(format);
+ filteredStreamConfigs.push_back(width);
+ filteredStreamConfigs.push_back(height);
+ filteredStreamConfigs.push_back(isInput);
+ }
+
+ mData.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, filteredStreamConfigs);
+
+ entry = mData.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS);
+ Vector<int32_t> filteredDepthStreamConfigs;
+ filteredDepthStreamConfigs.setCapacity(entry.count);
+
+ for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
+ int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET];
+ int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET];
+ int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET];
+ int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET];
+ if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
+ // Hide input streams
+ continue;
+ }
+ // Translate HAL formats to NDK format
+ if (format == HAL_PIXEL_FORMAT_BLOB) {
+ format = AIMAGE_FORMAT_DEPTH_POINT_CLOUD;
+ } else if (format == HAL_PIXEL_FORMAT_Y16) {
+ format = AIMAGE_FORMAT_DEPTH16;
+ }
+
+ filteredDepthStreamConfigs.push_back(format);
+ filteredDepthStreamConfigs.push_back(width);
+ filteredDepthStreamConfigs.push_back(height);
+ filteredDepthStreamConfigs.push_back(isInput);
+ }
+ mData.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, filteredDepthStreamConfigs);
}
bool
diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h
index fa0149a..442e1dd 100644
--- a/camera/ndk/impl/ACameraMetadata.h
+++ b/camera/ndk/impl/ACameraMetadata.h
@@ -55,6 +55,7 @@
inline bool isVendorTag(const uint32_t tag);
bool isCaptureRequestTag(const uint32_t tag);
void filterUnsupportedFeatures(); // Hide features not yet supported by NDK
+ void filterStreamConfigurations(); // Hide input streams, translate hal format to NDK formats
template<typename INTERNAL_T, typename NDK_T>
camera_status_t updateImpl(uint32_t tag, uint32_t count, const NDK_T* data) {
diff --git a/include/ndk/NdkImage.h b/include/ndk/NdkImage.h
new file mode 100644
index 0000000..5c92294
--- /dev/null
+++ b/include/ndk/NdkImage.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_IMAGE_H
+#define _NDK_IMAGE_H
+
+#include "NdkMediaError.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct AImage AImage;
+
+// Formats not listed here will not be supported by AImageReader
+enum {
+ AIMAGE_FORMAT_YUV_420_888 = 0x23,
+ AIMAGE_FORMAT_JPEG = 0x100,
+ AIMAGE_FORMAT_RAW16 = 0x20,
+ AIMAGE_FORMAT_RAW_PRIVATE = 0x24,
+ AIMAGE_FORMAT_RAW10 = 0x25,
+ AIMAGE_FORMAT_RAW12 = 0x26,
+ AIMAGE_FORMAT_DEPTH16 = 0x44363159,
+ AIMAGE_FORMAT_DEPTH_POINT_CLOUD = 0x101
+};
+
+typedef struct AImageCropRect {
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} AImageCropRect;
+
+// Return the image back to system and delete the AImage from memory
+// Do NOT use `image` after this call
+void AImage_delete(AImage* image);
+
+// AMEDIA_ERROR_INVALID_OBJECT will be returned if the parent AImageReader is deleted
+media_status_t AImage_getWidth(const AImage* image, /*out*/int32_t* width);
+
+// AMEDIA_ERROR_INVALID_OBJECT will be returned if the parent AImageReader is deleted
+media_status_t AImage_getHeight(const AImage* image, /*out*/int32_t* height);
+
+// AMEDIA_ERROR_INVALID_OBJECT will be returned if the parent AImageReader is deleted
+media_status_t AImage_getFormat(const AImage* image, /*out*/int32_t* format);
+
+// AMEDIA_ERROR_INVALID_OBJECT will be returned if the parent AImageReader is deleted
+media_status_t AImage_getCropRect(const AImage* image, /*out*/AImageCropRect* rect);
+
+// AMEDIA_ERROR_INVALID_OBJECT will be returned if the parent AImageReader is deleted
+media_status_t AImage_getTimestamp(const AImage* image, /*out*/int64_t* timestampNs);
+
+// AMEDIA_ERROR_INVALID_OBJECT will be returned if the parent AImageReader is deleted
+media_status_t AImage_getNumberOfPlanes(const AImage* image, /*out*/int32_t* numPlanes);
+
+// AMEDIA_ERROR_INVALID_OBJECT will be returned if the parent AImageReader is deleted
+media_status_t AImage_getPlanePixelStride(
+ const AImage* image, int planeIdx, /*out*/int32_t* pixelStride);
+
+// AMEDIA_ERROR_INVALID_OBJECT will be returned if the parent AImageReader is deleted
+media_status_t AImage_getPlaneRowStride(
+ const AImage* image, int planeIdx, /*out*/int32_t* rowStride);
+
+// AMEDIA_ERROR_INVALID_OBJECT will be returned if the parent AImageReader is deleted
+// Note that once the AImage or the parent AImageReader is deleted, the `*data` returned from
+// previous AImage_getPlaneData call becomes dangling pointer. Do NOT use it after
+// AImage or AImageReader is deleted
+media_status_t AImage_getPlaneData(
+ const AImage* image, int planeIdx,
+ /*out*/uint8_t** data, /*out*/int* dataLength);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //_NDK_IMAGE_H
diff --git a/include/ndk/NdkImageReader.h b/include/ndk/NdkImageReader.h
new file mode 100644
index 0000000..041c378
--- /dev/null
+++ b/include/ndk/NdkImageReader.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_IMAGE_READER_H
+#define _NDK_IMAGE_READER_H
+
+#include <android/native_window.h>
+#include "NdkMediaError.h"
+#include "NdkImage.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct AImageReader AImageReader;
+
+media_status_t AImageReader_new(
+ int32_t width, int32_t height, int32_t format, int32_t maxImages,
+ /*out*/AImageReader** reader);
+
+// Return all images acquired from this AImageReader back to system and delete
+// the AImageReader instance from memory
+// Do NOT use `reader` after this call
+void AImageReader_delete(AImageReader* reader);
+
+// Do NOT call ANativeWindow_release on the output. Just use AImageReader_delete.
+media_status_t AImageReader_getWindow(AImageReader*, /*out*/ANativeWindow** window);
+
+media_status_t AImageReader_getWidth(const AImageReader* reader, /*out*/int32_t* width);
+media_status_t AImageReader_getHeight(const AImageReader* reader, /*out*/int32_t* height);
+media_status_t AImageReader_getFormat(const AImageReader* reader, /*out*/int32_t* format);
+media_status_t AImageReader_getMaxImages(const AImageReader* reader, /*out*/int32_t* maxImages);
+
+media_status_t AImageReader_acquireNextImage(AImageReader* reader, /*out*/AImage** image);
+
+media_status_t AImageReader_acquireLatestImage(AImageReader* reader, /*out*/AImage** image);
+
+// The callback happens on one dedicated thread per AImageReader instance
+// It's okay to use AImageReader_*/AImage_* APIs within the callback
+typedef void (*AImageReader_ImageCallback)(void* context, AImageReader* reader);
+
+typedef struct AImageReader_ImageListener {
+ void* context; // optional application context.
+ AImageReader_ImageCallback onImageAvailable;
+} AImageReader_ImageListener;
+
+media_status_t AImageReader_setImageListener(
+ AImageReader* reader, AImageReader_ImageListener* listener);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //_NDK_IMAGE_READER_H
diff --git a/include/ndk/NdkMediaError.h b/include/ndk/NdkMediaError.h
index 12613eb..60d401b 100644
--- a/include/ndk/NdkMediaError.h
+++ b/include/ndk/NdkMediaError.h
@@ -53,6 +53,10 @@
AMEDIA_DRM_NEED_KEY = AMEDIA_DRM_ERROR_BASE - 8,
AMEDIA_DRM_LICENSE_EXPIRED = AMEDIA_DRM_ERROR_BASE - 9,
+ AMEDIA_IMGREADER_ERROR_BASE = -30000,
+ AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE = AMEDIA_IMGREADER_ERROR_BASE - 1,
+ AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED = AMEDIA_IMGREADER_ERROR_BASE - 2,
+
} media_status_t;
diff --git a/media/ndk/Android.mk b/media/ndk/Android.mk
index 8f795cd..6546f14 100644
--- a/media/ndk/Android.mk
+++ b/media/ndk/Android.mk
@@ -27,13 +27,16 @@
NdkMediaFormat.cpp \
NdkMediaMuxer.cpp \
NdkMediaDrm.cpp \
+ NdkImage.cpp \
+ NdkImageReader.cpp \
LOCAL_MODULE:= libmediandk
LOCAL_C_INCLUDES := \
bionic/libc/private \
frameworks/base/core/jni \
- frameworks/av/include/ndk
+ frameworks/av/include/ndk \
+ system/media/camera/include
LOCAL_CFLAGS += -fvisibility=hidden -D EXPORT='__attribute__ ((visibility ("default")))'
@@ -46,6 +49,8 @@
libutils \
libandroid_runtime \
libbinder \
+ libgui \
+ libui \
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/ndk/NdkImage.cpp b/media/ndk/NdkImage.cpp
new file mode 100644
index 0000000..40900ad
--- /dev/null
+++ b/media/ndk/NdkImage.cpp
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NdkImage"
+
+#include "NdkImagePriv.h"
+#include "NdkImageReaderPriv.h"
+
+#include <utils/Log.h>
+#include "hardware/camera3.h"
+
+using namespace android;
+
+#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
+
+AImage::AImage(AImageReader* reader, int32_t format,
+ CpuConsumer::LockedBuffer* buffer, int64_t timestamp,
+ int32_t width, int32_t height, int32_t numPlanes) :
+ mReader(reader), mFormat(format),
+ mBuffer(buffer), mTimestamp(timestamp),
+ mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
+}
+
+// Can only be called by free() with mLock hold
+AImage::~AImage() {
+ if (!mIsClosed) {
+ LOG_ALWAYS_FATAL(
+ "Error: AImage %p is deleted before returning buffer to AImageReader!", this);
+ }
+}
+
+bool
+AImage::isClosed() const {
+ Mutex::Autolock _l(mLock);
+ return mIsClosed;
+}
+
+void
+AImage::close() {
+ Mutex::Autolock _l(mLock);
+ if (mIsClosed) {
+ return;
+ }
+ sp<AImageReader> reader = mReader.promote();
+ if (reader == nullptr) {
+ LOG_ALWAYS_FATAL("Error: AImage not closed before AImageReader close!");
+ return;
+ }
+ reader->releaseImageLocked(this);
+ // Should have been set to nullptr in releaseImageLocked
+ // Set to nullptr here for extra safety only
+ mBuffer = nullptr;
+ mIsClosed = true;
+}
+
+void
+AImage::free() {
+ if (!isClosed()) {
+ ALOGE("Cannot free AImage before close!");
+ return;
+ }
+ Mutex::Autolock _l(mLock);
+ delete this;
+}
+
+void
+AImage::lockReader() const {
+ sp<AImageReader> reader = mReader.promote();
+ if (reader == nullptr) {
+ // Reader has been closed
+ return;
+ }
+ reader->mLock.lock();
+}
+
+void
+AImage::unlockReader() const {
+ sp<AImageReader> reader = mReader.promote();
+ if (reader == nullptr) {
+ // Reader has been closed
+ return;
+ }
+ reader->mLock.unlock();
+}
+
+media_status_t
+AImage::getWidth(int32_t* width) const {
+ if (width == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *width = -1;
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ *width = mWidth;
+ return AMEDIA_OK;
+}
+
+media_status_t
+AImage::getHeight(int32_t* height) const {
+ if (height == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *height = -1;
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ *height = mHeight;
+ return AMEDIA_OK;
+}
+
+media_status_t
+AImage::getFormat(int32_t* format) const {
+ if (format == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *format = -1;
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ *format = mFormat;
+ return AMEDIA_OK;
+}
+
+media_status_t
+AImage::getNumPlanes(int32_t* numPlanes) const {
+ if (numPlanes == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *numPlanes = -1;
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ *numPlanes = mNumPlanes;
+ return AMEDIA_OK;
+}
+
+media_status_t
+AImage::getTimestamp(int64_t* timestamp) const {
+ if (timestamp == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *timestamp = -1;
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ *timestamp = mTimestamp;
+ return AMEDIA_OK;
+}
+
+media_status_t
+AImage::getPlanePixelStride(int planeIdx, /*out*/int32_t* pixelStride) const {
+ if (planeIdx < 0 || planeIdx >= mNumPlanes) {
+ ALOGE("Error: planeIdx %d out of bound [0,%d]",
+ planeIdx, mNumPlanes - 1);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ if (pixelStride == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ int32_t fmt = mBuffer->flexFormat;
+ switch (fmt) {
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ *pixelStride = (planeIdx == 0) ? 1 : mBuffer->chromaStep;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ *pixelStride = (planeIdx == 0) ? 1 : 2;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_Y8:
+ *pixelStride = 1;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_YV12:
+ *pixelStride = 1;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_Y16:
+ case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RGB_565:
+ // Single plane 16bpp data.
+ *pixelStride = 2;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ *pixelStride = 4;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ // Single plane, 24bpp.
+ *pixelStride = 3;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_BLOB:
+ case HAL_PIXEL_FORMAT_RAW10:
+ case HAL_PIXEL_FORMAT_RAW12:
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ // Blob is used for JPEG data, RAW10 and RAW12 is used for 10-bit and 12-bit raw data,
+ // those are single plane data without pixel stride defined
+ return AMEDIA_ERROR_UNSUPPORTED;
+ default:
+ ALOGE("Pixel format: 0x%x is unsupported", fmt);
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+}
+
+media_status_t
+AImage::getPlaneRowStride(int planeIdx, /*out*/int32_t* rowStride) const {
+ if (planeIdx < 0 || planeIdx >= mNumPlanes) {
+ ALOGE("Error: planeIdx %d out of bound [0,%d]",
+ planeIdx, mNumPlanes - 1);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ if (rowStride == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ int32_t fmt = mBuffer->flexFormat;
+ switch (fmt) {
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ *rowStride = (planeIdx == 0) ? mBuffer->stride : mBuffer->chromaStride;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ *rowStride = mBuffer->width;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_YV12:
+ if (mBuffer->stride % 16) {
+ ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ *rowStride = (planeIdx == 0) ? mBuffer->stride : ALIGN(mBuffer->stride / 2, 16);
+ return AMEDIA_OK;
+ 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
+ *rowStride = mBuffer->stride;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_Y8:
+ if (mBuffer->stride % 16) {
+ ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ *rowStride = mBuffer->stride;
+ return AMEDIA_OK;
+ 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)
+ if (mBuffer->stride % 16) {
+ ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ *rowStride = mBuffer->stride * 2;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ *rowStride = mBuffer->stride * 2;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ *rowStride = mBuffer->stride * 4;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ // Single plane, 24bpp.
+ *rowStride = mBuffer->stride * 3;
+ return AMEDIA_OK;
+ case HAL_PIXEL_FORMAT_BLOB:
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ // Blob is used for JPEG/Raw opaque data. It is single plane and has 0 row stride and
+ // no row stride defined
+ return AMEDIA_ERROR_UNSUPPORTED;
+ default:
+ ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt);
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+}
+
+uint32_t
+AImage::getJpegSize() const {
+ if (mBuffer == nullptr) {
+ LOG_ALWAYS_FATAL("Error: buffer is null");
+ }
+
+ uint32_t size = 0;
+ uint32_t width = mBuffer->width;
+ uint8_t* jpegBuffer = mBuffer->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) {
+ /*
+ * 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;
+}
+
+media_status_t
+AImage::getPlaneData(int planeIdx,/*out*/uint8_t** data, /*out*/int* dataLength) const {
+ if (planeIdx < 0 || planeIdx >= mNumPlanes) {
+ ALOGE("Error: planeIdx %d out of bound [0,%d]",
+ planeIdx, mNumPlanes - 1);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ if (data == nullptr || dataLength == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ uint32_t dataSize, ySize, cSize, cStride;
+ uint8_t* cb = nullptr;
+ uint8_t* cr = nullptr;
+ uint8_t* pData = nullptr;
+ int bytesPerPixel = 0;
+ int32_t fmt = mBuffer->flexFormat;
+
+ switch (fmt) {
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ pData = (planeIdx == 0) ? mBuffer->data :
+ (planeIdx == 1) ? mBuffer->dataCb : mBuffer->dataCr;
+ // only map until last pixel
+ if (planeIdx == 0) {
+ dataSize = mBuffer->stride * (mBuffer->height - 1) + mBuffer->width;
+ } else {
+ dataSize = mBuffer->chromaStride * (mBuffer->height / 2 - 1) +
+ mBuffer->chromaStep * (mBuffer->width / 2 - 1) + 1;
+ }
+ break;
+ // NV21
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ cr = mBuffer->data + (mBuffer->stride * mBuffer->height);
+ cb = cr + 1;
+ // only map until last pixel
+ ySize = mBuffer->width * (mBuffer->height - 1) + mBuffer->width;
+ cSize = mBuffer->width * (mBuffer->height / 2 - 1) + mBuffer->width - 1;
+
+ pData = (planeIdx == 0) ? mBuffer->data :
+ (planeIdx == 1) ? cb : cr;
+ dataSize = (planeIdx == 0) ? ySize : cSize;
+ break;
+ case HAL_PIXEL_FORMAT_YV12:
+ // Y and C stride need to be 16 pixel aligned.
+ if (mBuffer->stride % 16) {
+ ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ ySize = mBuffer->stride * mBuffer->height;
+ cStride = ALIGN(mBuffer->stride / 2, 16);
+ cr = mBuffer->data + ySize;
+ cSize = cStride * mBuffer->height / 2;
+ cb = cr + cSize;
+
+ pData = (planeIdx == 0) ? mBuffer->data :
+ (planeIdx == 1) ? cb : cr;
+ dataSize = (planeIdx == 0) ? ySize : cSize;
+ break;
+ case HAL_PIXEL_FORMAT_Y8:
+ // Single plane, 8bpp.
+
+ pData = mBuffer->data;
+ dataSize = mBuffer->stride * mBuffer->height;
+ break;
+ case HAL_PIXEL_FORMAT_Y16:
+ bytesPerPixel = 2;
+
+ pData = mBuffer->data;
+ dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
+ break;
+ case HAL_PIXEL_FORMAT_BLOB:
+ // Used for JPEG data, height must be 1, width == size, single plane.
+ if (mBuffer->height != 1) {
+ ALOGE("Jpeg should have height value one but got %d", mBuffer->height);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ pData = mBuffer->data;
+ dataSize = getJpegSize();
+ break;
+ case HAL_PIXEL_FORMAT_RAW16:
+ // Single plane 16bpp bayer data.
+ bytesPerPixel = 2;
+ pData = mBuffer->data;
+ dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
+ break;
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ // Used for RAW_OPAQUE data, height must be 1, width == size, single plane.
+ if (mBuffer->height != 1) {
+ ALOGE("RAW_OPAQUE should have height value one but got %d", mBuffer->height);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ pData = mBuffer->data;
+ dataSize = mBuffer->width;
+ break;
+ case HAL_PIXEL_FORMAT_RAW10:
+ // Single plane 10bpp bayer data.
+ if (mBuffer->width % 4) {
+ ALOGE("Width is not multiple of 4 %d", mBuffer->width);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ if (mBuffer->height % 2) {
+ ALOGE("Height is not multiple of 2 %d", mBuffer->height);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ if (mBuffer->stride < (mBuffer->width * 10 / 8)) {
+ ALOGE("stride (%d) should be at least %d",
+ mBuffer->stride, mBuffer->width * 10 / 8);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ pData = mBuffer->data;
+ dataSize = mBuffer->stride * mBuffer->height;
+ break;
+ case HAL_PIXEL_FORMAT_RAW12:
+ // Single plane 10bpp bayer data.
+ if (mBuffer->width % 4) {
+ ALOGE("Width is not multiple of 4 %d", mBuffer->width);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ if (mBuffer->height % 2) {
+ ALOGE("Height is not multiple of 2 %d", mBuffer->height);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ if (mBuffer->stride < (mBuffer->width * 12 / 8)) {
+ ALOGE("stride (%d) should be at least %d",
+ mBuffer->stride, mBuffer->width * 12 / 8);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ pData = mBuffer->data;
+ dataSize = mBuffer->stride * mBuffer->height;
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ // Single plane, 32bpp.
+ bytesPerPixel = 4;
+ pData = mBuffer->data;
+ dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ // Single plane, 16bpp.
+ bytesPerPixel = 2;
+ pData = mBuffer->data;
+ dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ // Single plane, 24bpp.
+ bytesPerPixel = 3;
+ pData = mBuffer->data;
+ dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
+ break;
+ default:
+ ALOGE("Pixel format: 0x%x is unsupported", fmt);
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ *data = pData;
+ *dataLength = dataSize;
+ return AMEDIA_OK;
+}
+
+EXPORT
+void AImage_delete(AImage* image) {
+ ALOGV("%s", __FUNCTION__);
+ if (image != nullptr) {
+ image->lockReader();
+ image->close();
+ image->unlockReader();
+ if (!image->isClosed()) {
+ LOG_ALWAYS_FATAL("Image close failed!");
+ }
+ image->free();
+ }
+ return;
+}
+
+EXPORT
+media_status_t AImage_getWidth(const AImage* image, /*out*/int32_t* width) {
+ ALOGV("%s", __FUNCTION__);
+ if (image == nullptr || width == nullptr) {
+ ALOGE("%s: bad argument. image %p width %p",
+ __FUNCTION__, image, width);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getWidth(width);
+}
+
+EXPORT
+media_status_t AImage_getHeight(const AImage* image, /*out*/int32_t* height) {
+ ALOGV("%s", __FUNCTION__);
+ if (image == nullptr || height == nullptr) {
+ ALOGE("%s: bad argument. image %p height %p",
+ __FUNCTION__, image, height);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getHeight(height);
+}
+
+EXPORT
+media_status_t AImage_getFormat(const AImage* image, /*out*/int32_t* format) {
+ ALOGV("%s", __FUNCTION__);
+ if (image == nullptr || format == nullptr) {
+ ALOGE("%s: bad argument. image %p format %p",
+ __FUNCTION__, image, format);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getFormat(format);
+}
+
+EXPORT
+media_status_t AImage_getCropRect(const AImage* image, /*out*/AImageCropRect* rect) {
+ ALOGV("%s", __FUNCTION__);
+ if (image == nullptr || rect == nullptr) {
+ ALOGE("%s: bad argument. image %p rect %p",
+ __FUNCTION__, image, rect);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ // For now AImage only supports camera outputs where cropRect is always full window
+ int32_t width = -1;
+ media_status_t ret = image->getWidth(&width);
+ if (ret != AMEDIA_OK) {
+ return ret;
+ }
+ int32_t height = -1;
+ ret = image->getHeight(&height);
+ if (ret != AMEDIA_OK) {
+ return ret;
+ }
+ rect->left = 0;
+ rect->top = 0;
+ rect->right = width;
+ rect->bottom = height;
+ return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AImage_getTimestamp(const AImage* image, /*out*/int64_t* timestampNs) {
+ ALOGV("%s", __FUNCTION__);
+ if (image == nullptr || timestampNs == nullptr) {
+ ALOGE("%s: bad argument. image %p timestampNs %p",
+ __FUNCTION__, image, timestampNs);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getTimestamp(timestampNs);
+}
+
+EXPORT
+media_status_t AImage_getNumberOfPlanes(const AImage* image, /*out*/int32_t* numPlanes) {
+ ALOGV("%s", __FUNCTION__);
+ if (image == nullptr || numPlanes == nullptr) {
+ ALOGE("%s: bad argument. image %p numPlanes %p",
+ __FUNCTION__, image, numPlanes);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getNumPlanes(numPlanes);
+}
+
+EXPORT
+media_status_t AImage_getPlanePixelStride(
+ const AImage* image, int planeIdx, /*out*/int32_t* pixelStride) {
+ ALOGV("%s", __FUNCTION__);
+ if (image == nullptr || pixelStride == nullptr) {
+ ALOGE("%s: bad argument. image %p pixelStride %p",
+ __FUNCTION__, image, pixelStride);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getPlanePixelStride(planeIdx, pixelStride);
+}
+
+EXPORT
+media_status_t AImage_getPlaneRowStride(
+ const AImage* image, int planeIdx, /*out*/int32_t* rowStride) {
+ ALOGV("%s", __FUNCTION__);
+ if (image == nullptr || rowStride == nullptr) {
+ ALOGE("%s: bad argument. image %p rowStride %p",
+ __FUNCTION__, image, rowStride);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getPlaneRowStride(planeIdx, rowStride);
+}
+
+EXPORT
+media_status_t AImage_getPlaneData(
+ const AImage* image, int planeIdx,
+ /*out*/uint8_t** data, /*out*/int* dataLength) {
+ ALOGV("%s", __FUNCTION__);
+ if (image == nullptr || data == nullptr || dataLength == nullptr) {
+ ALOGE("%s: bad argument. image %p data %p dataLength %p",
+ __FUNCTION__, image, data, dataLength);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getPlaneData(planeIdx, data, dataLength);
+}
diff --git a/media/ndk/NdkImagePriv.h b/media/ndk/NdkImagePriv.h
new file mode 100644
index 0000000..89d2b7c
--- /dev/null
+++ b/media/ndk/NdkImagePriv.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NDK_IMAGE_PRIV_H
+#define _NDK_IMAGE_PRIV_H
+
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+
+#include <gui/CpuConsumer.h>
+
+#include "NdkImageReaderPriv.h"
+#include "NdkImage.h"
+
+
+using namespace android;
+
+// TODO: this only supports ImageReader
+struct AImage {
+ AImage(AImageReader* reader, int32_t format,
+ CpuConsumer::LockedBuffer* buffer, int64_t timestamp,
+ int32_t width, int32_t height, int32_t numPlanes);
+
+ // free all resources while keeping object alive. Caller must obtain reader lock
+ void close();
+
+ // Remove from object memory. Must be called after close
+ void free();
+
+ bool isClosed() const ;
+
+ // only For AImage to grab reader lock
+ // Always grab reader lock before grabbing image lock
+ void lockReader() const;
+ void unlockReader() const;
+
+ media_status_t getWidth(/*out*/int32_t* width) const;
+ media_status_t getHeight(/*out*/int32_t* height) const;
+ media_status_t getFormat(/*out*/int32_t* format) const;
+ media_status_t getNumPlanes(/*out*/int32_t* numPlanes) const;
+ media_status_t getTimestamp(/*out*/int64_t* timestamp) const;
+
+ media_status_t getPlanePixelStride(int planeIdx, /*out*/int32_t* pixelStride) const;
+ media_status_t getPlaneRowStride(int planeIdx, /*out*/int32_t* rowStride) const;
+ media_status_t getPlaneData(int planeIdx,/*out*/uint8_t** data, /*out*/int* dataLength) const;
+
+ private:
+ // AImage should be deleted through free() API.
+ ~AImage();
+
+ friend struct AImageReader; // for reader to access mBuffer
+
+ uint32_t getJpegSize() const;
+
+ // When reader is close, AImage will only accept close API call
+ wp<AImageReader> mReader;
+ const int32_t mFormat;
+ CpuConsumer::LockedBuffer* mBuffer;
+ const int64_t mTimestamp;
+ const int32_t mWidth;
+ const int32_t mHeight;
+ const int32_t mNumPlanes;
+ bool mIsClosed = false;
+ mutable Mutex mLock;
+};
+
+#endif // _NDK_IMAGE_PRIV_H
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
new file mode 100644
index 0000000..d57a86e
--- /dev/null
+++ b/media/ndk/NdkImageReader.cpp
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NdkImageReader"
+
+#include "NdkImagePriv.h"
+#include "NdkImageReaderPriv.h"
+
+#include <utils/Log.h>
+#include <android_runtime/android_view_Surface.h>
+
+using namespace android;
+
+namespace {
+ // Get an ID that's unique within this process.
+ static int32_t createProcessUniqueId() {
+ static volatile int32_t globalCounter = 0;
+ return android_atomic_inc(&globalCounter);
+ }
+}
+
+const char* AImageReader::kCallbackFpKey = "Callback";
+const char* AImageReader::kContextKey = "Context";
+
+bool
+AImageReader::isSupportedFormat(int32_t format) {
+ switch (format) {
+ case AIMAGE_FORMAT_YUV_420_888:
+ case AIMAGE_FORMAT_JPEG:
+ case AIMAGE_FORMAT_RAW16:
+ case AIMAGE_FORMAT_RAW_PRIVATE:
+ case AIMAGE_FORMAT_RAW10:
+ case AIMAGE_FORMAT_RAW12:
+ case AIMAGE_FORMAT_DEPTH16:
+ case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int
+AImageReader::getNumPlanesForFormat(int32_t format) {
+ switch (format) {
+ case AIMAGE_FORMAT_YUV_420_888:
+ return 3;
+ case AIMAGE_FORMAT_JPEG:
+ case AIMAGE_FORMAT_RAW16:
+ case AIMAGE_FORMAT_RAW_PRIVATE:
+ case AIMAGE_FORMAT_RAW10:
+ case AIMAGE_FORMAT_RAW12:
+ case AIMAGE_FORMAT_DEPTH16:
+ case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
+ return 1;
+ default:
+ return -1;
+ }
+}
+
+void
+AImageReader::FrameListener::onFrameAvailable(const BufferItem& /*item*/) {
+ Mutex::Autolock _l(mLock);
+ sp<AImageReader> reader = mReader.promote();
+ if (reader == nullptr) {
+ ALOGW("A frame is available after AImageReader closed!");
+ return; // reader has been closed
+ }
+ if (mListener.onImageAvailable == nullptr) {
+ return; // No callback registered
+ }
+
+ sp<AMessage> msg = new AMessage(AImageReader::kWhatImageAvailable, reader->mHandler);
+ msg->setPointer(AImageReader::kCallbackFpKey, (void *) mListener.onImageAvailable);
+ msg->setPointer(AImageReader::kContextKey, mListener.context);
+ msg->post();
+}
+
+media_status_t
+AImageReader::FrameListener::setImageListener(AImageReader_ImageListener* listener) {
+ Mutex::Autolock _l(mLock);
+ if (listener == nullptr) {
+ ALOGE("AImageReader: listener is null!");
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ mListener = *listener;
+ return AMEDIA_OK;
+}
+
+media_status_t
+AImageReader::setImageListenerLocked(AImageReader_ImageListener* listener) {
+ return mFrameListener->setImageListener(listener);
+}
+
+media_status_t
+AImageReader::setImageListener(AImageReader_ImageListener* listener) {
+ Mutex::Autolock _l(mLock);
+ return setImageListenerLocked(listener);
+}
+
+void AImageReader::CallbackHandler::onMessageReceived(
+ const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatImageAvailable:
+ {
+ AImageReader_ImageCallback onImageAvailable;
+ void* context;
+ bool found = msg->findPointer(kCallbackFpKey, (void**) &onImageAvailable);
+ if (!found || onImageAvailable == nullptr) {
+ ALOGE("%s: Cannot find onImageAvailable callback fp!", __FUNCTION__);
+ return;
+ }
+ found = msg->findPointer(kContextKey, &context);
+ if (!found) {
+ ALOGE("%s: Cannot find callback context!", __FUNCTION__);
+ return;
+ }
+ (*onImageAvailable)(context, mReader);
+ break;
+ }
+ default:
+ ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what());
+ break;
+ }
+}
+
+AImageReader::AImageReader(int32_t width, int32_t height, int32_t format, int32_t maxImages) :
+ mWidth(width), mHeight(height), mFormat(format), mMaxImages(maxImages),
+ mNumPlanes(getNumPlanesForFormat(format)),
+ mFrameListener(new FrameListener(this)) {}
+
+media_status_t
+AImageReader::init() {
+ PublicFormat publicFormat = static_cast<PublicFormat>(mFormat);
+ mHalFormat = android_view_Surface_mapPublicFormatToHalFormat(publicFormat);
+ mHalDataSpace = android_view_Surface_mapPublicFormatToHalDataspace(publicFormat);
+
+ sp<IGraphicBufferProducer> gbProducer;
+ sp<IGraphicBufferConsumer> gbConsumer;
+ BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
+
+ sp<CpuConsumer> cpuConsumer;
+ String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
+ mWidth, mHeight, mFormat, mMaxImages, getpid(),
+ createProcessUniqueId());
+
+ cpuConsumer = new CpuConsumer(gbConsumer, mMaxImages, /*controlledByApp*/true);
+ if (cpuConsumer == nullptr) {
+ ALOGE("Failed to allocate CpuConsumer");
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ mCpuConsumer = cpuConsumer;
+ mCpuConsumer->setName(consumerName);
+ mProducer = gbProducer;
+
+ sp<ConsumerBase> consumer = cpuConsumer;
+ consumer->setFrameAvailableListener(mFrameListener);
+
+ status_t res;
+ res = cpuConsumer->setDefaultBufferSize(mWidth, mHeight);
+ if (res != OK) {
+ ALOGE("Failed to set CpuConsumer buffer size");
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ res = cpuConsumer->setDefaultBufferFormat(mHalFormat);
+ if (res != OK) {
+ ALOGE("Failed to set CpuConsumer buffer format");
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ res = cpuConsumer->setDefaultBufferDataSpace(mHalDataSpace);
+ if (res != OK) {
+ ALOGE("Failed to set CpuConsumer buffer dataSpace");
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ mSurface = new Surface(mProducer, /*controlledByApp*/true);
+ if (mSurface == nullptr) {
+ ALOGE("Failed to create surface");
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ mWindow = static_cast<ANativeWindow*>(mSurface.get());
+
+ for (int i = 0; i < mMaxImages; i++) {
+ CpuConsumer::LockedBuffer* buffer = new CpuConsumer::LockedBuffer;
+ mBuffers.push_back(buffer);
+ }
+
+ mCbLooper = new ALooper;
+ mCbLooper->setName(consumerName.string());
+ status_t ret = mCbLooper->start(
+ /*runOnCallingThread*/false,
+ /*canCallJava*/ true,
+ PRIORITY_DEFAULT);
+ mHandler = new CallbackHandler(this);
+ mCbLooper->registerHandler(mHandler);
+
+ return AMEDIA_OK;
+}
+
+AImageReader::~AImageReader() {
+ Mutex::Autolock _l(mLock);
+ AImageReader_ImageListener nullListener = {nullptr, nullptr};
+ setImageListenerLocked(&nullListener);
+
+ if (mCbLooper != nullptr) {
+ mCbLooper->unregisterHandler(mHandler->id());
+ mCbLooper->stop();
+ }
+ mCbLooper.clear();
+ mHandler.clear();
+
+ // Close all previously acquired images
+ for (auto it = mAcquiredImages.begin();
+ it != mAcquiredImages.end(); it++) {
+ AImage* image = *it;
+ image->close();
+ }
+
+ // Delete LockedBuffers
+ for (auto it = mBuffers.begin();
+ it != mBuffers.end(); it++) {
+ delete *it;
+ }
+
+ if (mCpuConsumer != nullptr) {
+ mCpuConsumer->abandon();
+ mCpuConsumer->setFrameAvailableListener(nullptr);
+ }
+}
+
+media_status_t
+AImageReader::acquireCpuConsumerImageLocked(/*out*/AImage** image) {
+ *image = nullptr;
+ CpuConsumer::LockedBuffer* buffer = getLockedBufferLocked();
+ if (buffer == nullptr) {
+ ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than"
+ " maxImages buffers");
+ return AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED;
+ }
+
+ status_t res = mCpuConsumer->lockNextBuffer(buffer);
+ if (res != NO_ERROR) {
+ returnLockedBufferLocked(buffer);
+ if (res != BAD_VALUE /*no buffers*/) {
+ if (res == NOT_ENOUGH_DATA) {
+ return AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED;
+ } else {
+ ALOGE("%s Fail to lockNextBuffer with error: %d ",
+ __FUNCTION__, res);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ }
+ return AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE;
+ }
+
+ if (buffer->flexFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
+ ALOGE("NV21 format is not supported by AImageReader");
+ return AMEDIA_ERROR_UNSUPPORTED;
+ }
+
+ // 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) {
+ ALOGE("crop left top corner [%d, %d] need to be at origin", lt.x, lt.y);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+
+ // Check if the producer buffer configurations match what ImageReader configured.
+ int outputWidth = getBufferWidth(buffer);
+ int outputHeight = getBufferHeight(buffer);
+
+ int readerFmt = mHalFormat;
+ int readerWidth = mWidth;
+ int readerHeight = mHeight;
+
+ if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) && (readerFmt != HAL_PIXEL_FORMAT_BLOB) &&
+ (readerWidth != outputWidth || readerHeight != outputHeight)) {
+ ALOGW("%s: Producer buffer size: %dx%d, doesn't match AImageReader configured size: %dx%d",
+ __FUNCTION__, outputWidth, outputHeight, readerWidth, readerHeight);
+ }
+
+ int bufFmt = buffer->format;
+ if (readerFmt == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+ bufFmt = buffer->flexFormat;
+ }
+
+ if (readerFmt != bufFmt) {
+ if (readerFmt == 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).
+ mHalFormat = bufFmt;
+ ALOGD("%s: Overriding buffer format YUV_420_888 to %x.", __FUNCTION__, bufFmt);
+ } else {
+ // Return the buffer to the queue.
+ mCpuConsumer->unlockBuffer(*buffer);
+ returnLockedBufferLocked(buffer);
+
+ ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x",
+ buffer->format, readerFmt);
+
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ }
+
+ if (mHalFormat == HAL_PIXEL_FORMAT_BLOB) {
+ *image = new AImage(this, mFormat, buffer, buffer->timestamp,
+ readerWidth, readerHeight, mNumPlanes);
+ } else {
+ *image = new AImage(this, mFormat, buffer, buffer->timestamp,
+ outputWidth, outputHeight, mNumPlanes);
+ }
+ mAcquiredImages.push_back(*image);
+ return AMEDIA_OK;
+}
+
+CpuConsumer::LockedBuffer*
+AImageReader::getLockedBufferLocked() {
+ if (mBuffers.empty()) {
+ return nullptr;
+ }
+ // Return a LockedBuffer pointer and remove it from the list
+ auto it = mBuffers.begin();
+ CpuConsumer::LockedBuffer* buffer = *it;
+ mBuffers.erase(it);
+ return buffer;
+}
+
+void
+AImageReader::returnLockedBufferLocked(CpuConsumer::LockedBuffer* buffer) {
+ mBuffers.push_back(buffer);
+}
+
+void
+AImageReader::releaseImageLocked(AImage* image) {
+ CpuConsumer::LockedBuffer* buffer = image->mBuffer;
+ if (buffer == nullptr) {
+ // This should not happen, but is not fatal
+ ALOGW("AImage %p has no buffer!", image);
+ return;
+ }
+
+ mCpuConsumer->unlockBuffer(*buffer);
+ returnLockedBufferLocked(buffer);
+ image->mBuffer = nullptr;
+
+ bool found = false;
+ // cleanup acquired image list
+ for (auto it = mAcquiredImages.begin();
+ it != mAcquiredImages.end(); it++) {
+ AImage* readerCopy = *it;
+ if (readerCopy == image) {
+ found = true;
+ mAcquiredImages.erase(it);
+ break;
+ }
+ }
+ if (!found) {
+ ALOGE("Error: AImage %p is not generated by AImageReader %p",
+ image, this);
+ }
+}
+
+int
+AImageReader::getBufferWidth(CpuConsumer::LockedBuffer* buffer) {
+ if (buffer == nullptr) return -1;
+
+ if (!buffer->crop.isEmpty()) {
+ return buffer->crop.getWidth();
+ }
+ return buffer->width;
+}
+
+int
+AImageReader::getBufferHeight(CpuConsumer::LockedBuffer* buffer) {
+ if (buffer == nullptr) return -1;
+
+ if (!buffer->crop.isEmpty()) {
+ return buffer->crop.getHeight();
+ }
+ return buffer->height;
+}
+
+media_status_t
+AImageReader::acquireNextImage(/*out*/AImage** image) {
+ Mutex::Autolock _l(mLock);
+ return acquireCpuConsumerImageLocked(image);
+}
+
+media_status_t
+AImageReader::acquireLatestImage(/*out*/AImage** image) {
+ if (image == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ Mutex::Autolock _l(mLock);
+ *image = nullptr;
+ AImage* prevImage = nullptr;
+ AImage* nextImage = nullptr;
+ media_status_t ret = acquireCpuConsumerImageLocked(&prevImage);
+ if (prevImage == nullptr) {
+ return ret;
+ }
+ for (;;) {
+ ret = acquireCpuConsumerImageLocked(&nextImage);
+ if (nextImage == nullptr) {
+ *image = prevImage;
+ return AMEDIA_OK;
+ }
+ prevImage->close();
+ prevImage->free();
+ prevImage = nextImage;
+ nextImage = nullptr;
+ }
+}
+
+EXPORT
+media_status_t AImageReader_new(
+ int32_t width, int32_t height, int32_t format, int32_t maxImages,
+ /*out*/AImageReader** reader) {
+ ALOGV("%s", __FUNCTION__);
+
+ if (width < 1 || height < 1) {
+ ALOGE("%s: image dimension must be positive: w:%d h:%d",
+ __FUNCTION__, width, height);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (maxImages < 1) {
+ ALOGE("%s: max outstanding image count must be at least 1 (%d)",
+ __FUNCTION__, maxImages);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!AImageReader::isSupportedFormat(format)) {
+ ALOGE("%s: format %d is not supported by AImageReader",
+ __FUNCTION__, format);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (reader == nullptr) {
+ ALOGE("%s: reader argument is null", __FUNCTION__);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ //*reader = new AImageReader(width, height, format, maxImages);
+ AImageReader* tmpReader = new AImageReader(width, height, format, maxImages);
+ if (tmpReader == nullptr) {
+ ALOGE("%s: AImageReader allocation failed", __FUNCTION__);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ media_status_t ret = tmpReader->init();
+ if (ret != AMEDIA_OK) {
+ ALOGE("%s: AImageReader initialization failed!", __FUNCTION__);
+ delete tmpReader;
+ return ret;
+ }
+ *reader = tmpReader;
+ (*reader)->incStrong((void*) AImageReader_new);
+ return AMEDIA_OK;
+}
+
+EXPORT
+void AImageReader_delete(AImageReader* reader) {
+ ALOGV("%s", __FUNCTION__);
+ if (reader != nullptr) {
+ reader->decStrong((void*) AImageReader_delete);
+ }
+ return;
+}
+
+EXPORT
+media_status_t AImageReader_getWindow(AImageReader* reader, /*out*/ANativeWindow** window) {
+ ALOGE("%s", __FUNCTION__);
+ if (reader == nullptr || window == nullptr) {
+ ALOGE("%s: invalid argument. reader %p, window %p",
+ __FUNCTION__, reader, window);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *window = reader->getWindow();
+ return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AImageReader_getWidth(const AImageReader* reader, /*out*/int32_t* width) {
+ ALOGV("%s", __FUNCTION__);
+ if (reader == nullptr || width == nullptr) {
+ ALOGE("%s: invalid argument. reader %p, width %p",
+ __FUNCTION__, reader, width);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *width = reader->getWidth();
+ return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AImageReader_getHeight(const AImageReader* reader, /*out*/int32_t* height) {
+ ALOGV("%s", __FUNCTION__);
+ if (reader == nullptr || height == nullptr) {
+ ALOGE("%s: invalid argument. reader %p, height %p",
+ __FUNCTION__, reader, height);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *height = reader->getHeight();
+ return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AImageReader_getFormat(const AImageReader* reader, /*out*/int32_t* format) {
+ ALOGV("%s", __FUNCTION__);
+ if (reader == nullptr || format == nullptr) {
+ ALOGE("%s: invalid argument. reader %p, format %p",
+ __FUNCTION__, reader, format);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *format = reader->getFormat();
+ return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AImageReader_getMaxImages(const AImageReader* reader, /*out*/int32_t* maxImages) {
+ ALOGV("%s", __FUNCTION__);
+ if (reader == nullptr || maxImages == nullptr) {
+ ALOGE("%s: invalid argument. reader %p, maxImages %p",
+ __FUNCTION__, reader, maxImages);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *maxImages = reader->getMaxImages();
+ return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AImageReader_acquireNextImage(AImageReader* reader, /*out*/AImage** image) {
+ ALOGV("%s", __FUNCTION__);
+ if (reader == nullptr || image == nullptr) {
+ ALOGE("%s: invalid argument. reader %p, maxImages %p",
+ __FUNCTION__, reader, image);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return reader->acquireNextImage(image);
+}
+
+EXPORT
+media_status_t AImageReader_acquireLatestImage(AImageReader* reader, /*out*/AImage** image) {
+ ALOGV("%s", __FUNCTION__);
+ if (reader == nullptr || image == nullptr) {
+ ALOGE("%s: invalid argument. reader %p, maxImages %p",
+ __FUNCTION__, reader, image);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return reader->acquireLatestImage(image);
+}
+
+EXPORT
+media_status_t AImageReader_setImageListener(
+ AImageReader* reader, AImageReader_ImageListener* listener) {
+ ALOGV("%s", __FUNCTION__);
+ if (reader == nullptr || listener == nullptr) {
+ ALOGE("%s: invalid argument! read %p listener %p", __FUNCTION__, reader, listener);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ reader->setImageListener(listener);
+ return AMEDIA_OK;
+}
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
new file mode 100644
index 0000000..48f0953
--- /dev/null
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _NDK_IMAGE_READER_PRIV_H
+#define _NDK_IMAGE_READER_PRIV_H
+
+#include <inttypes.h>
+
+#include "NdkImageReader.h"
+
+#include <utils/List.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+
+#include <gui/CpuConsumer.h>
+#include <gui/Surface.h>
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+using namespace android;
+
+namespace {
+ enum {
+ IMAGE_READER_MAX_NUM_PLANES = 3,
+ };
+
+ enum {
+ ACQUIRE_SUCCESS = 0,
+ ACQUIRE_NO_BUFFERS = 1,
+ ACQUIRE_MAX_IMAGES = 2,
+ };
+}
+
+struct AImageReader : public RefBase {
+ public:
+
+ static bool isSupportedFormat(int32_t format);
+ static int getNumPlanesForFormat(int32_t format);
+
+ AImageReader(int32_t width, int32_t height, int32_t format, int32_t maxImages);
+ ~AImageReader();
+
+ // Inintialize AImageReader, uninitialized or failed to initialize AImageReader
+ // should never be passed to application
+ media_status_t init();
+
+ media_status_t setImageListener(AImageReader_ImageListener* listener);
+
+ media_status_t acquireNextImage(/*out*/AImage** image);
+ media_status_t acquireLatestImage(/*out*/AImage** image);
+
+ ANativeWindow* getWindow() const { return mWindow.get(); };
+ int32_t getWidth() const { return mWidth; };
+ int32_t getHeight() const { return mHeight; };
+ int32_t getFormat() const { return mFormat; };
+ int32_t getMaxImages() const { return mMaxImages; };
+
+
+ private:
+
+ friend struct AImage; // for grabing reader lock
+
+ media_status_t acquireCpuConsumerImageLocked(/*out*/AImage** image);
+ CpuConsumer::LockedBuffer* getLockedBufferLocked();
+ void returnLockedBufferLocked(CpuConsumer::LockedBuffer* buffer);
+
+ // Called by AImage to close image
+ void releaseImageLocked(AImage* image);
+
+ static int getBufferWidth(CpuConsumer::LockedBuffer* buffer);
+ static int getBufferHeight(CpuConsumer::LockedBuffer* buffer);
+
+ media_status_t setImageListenerLocked(AImageReader_ImageListener* listener);
+
+ // definition of handler and message
+ enum {
+ kWhatImageAvailable
+ };
+ static const char* kCallbackFpKey;
+ static const char* kContextKey;
+ class CallbackHandler : public AHandler {
+ public:
+ CallbackHandler(AImageReader* reader) : mReader(reader) {}
+ void onMessageReceived(const sp<AMessage> &msg) override;
+ private:
+ AImageReader* mReader;
+ };
+ sp<CallbackHandler> mHandler;
+ sp<ALooper> mCbLooper; // Looper thread where callbacks actually happen on
+
+ List<CpuConsumer::LockedBuffer*> mBuffers;
+ const int32_t mWidth;
+ const int32_t mHeight;
+ const int32_t mFormat;
+ const int32_t mMaxImages;
+ const int32_t mNumPlanes;
+
+ struct FrameListener : public ConsumerBase::FrameAvailableListener {
+ public:
+ FrameListener(AImageReader* parent) : mReader(parent) {}
+
+ void onFrameAvailable(const BufferItem& item) override;
+
+ media_status_t setImageListener(AImageReader_ImageListener* listener);
+
+ private:
+ AImageReader_ImageListener mListener = {nullptr, nullptr};
+ wp<AImageReader> mReader;
+ Mutex mLock;
+ };
+ sp<FrameListener> mFrameListener;
+
+ int mHalFormat;
+ android_dataspace mHalDataSpace;
+
+ sp<IGraphicBufferProducer> mProducer;
+ sp<Surface> mSurface;
+ sp<CpuConsumer> mCpuConsumer;
+ sp<ANativeWindow> mWindow;
+
+ List<AImage*> mAcquiredImages;
+
+ Mutex mLock;
+};
+
+#endif // _NDK_IMAGE_READER_PRIV_H