Refactor AImage/AImageReader
1/ Use BufferItemConsumer instead of CpuConsumer for AImageReader.
2/ Delay lock image in AImage until the first time getPlaneXXX functions
get called.
3/ Add libmedia_jni as dependency so that we can reuse existing code
from android_media_Utils.h
Bug: 35114769
Test: Ran the following CTS test case from CtsCameraTestCases:
NativeCameraDeviceTest, NativeCameraManagerTest,
NativeImageReaderTest, NativeStillCaptureTest
Change-Id: Ia8dc451ea873e8290592deacc7f8d40360382f86
diff --git a/media/ndk/NdkImage.cpp b/media/ndk/NdkImage.cpp
index 40900ad..6c9a644 100644
--- a/media/ndk/NdkImage.cpp
+++ b/media/ndk/NdkImage.cpp
@@ -22,6 +22,8 @@
#include "NdkImagePriv.h"
#include "NdkImageReaderPriv.h"
+#include <android_media_Utils.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <utils/Log.h>
#include "hardware/camera3.h"
@@ -29,11 +31,11 @@
#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
-AImage::AImage(AImageReader* reader, int32_t format,
- CpuConsumer::LockedBuffer* buffer, int64_t timestamp,
+AImage::AImage(AImageReader* reader, int32_t format, uint64_t usage,
+ BufferItem* buffer, int64_t timestamp,
int32_t width, int32_t height, int32_t numPlanes) :
- mReader(reader), mFormat(format),
- mBuffer(buffer), mTimestamp(timestamp),
+ mReader(reader), mFormat(format), mUsage(usage),
+ mBuffer(buffer), mLockedBuffer(nullptr), mTimestamp(timestamp),
mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
}
@@ -66,6 +68,7 @@
// Should have been set to nullptr in releaseImageLocked
// Set to nullptr here for extra safety only
mBuffer = nullptr;
+ mLockedBuffer = nullptr;
mIsClosed = true;
}
@@ -169,8 +172,80 @@
return AMEDIA_OK;
}
+media_status_t AImage::lockImage() {
+ if (mBuffer == nullptr || mBuffer->mGraphicBuffer == nullptr) {
+ LOG_ALWAYS_FATAL("%s: AImage %p has no buffer.", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ if ((mUsage & AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN) == 0) {
+ ALOGE("%s: AImage %p does not have any software read usage bits set, usage=%" PRIu64 "",
+ __FUNCTION__, this, mUsage);
+ return AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE;
+ }
+
+ if (mLockedBuffer != nullptr) {
+ // Return immediately if the image has already been locked.
+ return AMEDIA_OK;
+ }
+
+ auto lockedBuffer = std::make_unique<CpuConsumer::LockedBuffer>();
+
+ uint64_t producerUsage;
+ uint64_t consumerUsage;
+ android_hardware_HardwareBuffer_convertToGrallocUsageBits(
+ &producerUsage, &consumerUsage, mUsage, 0);
+
+ status_t ret =
+ lockImageFromBuffer(mBuffer, consumerUsage, mBuffer->mFence->dup(), lockedBuffer.get());
+ if (ret != OK) {
+ ALOGE("%s: AImage %p failed to lock, error=%d", __FUNCTION__, this, ret);
+ return AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE;
+ }
+
+ ALOGV("%s: Successfully locked the image %p.", __FUNCTION__, this);
+ mLockedBuffer = std::move(lockedBuffer);
+
+ return AMEDIA_OK;
+}
+
+media_status_t AImage::unlockImageIfLocked(int* fenceFd) {
+ if (fenceFd == nullptr) {
+ LOG_ALWAYS_FATAL("%s: fenceFd cannot be null.", __FUNCTION__);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (mBuffer == nullptr || mBuffer->mGraphicBuffer == nullptr) {
+ LOG_ALWAYS_FATAL("%s: AImage %p has no buffer.", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ if (mLockedBuffer == nullptr) {
+ // This image hasn't been locked yet, no need to unlock.
+ *fenceFd = -1;
+ return AMEDIA_OK;
+ }
+
+ // No fence by default.
+ int releaseFenceFd = -1;
+ status_t res = mBuffer->mGraphicBuffer->unlockAsync(&releaseFenceFd);
+ if (res != OK) {
+ ALOGE("%s unlock buffer failed on iamge %p.", __FUNCTION__, this);
+ *fenceFd = -1;
+ return AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE;
+ }
+
+ *fenceFd = releaseFenceFd;
+ return AMEDIA_OK;
+}
+
media_status_t
AImage::getPlanePixelStride(int planeIdx, /*out*/int32_t* pixelStride) const {
+ if (mLockedBuffer == nullptr) {
+ ALOGE("%s: buffer not locked.", __FUNCTION__);
+ return AMEDIA_IMGREADER_IMAGE_NOT_LOCKED;
+ }
+
if (planeIdx < 0 || planeIdx >= mNumPlanes) {
ALOGE("Error: planeIdx %d out of bound [0,%d]",
planeIdx, mNumPlanes - 1);
@@ -183,10 +258,10 @@
ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
return AMEDIA_ERROR_INVALID_OBJECT;
}
- int32_t fmt = mBuffer->flexFormat;
+ int32_t fmt = mLockedBuffer->flexFormat;
switch (fmt) {
case HAL_PIXEL_FORMAT_YCbCr_420_888:
- *pixelStride = (planeIdx == 0) ? 1 : mBuffer->chromaStep;
+ *pixelStride = (planeIdx == 0) ? 1 : mLockedBuffer->chromaStep;
return AMEDIA_OK;
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
*pixelStride = (planeIdx == 0) ? 1 : 2;
@@ -226,6 +301,11 @@
media_status_t
AImage::getPlaneRowStride(int planeIdx, /*out*/int32_t* rowStride) const {
+ if (mLockedBuffer == nullptr) {
+ ALOGE("%s: buffer not locked.", __FUNCTION__);
+ return AMEDIA_IMGREADER_IMAGE_NOT_LOCKED;
+ }
+
if (planeIdx < 0 || planeIdx >= mNumPlanes) {
ALOGE("Error: planeIdx %d out of bound [0,%d]",
planeIdx, mNumPlanes - 1);
@@ -238,54 +318,58 @@
ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
return AMEDIA_ERROR_INVALID_OBJECT;
}
- int32_t fmt = mBuffer->flexFormat;
+ int32_t fmt = mLockedBuffer->flexFormat;
switch (fmt) {
case HAL_PIXEL_FORMAT_YCbCr_420_888:
- *rowStride = (planeIdx == 0) ? mBuffer->stride : mBuffer->chromaStride;
+ *rowStride = (planeIdx == 0) ? mLockedBuffer->stride
+ : mLockedBuffer->chromaStride;
return AMEDIA_OK;
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- *rowStride = mBuffer->width;
+ *rowStride = mLockedBuffer->width;
return AMEDIA_OK;
case HAL_PIXEL_FORMAT_YV12:
- if (mBuffer->stride % 16) {
- ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
+ if (mLockedBuffer->stride % 16) {
+ ALOGE("Stride %d is not 16 pixel aligned!", mLockedBuffer->stride);
return AMEDIA_ERROR_UNKNOWN;
}
- *rowStride = (planeIdx == 0) ? mBuffer->stride : ALIGN(mBuffer->stride / 2, 16);
+ *rowStride = (planeIdx == 0) ? mLockedBuffer->stride
+ : ALIGN(mLockedBuffer->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;
+ *rowStride = mLockedBuffer->stride;
return AMEDIA_OK;
case HAL_PIXEL_FORMAT_Y8:
- if (mBuffer->stride % 16) {
- ALOGE("Stride %d is not 16 pixel aligned!", mBuffer->stride);
+ if (mLockedBuffer->stride % 16) {
+ ALOGE("Stride %d is not 16 pixel aligned!",
+ mLockedBuffer->stride);
return AMEDIA_ERROR_UNKNOWN;
}
- *rowStride = mBuffer->stride;
+ *rowStride = mLockedBuffer->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);
+ if (mLockedBuffer->stride % 16) {
+ ALOGE("Stride %d is not 16 pixel aligned!",
+ mLockedBuffer->stride);
return AMEDIA_ERROR_UNKNOWN;
}
- *rowStride = mBuffer->stride * 2;
+ *rowStride = mLockedBuffer->stride * 2;
return AMEDIA_OK;
case HAL_PIXEL_FORMAT_RGB_565:
- *rowStride = mBuffer->stride * 2;
+ *rowStride = mLockedBuffer->stride * 2;
return AMEDIA_OK;
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
- *rowStride = mBuffer->stride * 4;
+ *rowStride = mLockedBuffer->stride * 4;
return AMEDIA_OK;
case HAL_PIXEL_FORMAT_RGB_888:
// Single plane, 24bpp.
- *rowStride = mBuffer->stride * 3;
+ *rowStride = mLockedBuffer->stride * 3;
return AMEDIA_OK;
case HAL_PIXEL_FORMAT_BLOB:
case HAL_PIXEL_FORMAT_RAW_OPAQUE:
@@ -300,13 +384,13 @@
uint32_t
AImage::getJpegSize() const {
- if (mBuffer == nullptr) {
+ if (mLockedBuffer == nullptr) {
LOG_ALWAYS_FATAL("Error: buffer is null");
}
uint32_t size = 0;
- uint32_t width = mBuffer->width;
- uint8_t* jpegBuffer = mBuffer->data;
+ uint32_t width = mLockedBuffer->width;
+ uint8_t* jpegBuffer = mLockedBuffer->data;
// First check for JPEG transport header at the end of the buffer
uint8_t* header = jpegBuffer + (width - sizeof(struct camera3_jpeg_blob));
@@ -334,6 +418,11 @@
media_status_t
AImage::getPlaneData(int planeIdx,/*out*/uint8_t** data, /*out*/int* dataLength) const {
+ if (mLockedBuffer == nullptr) {
+ ALOGE("%s: buffer not locked.", __FUNCTION__);
+ return AMEDIA_IMGREADER_IMAGE_NOT_LOCKED;
+ }
+
if (planeIdx < 0 || planeIdx >= mNumPlanes) {
ALOGE("Error: planeIdx %d out of bound [0,%d]",
planeIdx, mNumPlanes - 1);
@@ -352,140 +441,154 @@
uint8_t* cr = nullptr;
uint8_t* pData = nullptr;
int bytesPerPixel = 0;
- int32_t fmt = mBuffer->flexFormat;
+ int32_t fmt = mLockedBuffer->flexFormat;
switch (fmt) {
case HAL_PIXEL_FORMAT_YCbCr_420_888:
- pData = (planeIdx == 0) ? mBuffer->data :
- (planeIdx == 1) ? mBuffer->dataCb : mBuffer->dataCr;
+ pData = (planeIdx == 0) ? mLockedBuffer->data
+ : (planeIdx == 1) ? mLockedBuffer->dataCb
+ : mLockedBuffer->dataCr;
// only map until last pixel
if (planeIdx == 0) {
- dataSize = mBuffer->stride * (mBuffer->height - 1) + mBuffer->width;
+ dataSize = mLockedBuffer->stride * (mLockedBuffer->height - 1) +
+ mLockedBuffer->width;
} else {
- dataSize = mBuffer->chromaStride * (mBuffer->height / 2 - 1) +
- mBuffer->chromaStep * (mBuffer->width / 2 - 1) + 1;
+ dataSize =
+ mLockedBuffer->chromaStride *
+ (mLockedBuffer->height / 2 - 1) +
+ mLockedBuffer->chromaStep * (mLockedBuffer->width / 2 - 1) +
+ 1;
}
break;
// NV21
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- cr = mBuffer->data + (mBuffer->stride * mBuffer->height);
+ cr = mLockedBuffer->data +
+ (mLockedBuffer->stride * mLockedBuffer->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;
+ ySize = mLockedBuffer->width * (mLockedBuffer->height - 1) +
+ mLockedBuffer->width;
+ cSize = mLockedBuffer->width * (mLockedBuffer->height / 2 - 1) +
+ mLockedBuffer->width - 1;
+ pData = (planeIdx == 0) ? mLockedBuffer->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);
+ if (mLockedBuffer->stride % 16) {
+ ALOGE("Stride %d is not 16 pixel aligned!",
+ mLockedBuffer->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;
+ ySize = mLockedBuffer->stride * mLockedBuffer->height;
+ cStride = ALIGN(mLockedBuffer->stride / 2, 16);
+ cr = mLockedBuffer->data + ySize;
+ cSize = cStride * mLockedBuffer->height / 2;
cb = cr + cSize;
- pData = (planeIdx == 0) ? mBuffer->data :
- (planeIdx == 1) ? cb : cr;
+ pData = (planeIdx == 0) ? mLockedBuffer->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;
+ pData = mLockedBuffer->data;
+ dataSize = mLockedBuffer->stride * mLockedBuffer->height;
break;
case HAL_PIXEL_FORMAT_Y16:
bytesPerPixel = 2;
- pData = mBuffer->data;
- dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
+ pData = mLockedBuffer->data;
+ dataSize =
+ mLockedBuffer->stride * mLockedBuffer->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);
+ if (mLockedBuffer->height != 1) {
+ ALOGE("Jpeg should have height value one but got %d",
+ mLockedBuffer->height);
return AMEDIA_ERROR_UNKNOWN;
}
- pData = mBuffer->data;
+ pData = mLockedBuffer->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;
+ pData = mLockedBuffer->data;
+ dataSize =
+ mLockedBuffer->stride * mLockedBuffer->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);
+ if (mLockedBuffer->height != 1) {
+ ALOGE("RAW_OPAQUE should have height value one but got %d",
+ mLockedBuffer->height);
return AMEDIA_ERROR_UNKNOWN;
}
- pData = mBuffer->data;
- dataSize = mBuffer->width;
+ pData = mLockedBuffer->data;
+ dataSize = mLockedBuffer->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);
+ if (mLockedBuffer->width % 4) {
+ ALOGE("Width is not multiple of 4 %d", mLockedBuffer->width);
return AMEDIA_ERROR_UNKNOWN;
}
- if (mBuffer->height % 2) {
- ALOGE("Height is not multiple of 2 %d", mBuffer->height);
+ if (mLockedBuffer->height % 2) {
+ ALOGE("Height is not multiple of 2 %d", mLockedBuffer->height);
return AMEDIA_ERROR_UNKNOWN;
}
- if (mBuffer->stride < (mBuffer->width * 10 / 8)) {
+ if (mLockedBuffer->stride < (mLockedBuffer->width * 10 / 8)) {
ALOGE("stride (%d) should be at least %d",
- mBuffer->stride, mBuffer->width * 10 / 8);
+ mLockedBuffer->stride, mLockedBuffer->width * 10 / 8);
return AMEDIA_ERROR_UNKNOWN;
}
- pData = mBuffer->data;
- dataSize = mBuffer->stride * mBuffer->height;
+ pData = mLockedBuffer->data;
+ dataSize = mLockedBuffer->stride * mLockedBuffer->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);
+ if (mLockedBuffer->width % 4) {
+ ALOGE("Width is not multiple of 4 %d", mLockedBuffer->width);
return AMEDIA_ERROR_UNKNOWN;
}
- if (mBuffer->height % 2) {
- ALOGE("Height is not multiple of 2 %d", mBuffer->height);
+ if (mLockedBuffer->height % 2) {
+ ALOGE("Height is not multiple of 2 %d", mLockedBuffer->height);
return AMEDIA_ERROR_UNKNOWN;
}
- if (mBuffer->stride < (mBuffer->width * 12 / 8)) {
+ if (mLockedBuffer->stride < (mLockedBuffer->width * 12 / 8)) {
ALOGE("stride (%d) should be at least %d",
- mBuffer->stride, mBuffer->width * 12 / 8);
+ mLockedBuffer->stride, mLockedBuffer->width * 12 / 8);
return AMEDIA_ERROR_UNKNOWN;
}
- pData = mBuffer->data;
- dataSize = mBuffer->stride * mBuffer->height;
+ pData = mLockedBuffer->data;
+ dataSize = mLockedBuffer->stride * mLockedBuffer->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;
+ pData = mLockedBuffer->data;
+ dataSize =
+ mLockedBuffer->stride * mLockedBuffer->height * bytesPerPixel;
break;
case HAL_PIXEL_FORMAT_RGB_565:
// Single plane, 16bpp.
bytesPerPixel = 2;
- pData = mBuffer->data;
- dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
+ pData = mLockedBuffer->data;
+ dataSize =
+ mLockedBuffer->stride * mLockedBuffer->height * bytesPerPixel;
break;
case HAL_PIXEL_FORMAT_RGB_888:
// Single plane, 24bpp.
bytesPerPixel = 3;
- pData = mBuffer->data;
- dataSize = mBuffer->stride * mBuffer->height * bytesPerPixel;
+ pData = mLockedBuffer->data;
+ dataSize = mLockedBuffer->stride * mLockedBuffer->height * bytesPerPixel;
break;
default:
ALOGE("Pixel format: 0x%x is unsupported", fmt);
@@ -602,6 +705,12 @@
__FUNCTION__, image, pixelStride);
return AMEDIA_ERROR_INVALID_PARAMETER;
}
+ media_status_t ret = const_cast<AImage*>(image)->lockImage();
+ if (ret != AMEDIA_OK) {
+ ALOGE("%s: failed to lock buffer for CPU access. image %p, error=%d.",
+ __FUNCTION__, image, ret);
+ return ret;
+ }
return image->getPlanePixelStride(planeIdx, pixelStride);
}
@@ -614,6 +723,12 @@
__FUNCTION__, image, rowStride);
return AMEDIA_ERROR_INVALID_PARAMETER;
}
+ media_status_t ret = const_cast<AImage*>(image)->lockImage();
+ if (ret != AMEDIA_OK) {
+ ALOGE("%s: failed to lock buffer for CPU access. image %p, error=%d.",
+ __FUNCTION__, image, ret);
+ return ret;
+ }
return image->getPlaneRowStride(planeIdx, rowStride);
}
@@ -627,5 +742,11 @@
__FUNCTION__, image, data, dataLength);
return AMEDIA_ERROR_INVALID_PARAMETER;
}
+ media_status_t ret = const_cast<AImage*>(image)->lockImage();
+ if (ret != AMEDIA_OK) {
+ ALOGE("%s: failed to lock buffer for CPU access. image %p, error=%d.",
+ __FUNCTION__, image, ret);
+ return ret;
+ }
return image->getPlaneData(planeIdx, data, dataLength);
}