Add DataSpace API to convert between ADataSpace and ColorSpace
Also implement getDataSpace functions in SurfaceTexture and Image field
to help with upstream HDR work.
Bug: 201539996
Bug: 201535454
Test: android.hardware.camera2.cts.ImageWriterTest, android.hardware.cts.DataSpaceTest pass
Change-Id: I17fa43c1ba60e6f59da1910cebcef0dab993ce3b
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index bac44ad..f7bd614 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -17,9 +17,12 @@
package android.media;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Rect;
+import android.hardware.DataSpace;
+import android.hardware.DataSpace.NamedDataSpace;
import android.hardware.HardwareBuffer;
import java.nio.ByteBuffer;
@@ -272,6 +275,31 @@
return;
}
+ private @NamedDataSpace long mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+
+ /**
+ * Get the dataspace associated with this frame.
+ */
+ @SuppressLint("MethodNameUnits")
+ public @NamedDataSpace long getDataSpace() {
+ throwISEIfImageIsInvalid();
+ return mDataSpace;
+ }
+
+ /**
+ * Set the dataspace associated with this frame.
+ * <p>
+ * If dataspace for an image is not set, dataspace value depends on {@link android.view.Surface}
+ * that is provided in the {@link ImageWriter} constructor.
+ * </p>
+ *
+ * @param dataSpace The Dataspace to be set for this image
+ */
+ public void setDataSpace(@NamedDataSpace long dataSpace) {
+ throwISEIfImageIsInvalid();
+ mDataSpace = dataSpace;
+ }
+
private Rect mCropRect;
/**
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 5656dff..bd0f32e 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -16,7 +16,6 @@
package android.media;
-import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.graphics.GraphicBuffer;
@@ -28,7 +27,6 @@
import android.hardware.camera2.MultiResolutionImageReader;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.view.Surface;
import dalvik.system.VMRuntime;
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 1b74367..1fc2cf9 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -23,9 +23,9 @@
import android.graphics.ImageFormat.Format;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SurfaceUtils;
-import android.hardware.HardwareBuffer;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -454,8 +454,9 @@
}
Rect crop = image.getCropRect();
- nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), crop.left, crop.top,
- crop.right, crop.bottom, image.getTransform(), image.getScalingMode());
+ nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), image.getDataSpace(),
+ crop.left, crop.top, crop.right, crop.bottom, image.getTransform(),
+ image.getScalingMode());
/**
* Only remove and cleanup the Images that are owned by this
@@ -642,13 +643,13 @@
Rect crop = image.getCropRect();
if (image.getNativeContext() != 0) {
nativeAttachAndQueueImage(mNativeContext, image.getNativeContext(), image.getFormat(),
- image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
- image.getTransform(), image.getScalingMode());
+ image.getTimestamp(), image.getDataSpace(), crop.left, crop.top, crop.right,
+ crop.bottom, image.getTransform(), image.getScalingMode());
} else {
GraphicBuffer gb = GraphicBuffer.createFromHardwareBuffer(image.getHardwareBuffer());
nativeAttachAndQueueGraphicBuffer(mNativeContext, gb, image.getFormat(),
- image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
- image.getTransform(), image.getScalingMode());
+ image.getTimestamp(), image.getDataSpace(), crop.left, crop.top, crop.right,
+ crop.bottom, image.getTransform(), image.getScalingMode());
gb.destroy();
image.close();
}
@@ -976,15 +977,15 @@
private synchronized native void nativeDequeueInputImage(long nativeCtx, Image wi);
private synchronized native void nativeQueueInputImage(long nativeCtx, Image image,
- long timestampNs, int left, int top, int right, int bottom, int transform,
- int scalingMode);
+ long timestampNs, long dataSpace, int left, int top, int right, int bottom,
+ int transform, int scalingMode);
private synchronized native int nativeAttachAndQueueImage(long nativeCtx,
- long imageNativeBuffer, int imageFormat, long timestampNs, int left,
- int top, int right, int bottom, int transform, int scalingMode);
+ long imageNativeBuffer, int imageFormat, long timestampNs, long dataSpace,
+ int left, int top, int right, int bottom, int transform, int scalingMode);
private synchronized native int nativeAttachAndQueueGraphicBuffer(long nativeCtx,
- GraphicBuffer graphicBuffer, int imageFormat, long timestampNs, int left,
- int top, int right, int bottom, int transform, int scalingMode);
+ GraphicBuffer graphicBuffer, int imageFormat, long timestampNs, long dataSpace,
+ int left, int top, int right, int bottom, int transform, int scalingMode);
private synchronized native void cancelImage(long nativeCtx, Image image);
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 5174c0c..021507c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -48,6 +48,7 @@
#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext"
#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mNativeBuffer"
#define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID "mTimestamp"
+#define ANDROID_MEDIA_SURFACEIMAGE_DS_JNI_ID "mDataSpace"
#define ANDROID_MEDIA_SURFACEIMAGE_TF_JNI_ID "mTransform"
#define ANDROID_MEDIA_SURFACEIMAGE_SM_JNI_ID "mScalingMode"
@@ -71,6 +72,7 @@
static struct {
jfieldID mNativeBuffer;
jfieldID mTimestamp;
+ jfieldID mDataSpace;
jfieldID mTransform;
jfieldID mScalingMode;
jfieldID mPlanes;
@@ -319,6 +321,12 @@
"can't find android/graphics/ImageReader.%s",
ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID);
+ gSurfaceImageClassInfo.mDataSpace = env->GetFieldID(
+ imageClazz, ANDROID_MEDIA_SURFACEIMAGE_DS_JNI_ID, "J");
+ LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mDataSpace == NULL,
+ "can't find android/graphics/ImageReader.%s",
+ ANDROID_MEDIA_SURFACEIMAGE_DS_JNI_ID);
+
gSurfaceImageClassInfo.mTransform = env->GetFieldID(
imageClazz, ANDROID_MEDIA_SURFACEIMAGE_TF_JNI_ID, "I");
LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mTransform == NULL,
@@ -619,6 +627,8 @@
Image_setBufferItem(env, image, buffer);
env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
static_cast<jlong>(buffer->mTimestamp));
+ env->SetLongField(image, gSurfaceImageClassInfo.mDataSpace,
+ static_cast<jlong>(buffer->mDataSpace));
auto transform = buffer->mTransform;
if (buffer->mTransformToDisplayInverse) {
transform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index b291ac95b..0a5490d 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -53,6 +53,7 @@
} gImageWriterClassInfo;
static struct {
+ jfieldID mDataSpace;
jfieldID mNativeBuffer;
jfieldID mNativeFenceFd;
jfieldID mPlanes;
@@ -87,6 +88,9 @@
void setBufferHeight(int height) { mHeight = height; }
int getBufferHeight() { return mHeight; }
+ void setBufferDataSpace(android_dataspace dataSpace) { mDataSpace = dataSpace; }
+ android_dataspace getBufferDataSpace() { return mDataSpace; }
+
void queueAttachedFlag(bool isAttached) {
Mutex::Autolock l(mAttachedFlagQueueLock);
mAttachedFlagQueue.push_back(isAttached);
@@ -105,6 +109,7 @@
int mFormat;
int mWidth;
int mHeight;
+ android_dataspace mDataSpace;
// Class for a shared thread used to detach buffers from buffer queues
// to discard buffers after consumers are done using them.
@@ -316,7 +321,7 @@
// -------------------------------Private method declarations--------------
static void Image_setNativeContext(JNIEnv* env, jobject thiz,
- sp<GraphicBuffer> buffer, int fenceFd);
+ sp<GraphicBuffer> buffer, int fenceFd, long dataSpace);
static void Image_getNativeContext(JNIEnv* env, jobject thiz,
GraphicBuffer** buffer, int* fenceFd);
static void Image_unlockIfLocked(JNIEnv* env, jobject thiz);
@@ -328,6 +333,12 @@
jclass imageClazz = env->FindClass("android/media/ImageWriter$WriterSurfaceImage");
LOG_ALWAYS_FATAL_IF(imageClazz == NULL,
"can't find android/media/ImageWriter$WriterSurfaceImage");
+
+ gSurfaceImageClassInfo.mDataSpace = env->GetFieldID(
+ imageClazz, "mDataSpace", "J");
+ LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mDataSpace == NULL,
+ "can't find android/media/ImageWriter$WriterSurfaceImage.mDataSpace");
+
gSurfaceImageClassInfo.mNativeBuffer = env->GetFieldID(
imageClazz, IMAGE_BUFFER_JNI_ID, "J");
LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mNativeBuffer == NULL,
@@ -465,6 +476,7 @@
jniThrowRuntimeException(env, "Failed to set Surface dataspace");
return 0;
}
+ ctx->setBufferDataSpace(nativeDataspace);
surfaceFormat = userFormat;
}
@@ -544,7 +556,7 @@
// 3. need use lockAsync here, as it will handle the dequeued fence for us automatically.
// Finally, set the native info into image object.
- Image_setNativeContext(env, image, buffer, fenceFd);
+ Image_setNativeContext(env, image, buffer, fenceFd, ctx->getBufferDataSpace());
}
static void ImageWriter_close(JNIEnv* env, jobject thiz, jlong nativeCtx) {
@@ -605,12 +617,12 @@
anw->cancelBuffer(anw.get(), buffer, fenceFd);
- Image_setNativeContext(env, image, NULL, -1);
+ Image_setNativeContext(env, image, NULL, -1, HAL_DATASPACE_UNKNOWN);
}
static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image,
- jlong timestampNs, jint left, jint top, jint right, jint bottom, jint transform,
- jint scalingMode) {
+ jlong timestampNs, jlong dataSpace, jint left, jint top, jint right,
+ jint bottom, jint transform, jint scalingMode) {
ALOGV("%s", __FUNCTION__);
JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
if (ctx == NULL || thiz == NULL) {
@@ -642,6 +654,15 @@
return;
}
+ // Set dataSpace
+ ALOGV("dataSpace to be queued: %" PRId64, dataSpace);
+ res = native_window_set_buffers_data_space(
+ anw.get(), static_cast<android_dataspace>(dataSpace));
+ if (res != OK) {
+ jniThrowRuntimeException(env, "Set dataspace failed");
+ return;
+ }
+
// Set crop
android_native_rect_t cropRect;
cropRect.left = left;
@@ -689,12 +710,12 @@
}
// Clear the image native context: end of this image's lifecycle in public API.
- Image_setNativeContext(env, image, NULL, -1);
+ Image_setNativeContext(env, image, NULL, -1, HAL_DATASPACE_UNKNOWN);
}
static status_t attachAndQeueuGraphicBuffer(JNIEnv* env, JNIImageWriterContext *ctx,
- sp<Surface> surface, sp<GraphicBuffer> gb, jlong timestampNs, jint left, jint top,
- jint right, jint bottom, jint transform, jint scalingMode) {
+ sp<Surface> surface, sp<GraphicBuffer> gb, jlong timestampNs, jlong dataSpace,
+ jint left, jint top, jint right, jint bottom, jint transform, jint scalingMode) {
status_t res = OK;
// Step 1. Attach Image
res = surface->attachBuffer(gb.get());
@@ -713,8 +734,8 @@
}
sp < ANativeWindow > anw = surface;
- // Step 2. Set timestamp, crop, transform and scaling mode. Note that we do not need unlock the
- // image because it was not locked.
+ // Step 2. Set timestamp, dataspace, crop, transform and scaling mode.
+ // Note that we do not need unlock the image because it was not locked.
ALOGV("timestamp to be queued: %" PRId64, timestampNs);
res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
if (res != OK) {
@@ -722,6 +743,14 @@
return res;
}
+ ALOGV("dataSpace to be queued: %" PRId64, dataSpace);
+ res = native_window_set_buffers_data_space(
+ anw.get(), static_cast<android_dataspace>(dataSpace));
+ if (res != OK) {
+ jniThrowRuntimeException(env, "Set dataSpace failed");
+ return res;
+ }
+
android_native_rect_t cropRect;
cropRect.left = left;
cropRect.top = top;
@@ -775,8 +804,8 @@
}
static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nativeCtx,
- jlong nativeBuffer, jint imageFormat, jlong timestampNs, jint left, jint top,
- jint right, jint bottom, jint transform, jint scalingMode) {
+ jlong nativeBuffer, jint imageFormat, jlong timestampNs, jlong dataSpace,
+ jint left, jint top, jint right, jint bottom, jint transform, jint scalingMode) {
ALOGV("%s", __FUNCTION__);
JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
if (ctx == NULL || thiz == NULL) {
@@ -801,12 +830,12 @@
return -1;
}
- return attachAndQeueuGraphicBuffer(env, ctx, surface, buffer->mGraphicBuffer, timestampNs, left,
- top, right, bottom, transform, scalingMode);
+ return attachAndQeueuGraphicBuffer(env, ctx, surface, buffer->mGraphicBuffer, timestampNs,
+ dataSpace, left, top, right, bottom, transform, scalingMode);
}
static jint ImageWriter_attachAndQueueGraphicBuffer(JNIEnv* env, jobject thiz, jlong nativeCtx,
- jobject buffer, jint format, jlong timestampNs, jint left, jint top,
+ jobject buffer, jint format, jlong timestampNs, jlong dataSpace, jint left, jint top,
jint right, jint bottom, jint transform, jint scalingMode) {
ALOGV("%s", __FUNCTION__);
JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
@@ -830,9 +859,8 @@
"Trying to attach an invalid graphic buffer");
return -1;
}
-
- return attachAndQeueuGraphicBuffer(env, ctx, surface, graphicBuffer, timestampNs, left,
- top, right, bottom, transform, scalingMode);
+ return attachAndQeueuGraphicBuffer(env, ctx, surface, graphicBuffer, timestampNs,
+ dataSpace, left, top, right, bottom, transform, scalingMode);
}
// --------------------------Image methods---------------------------------------
@@ -853,7 +881,7 @@
}
static void Image_setNativeContext(JNIEnv* env, jobject thiz,
- sp<GraphicBuffer> buffer, int fenceFd) {
+ sp<GraphicBuffer> buffer, int fenceFd, long dataSpace) {
ALOGV("%s:", __FUNCTION__);
GraphicBuffer* p = NULL;
Image_getNativeContext(env, thiz, &p, /*fenceFd*/NULL);
@@ -867,6 +895,8 @@
reinterpret_cast<jlong>(buffer.get()));
env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
+
+ env->SetLongField(thiz, gSurfaceImageClassInfo.mDataSpace, dataSpace);
}
static void Image_unlockIfLocked(JNIEnv* env, jobject thiz) {
@@ -1066,12 +1096,15 @@
{"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIII)J",
(void*)ImageWriter_init },
{"nativeClose", "(J)V", (void*)ImageWriter_close },
- {"nativeAttachAndQueueImage", "(JJIJIIIIII)I", (void*)ImageWriter_attachAndQueueImage },
+ {"nativeAttachAndQueueImage",
+ "(JJIJJIIIIII)I",
+ (void*)ImageWriter_attachAndQueueImage },
{"nativeAttachAndQueueGraphicBuffer",
- "(JLandroid/graphics/GraphicBuffer;IJIIIIII)I",
+ "(JLandroid/graphics/GraphicBuffer;IJJIIIIII)I",
(void*)ImageWriter_attachAndQueueGraphicBuffer },
{"nativeDequeueInputImage", "(JLandroid/media/Image;)V", (void*)ImageWriter_dequeueImage },
- {"nativeQueueInputImage", "(JLandroid/media/Image;JIIIIII)V", (void*)ImageWriter_queueImage },
+ {"nativeQueueInputImage", "(JLandroid/media/Image;JJIIIIII)V",
+ (void*)ImageWriter_queueImage },
{"cancelImage", "(JLandroid/media/Image;)V", (void*)ImageWriter_cancelImage },
};