camera2: Switch to using YV12 for ImageReader.
Bug: 15116722
- Also fixes incorrect frame number for single captures.
Change-Id: I8552124d18ad176e6724f089a1e3a3f49a5eeec4
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 50515a2..cb951b3 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -174,13 +174,17 @@
private final RequestThreadManager mRequestThreadManager;
/**
- * Check if a given surface uses {@link ImageFormat#YUV_420_888} format.
+ * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
+ * converted to this; YV12 and NV21 are the two currently supported formats.
*
* @param s the surface to check.
- * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888}.
+ * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
+ * format.
*/
static boolean needsConversion(Surface s) {
- return LegacyCameraDevice.nativeDetectSurfaceType(s) == ImageFormat.YUV_420_888;
+ int nativeType = LegacyCameraDevice.nativeDetectSurfaceType(s);
+ return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
+ nativeType == ImageFormat.NV21;
}
/**
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 6fa2134..7f23561 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -49,7 +49,7 @@
private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // ms
- private static final long APPROXIMATE_SENSOR_AREA = (1 << 20); // 8mp
+ private static final long APPROXIMATE_SENSOR_AREA = (1 << 23); // 8mp
private static final long APPROXIMATE_JPEG_ENCODE_TIME = 600; // ms
private static final long NS_PER_MS = 1000000;
diff --git a/core/java/android/hardware/camera2/legacy/RequestQueue.java b/core/java/android/hardware/camera2/legacy/RequestQueue.java
index 5c68303..6bedc48 100644
--- a/core/java/android/hardware/camera2/legacy/RequestQueue.java
+++ b/core/java/android/hardware/camera2/legacy/RequestQueue.java
@@ -122,7 +122,7 @@
for (BurstHolder b : mRequestQueue) {
total += b.getNumberOfRequests();
if (b.getRequestId() == requestId) {
- return total;
+ return total - 1;
}
}
throw new IllegalStateException(
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index e0f3429..a4b1099 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -239,6 +239,9 @@
mGLThreadManager.queueNewFrame(holder.getHolderTargets());
}
+ /**
+ * TODO: Get timestamp from GL thread after buffer update.
+ */
mLastPreviewTimestamp = surfaceTexture.getTimestamp();
mReceivedPreview.open();
}
@@ -495,7 +498,6 @@
if (holder.hasJpegTargets()) {
mReceivedJpeg.close();
doJpegCapture(holder);
- mReceivedJpeg.block();
if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
// TODO: report error to CameraDevice
Log.e(TAG, "Hit timeout for jpeg callback!");
@@ -507,6 +509,9 @@
// TODO: err handling
throw new IOError(e);
}
+ if (timestamp == 0) {
+ timestamp = SystemClock.elapsedRealtimeNanos();
+ }
CameraMetadataNative result = LegacyMetadataMapper.convertResultMetadata(mParams,
request, timestamp);
mDeviceState.setCaptureResult(holder, result);
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index e9d32f0..bbc7005 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -439,7 +439,7 @@
for (Surface s : surfaces) {
// If pixel conversions aren't handled by egl, use a pbuffer
if (LegacyCameraDevice.needsConversion(s)) {
- LegacyCameraDevice.nativeSetSurfaceFormat(s, ImageFormat.NV21);
+ LegacyCameraDevice.nativeSetSurfaceFormat(s, ImageFormat.YV12);
EGLSurfaceHolder holder = new EGLSurfaceHolder();
holder.surface = s;
mConversionSurfaces.add(holder);
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 40e9544..0048426 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -35,6 +35,8 @@
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
+#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
+
/**
* Convert from RGB 888 to Y'CbCr using the conversion specified in ITU-R BT.601 for
* digital RGB with K_b = 0.114, and K_r = 0.299.
@@ -152,6 +154,11 @@
ANativeWindowBuffer* anb;
ALOGV("%s: Dequeue buffer from %p",__FUNCTION__, anw.get());
+ if (width < 0 || height < 0 || bufSize < 0) {
+ ALOGE("%s: Illegal argument, negative dimension passed to produceFrame", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
// TODO: Switch to using Surface::lock and Surface::unlockAndPost
err = native_window_dequeue_buffer_and_wait(anw.get(), &anb);
if (err != NO_ERROR) return err;
@@ -181,6 +188,41 @@
uPlane, vPlane, chromaStep, yStride, chromaStride);
break;
}
+ case HAL_PIXEL_FORMAT_YV12: {
+ if (bufSize < width * height * 4) {
+ ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
+ bufSize);
+ return BAD_VALUE;
+ }
+
+ if ((width & 1) || (height & 1)) {
+ ALOGE("%s: Dimens %dx%d are not divisible by 2.", __FUNCTION__, width, height);
+ return BAD_VALUE;
+ }
+
+ uint8_t* img = NULL;
+ ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
+ err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+ if (err != NO_ERROR) {
+ ALOGE("%s: Error %s (%d) while locking gralloc buffer for write.", __FUNCTION__,
+ strerror(-err), err);
+ return err;
+ }
+
+ uint32_t stride = buf->getStride();
+ LOG_ALWAYS_FATAL_IF(stride % 16, "Stride is not 16 pixel aligned %d", stride);
+
+ uint32_t cStride = ALIGN(stride / 2, 16);
+ size_t chromaStep = 1;
+
+ uint8_t* yPlane = img;
+ uint8_t* crPlane = img + static_cast<uint32_t>(height) * stride;
+ uint8_t* cbPlane = crPlane + cStride * static_cast<uint32_t>(height) / 2;
+
+ rgbToYuv420(pixelBuffer, width, height, yPlane,
+ crPlane, cbPlane, chromaStep, stride, cStride);
+ break;
+ }
case HAL_PIXEL_FORMAT_YCbCr_420_888: {
// Software writes with YCbCr_420_888 format are unsupported
// by the gralloc module for now
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 36cfb0f..41ed9e1 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -767,11 +767,12 @@
int imgReaderFmt = ctx->getBufferFormat();
int bufFmt = buffer->format;
if (imgReaderFmt != bufFmt) {
- // Special casing for when producer switches format
- if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && bufFmt ==
- HAL_PIXEL_FORMAT_YCrCb_420_SP) {
- ctx->setBufferFormat(HAL_PIXEL_FORMAT_YCrCb_420_SP);
- ALOGV("%s: Overriding NV21 to YUV_420_888.", __FUNCTION__);
+ // Special casing for when producer switches to a format compatible with flexible YUV
+ // (HAL_PIXEL_FORMAT_YCbCr_420_888).
+ if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && (bufFmt ==
+ HAL_PIXEL_FORMAT_YCrCb_420_SP || bufFmt == HAL_PIXEL_FORMAT_YV12)) {
+ ctx->setBufferFormat(bufFmt);
+ ALOGV("%s: Overriding buffer format YUV_420_888 to %x.", __FUNCTION__, bufFmt);
} else {
// Return the buffer to the queue.
consumer->unlockBuffer(*buffer);