blob: 5d959a3930f474898e59a063383fbb9a0de50d7e [file] [log] [blame]
Zhijun Hef6a09e52015-02-24 18:12:23 -08001/*
2 * Copyright 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "ImageWriter_JNI"
Zhijun He0ab41622016-02-25 16:00:38 -080019#include "android_media_Utils.h"
20
Yin-Chia Yeh6132b022019-02-14 16:40:20 -080021#include <utils/Condition.h>
Zhijun Hef6a09e52015-02-24 18:12:23 -080022#include <utils/Log.h>
Yin-Chia Yeh6132b022019-02-14 16:40:20 -080023#include <utils/Mutex.h>
Zhijun Hef6a09e52015-02-24 18:12:23 -080024#include <utils/String8.h>
Yin-Chia Yeh6132b022019-02-14 16:40:20 -080025#include <utils/Thread.h>
Zhijun Hef6a09e52015-02-24 18:12:23 -080026
27#include <gui/IProducerListener.h>
28#include <gui/Surface.h>
Yin-Chia Yeh6dc192e2019-07-30 15:29:16 -070029#include <ui/PublicFormat.h>
Zhijun Hef6a09e52015-02-24 18:12:23 -080030#include <android_runtime/AndroidRuntime.h>
31#include <android_runtime/android_view_Surface.h>
Emilian Peev9c778e12020-10-29 17:36:22 -070032#include <android_runtime/android_graphics_GraphicBuffer.h>
rennbe092192018-05-07 10:18:05 -070033#include <android_runtime/android_hardware_HardwareBuffer.h>
34#include <private/android/AHardwareBufferHelpers.h>
Zhijun Hef6a09e52015-02-24 18:12:23 -080035#include <jni.h>
Steven Moreland2279b252017-07-19 09:50:45 -070036#include <nativehelper/JNIHelp.h>
Zhijun Hef6a09e52015-02-24 18:12:23 -080037
38#include <stdint.h>
39#include <inttypes.h>
rennbe092192018-05-07 10:18:05 -070040#include <android/hardware_buffer_jni.h>
Zhijun Hef6a09e52015-02-24 18:12:23 -080041
Yin-Chia Yeh6132b022019-02-14 16:40:20 -080042#include <deque>
43
Zhijun Hef6a09e52015-02-24 18:12:23 -080044#define IMAGE_BUFFER_JNI_ID "mNativeBuffer"
Zhijun He916d8ac2017-03-22 17:33:47 -070045#define IMAGE_FORMAT_UNKNOWN 0 // This is the same value as ImageFormat#UNKNOWN.
Zhijun Hef6a09e52015-02-24 18:12:23 -080046// ----------------------------------------------------------------------------
47
48using namespace android;
49
Zhijun Hef6a09e52015-02-24 18:12:23 -080050static struct {
51 jmethodID postEventFromNative;
52 jfieldID mWriterFormat;
53} gImageWriterClassInfo;
54
55static struct {
56 jfieldID mNativeBuffer;
57 jfieldID mNativeFenceFd;
58 jfieldID mPlanes;
59} gSurfaceImageClassInfo;
60
61static struct {
62 jclass clazz;
63 jmethodID ctor;
64} gSurfacePlaneClassInfo;
65
Zhijun Hef6a09e52015-02-24 18:12:23 -080066// ----------------------------------------------------------------------------
67
68class JNIImageWriterContext : public BnProducerListener {
69public:
70 JNIImageWriterContext(JNIEnv* env, jobject weakThiz, jclass clazz);
71
72 virtual ~JNIImageWriterContext();
73
74 // Implementation of IProducerListener, used to notify the ImageWriter that the consumer
75 // has returned a buffer and it is ready for ImageWriter to dequeue.
76 virtual void onBufferReleased();
77
Zhijun Hece9d6f92015-03-29 16:33:59 -070078 void setProducer(const sp<Surface>& producer) { mProducer = producer; }
79 Surface* getProducer() { return mProducer.get(); }
Zhijun Hef6a09e52015-02-24 18:12:23 -080080
81 void setBufferFormat(int format) { mFormat = format; }
82 int getBufferFormat() { return mFormat; }
83
84 void setBufferWidth(int width) { mWidth = width; }
85 int getBufferWidth() { return mWidth; }
86
87 void setBufferHeight(int height) { mHeight = height; }
88 int getBufferHeight() { return mHeight; }
89
Shuzhen Wangb9adb572020-06-24 09:33:27 -070090 void queueAttachedFlag(bool isAttached) {
91 Mutex::Autolock l(mAttachedFlagQueueLock);
92 mAttachedFlagQueue.push_back(isAttached);
93 }
94 void dequeueAttachedFlag() {
95 Mutex::Autolock l(mAttachedFlagQueueLock);
96 mAttachedFlagQueue.pop_back();
97 }
Zhijun Hef6a09e52015-02-24 18:12:23 -080098private:
99 static JNIEnv* getJNIEnv(bool* needsDetach);
100 static void detachJNI();
101
Zhijun Hece9d6f92015-03-29 16:33:59 -0700102 sp<Surface> mProducer;
Zhijun Hef6a09e52015-02-24 18:12:23 -0800103 jobject mWeakThiz;
104 jclass mClazz;
105 int mFormat;
106 int mWidth;
107 int mHeight;
Yin-Chia Yeh6132b022019-02-14 16:40:20 -0800108
109 // Class for a shared thread used to detach buffers from buffer queues
110 // to discard buffers after consumers are done using them.
111 // This is needed because detaching buffers in onBufferReleased callback
112 // can lead to deadlock when consumer/producer are on the same process.
113 class BufferDetacher {
114 public:
115 // Called by JNIImageWriterContext ctor. Will start the thread for first ref.
116 void addRef();
117 // Called by JNIImageWriterContext dtor. Will stop the thread after ref goes to 0.
118 void removeRef();
119 // Called by onBufferReleased to signal this thread to detach a buffer
120 void detach(wp<Surface>);
121
122 private:
123
124 class DetachThread : public Thread {
125 public:
126 DetachThread() : Thread(/*canCallJava*/false) {};
127
128 void detach(wp<Surface>);
129
130 virtual void requestExit() override;
131
132 private:
133 virtual bool threadLoop() override;
134
135 Mutex mLock;
136 Condition mCondition;
137 std::deque<wp<Surface>> mQueue;
138
Yin-Chia Yehedace1a2019-07-30 13:02:59 -0700139 static const nsecs_t kWaitDuration = 500000000; // 500 ms
Yin-Chia Yeh6132b022019-02-14 16:40:20 -0800140 };
141 sp<DetachThread> mThread;
142
143 Mutex mLock;
144 int mRefCount = 0;
145 };
146
147 static BufferDetacher sBufferDetacher;
Shuzhen Wangb9adb572020-06-24 09:33:27 -0700148
149 // Buffer queue guarantees both producer and consumer side buffer flows are
150 // in order. See b/19977520. As a result, we can use a queue here.
151 Mutex mAttachedFlagQueueLock;
152 std::deque<bool> mAttachedFlagQueue;
Zhijun Hef6a09e52015-02-24 18:12:23 -0800153};
154
Yin-Chia Yeh6132b022019-02-14 16:40:20 -0800155JNIImageWriterContext::BufferDetacher JNIImageWriterContext::sBufferDetacher;
156
157void JNIImageWriterContext::BufferDetacher::addRef() {
158 Mutex::Autolock l(mLock);
159 mRefCount++;
160 if (mRefCount == 1) {
161 mThread = new DetachThread();
162 mThread->run("BufDtchThrd");
163 }
164}
165
166void JNIImageWriterContext::BufferDetacher::removeRef() {
167 Mutex::Autolock l(mLock);
168 mRefCount--;
169 if (mRefCount == 0) {
170 mThread->requestExit();
171 mThread->join();
172 mThread.clear();
173 }
174}
175
176void JNIImageWriterContext::BufferDetacher::detach(wp<Surface> bq) {
177 Mutex::Autolock l(mLock);
178 if (mThread == nullptr) {
179 ALOGE("%s: buffer detach thread is gone!", __FUNCTION__);
180 return;
181 }
182 mThread->detach(bq);
183}
184
185void JNIImageWriterContext::BufferDetacher::DetachThread::detach(wp<Surface> bq) {
186 Mutex::Autolock l(mLock);
187 mQueue.push_back(bq);
188 mCondition.signal();
189}
190
191void JNIImageWriterContext::BufferDetacher::DetachThread::requestExit() {
192 Thread::requestExit();
193 {
194 Mutex::Autolock l(mLock);
195 mQueue.clear();
196 }
197 mCondition.signal();
198}
199
200bool JNIImageWriterContext::BufferDetacher::DetachThread::threadLoop() {
201 Mutex::Autolock l(mLock);
202 mCondition.waitRelative(mLock, kWaitDuration);
203
204 while (!mQueue.empty()) {
205 if (exitPending()) {
206 return false;
207 }
208
209 wp<Surface> wbq = mQueue.front();
210 mQueue.pop_front();
211 sp<Surface> bq = wbq.promote();
212 if (bq != nullptr) {
213 sp<Fence> fence;
214 sp<GraphicBuffer> buffer;
215 ALOGV("%s: One buffer is detached", __FUNCTION__);
216 mLock.unlock();
217 bq->detachNextBuffer(&buffer, &fence);
218 mLock.lock();
219 }
220 }
221 return !exitPending();
222}
223
Zhijun Hef6a09e52015-02-24 18:12:23 -0800224JNIImageWriterContext::JNIImageWriterContext(JNIEnv* env, jobject weakThiz, jclass clazz) :
Yin-Chia Yeh6132b022019-02-14 16:40:20 -0800225 mWeakThiz(env->NewGlobalRef(weakThiz)),
226 mClazz((jclass)env->NewGlobalRef(clazz)),
227 mFormat(0),
228 mWidth(-1),
229 mHeight(-1) {
230 sBufferDetacher.addRef();
Zhijun Hef6a09e52015-02-24 18:12:23 -0800231}
232
233JNIImageWriterContext::~JNIImageWriterContext() {
234 ALOGV("%s", __FUNCTION__);
235 bool needsDetach = false;
236 JNIEnv* env = getJNIEnv(&needsDetach);
237 if (env != NULL) {
238 env->DeleteGlobalRef(mWeakThiz);
239 env->DeleteGlobalRef(mClazz);
240 } else {
241 ALOGW("leaking JNI object references");
242 }
243 if (needsDetach) {
244 detachJNI();
245 }
246
247 mProducer.clear();
Yin-Chia Yeh6132b022019-02-14 16:40:20 -0800248 sBufferDetacher.removeRef();
Zhijun Hef6a09e52015-02-24 18:12:23 -0800249}
250
251JNIEnv* JNIImageWriterContext::getJNIEnv(bool* needsDetach) {
252 ALOGV("%s", __FUNCTION__);
253 LOG_ALWAYS_FATAL_IF(needsDetach == NULL, "needsDetach is null!!!");
254 *needsDetach = false;
255 JNIEnv* env = AndroidRuntime::getJNIEnv();
256 if (env == NULL) {
257 JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
258 JavaVM* vm = AndroidRuntime::getJavaVM();
259 int result = vm->AttachCurrentThread(&env, (void*) &args);
260 if (result != JNI_OK) {
261 ALOGE("thread attach failed: %#x", result);
262 return NULL;
263 }
264 *needsDetach = true;
265 }
266 return env;
267}
268
269void JNIImageWriterContext::detachJNI() {
270 ALOGV("%s", __FUNCTION__);
271 JavaVM* vm = AndroidRuntime::getJavaVM();
272 int result = vm->DetachCurrentThread();
273 if (result != JNI_OK) {
274 ALOGE("thread detach failed: %#x", result);
275 }
276}
277
278void JNIImageWriterContext::onBufferReleased() {
279 ALOGV("%s: buffer released", __FUNCTION__);
280 bool needsDetach = false;
281 JNIEnv* env = getJNIEnv(&needsDetach);
Shuzhen Wangb9adb572020-06-24 09:33:27 -0700282
283 bool bufferIsAttached = false;
284 {
285 Mutex::Autolock l(mAttachedFlagQueueLock);
286 if (!mAttachedFlagQueue.empty()) {
287 bufferIsAttached = mAttachedFlagQueue.front();
288 mAttachedFlagQueue.pop_front();
289 } else {
290 ALOGW("onBufferReleased called with no attached flag queued");
291 }
292 }
293
Zhijun Hef6a09e52015-02-24 18:12:23 -0800294 if (env != NULL) {
Zhijun Hece9d6f92015-03-29 16:33:59 -0700295 // Detach the buffer every time when a buffer consumption is done,
296 // need let this callback give a BufferItem, then only detach if it was attached to this
Shuzhen Wangb9adb572020-06-24 09:33:27 -0700297 // Writer. see b/19977520
298 if (mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED || bufferIsAttached) {
Yin-Chia Yeh6132b022019-02-14 16:40:20 -0800299 sBufferDetacher.detach(mProducer);
Zhijun Hece9d6f92015-03-29 16:33:59 -0700300 }
301
Zhijun Hef6a09e52015-02-24 18:12:23 -0800302 env->CallStaticVoidMethod(mClazz, gImageWriterClassInfo.postEventFromNative, mWeakThiz);
303 } else {
304 ALOGW("onBufferReleased event will not posted");
305 }
Zhijun Hece9d6f92015-03-29 16:33:59 -0700306
Zhijun Hef6a09e52015-02-24 18:12:23 -0800307 if (needsDetach) {
308 detachJNI();
309 }
310}
311
312// ----------------------------------------------------------------------------
313
314extern "C" {
315
316// -------------------------------Private method declarations--------------
317
Zhijun Hef6a09e52015-02-24 18:12:23 -0800318static void Image_setNativeContext(JNIEnv* env, jobject thiz,
319 sp<GraphicBuffer> buffer, int fenceFd);
320static void Image_getNativeContext(JNIEnv* env, jobject thiz,
321 GraphicBuffer** buffer, int* fenceFd);
322static void Image_unlockIfLocked(JNIEnv* env, jobject thiz);
323
324// --------------------------ImageWriter methods---------------------------------------
325
326static void ImageWriter_classInit(JNIEnv* env, jclass clazz) {
327 ALOGV("%s:", __FUNCTION__);
328 jclass imageClazz = env->FindClass("android/media/ImageWriter$WriterSurfaceImage");
329 LOG_ALWAYS_FATAL_IF(imageClazz == NULL,
330 "can't find android/media/ImageWriter$WriterSurfaceImage");
331 gSurfaceImageClassInfo.mNativeBuffer = env->GetFieldID(
332 imageClazz, IMAGE_BUFFER_JNI_ID, "J");
333 LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mNativeBuffer == NULL,
334 "can't find android/media/ImageWriter$WriterSurfaceImage.%s", IMAGE_BUFFER_JNI_ID);
335
336 gSurfaceImageClassInfo.mNativeFenceFd = env->GetFieldID(
337 imageClazz, "mNativeFenceFd", "I");
338 LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mNativeFenceFd == NULL,
339 "can't find android/media/ImageWriter$WriterSurfaceImage.mNativeFenceFd");
340
341 gSurfaceImageClassInfo.mPlanes = env->GetFieldID(
342 imageClazz, "mPlanes", "[Landroid/media/ImageWriter$WriterSurfaceImage$SurfacePlane;");
343 LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mPlanes == NULL,
344 "can't find android/media/ImageWriter$WriterSurfaceImage.mPlanes");
345
346 gImageWriterClassInfo.postEventFromNative = env->GetStaticMethodID(
347 clazz, "postEventFromNative", "(Ljava/lang/Object;)V");
348 LOG_ALWAYS_FATAL_IF(gImageWriterClassInfo.postEventFromNative == NULL,
349 "can't find android/media/ImageWriter.postEventFromNative");
350
351 gImageWriterClassInfo.mWriterFormat = env->GetFieldID(
352 clazz, "mWriterFormat", "I");
353 LOG_ALWAYS_FATAL_IF(gImageWriterClassInfo.mWriterFormat == NULL,
354 "can't find android/media/ImageWriter.mWriterFormat");
355
356 jclass planeClazz = env->FindClass("android/media/ImageWriter$WriterSurfaceImage$SurfacePlane");
357 LOG_ALWAYS_FATAL_IF(planeClazz == NULL, "Can not find SurfacePlane class");
358 // FindClass only gives a local reference of jclass object.
359 gSurfacePlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz);
360 gSurfacePlaneClassInfo.ctor = env->GetMethodID(gSurfacePlaneClassInfo.clazz, "<init>",
361 "(Landroid/media/ImageWriter$WriterSurfaceImage;IILjava/nio/ByteBuffer;)V");
362 LOG_ALWAYS_FATAL_IF(gSurfacePlaneClassInfo.ctor == NULL,
363 "Can not find SurfacePlane constructor");
364}
365
366static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface,
Zhijun He916d8ac2017-03-22 17:33:47 -0700367 jint maxImages, jint userFormat) {
Zhijun Hef6a09e52015-02-24 18:12:23 -0800368 status_t res;
369
370 ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages);
371
372 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
373 if (surface == NULL) {
374 jniThrowException(env,
375 "java/lang/IllegalArgumentException",
376 "The surface has been released");
377 return 0;
378 }
379 sp<IGraphicBufferProducer> bufferProducer = surface->getIGraphicBufferProducer();
380
381 jclass clazz = env->GetObjectClass(thiz);
382 if (clazz == NULL) {
383 jniThrowRuntimeException(env, "Can't find android/graphics/ImageWriter");
384 return 0;
385 }
386 sp<JNIImageWriterContext> ctx(new JNIImageWriterContext(env, weakThiz, clazz));
387
388 sp<Surface> producer = new Surface(bufferProducer, /*controlledByApp*/false);
389 ctx->setProducer(producer);
390 /**
391 * NATIVE_WINDOW_API_CPU isn't a good choice here, as it makes the bufferQueue not connectable
392 * after disconnect. MEDIA or CAMERA are treated the same internally. The producer listener
393 * will be cleared after disconnect call.
394 */
Emilian Peev2adf4032018-06-05 17:52:16 +0100395 res = producer->connect(/*api*/NATIVE_WINDOW_API_CAMERA, /*listener*/ctx);
396 if (res != OK) {
397 ALOGE("%s: Connecting to surface producer interface failed: %s (%d)",
398 __FUNCTION__, strerror(-res), res);
399 jniThrowRuntimeException(env, "Failed to connect to native window");
400 return 0;
401 }
402
Zhijun Hef6a09e52015-02-24 18:12:23 -0800403 jlong nativeCtx = reinterpret_cast<jlong>(ctx.get());
404
405 // Get the dimension and format of the producer.
406 sp<ANativeWindow> anw = producer;
Zhijun He916d8ac2017-03-22 17:33:47 -0700407 int32_t width, height, surfaceFormat;
Zhijun Hef6a09e52015-02-24 18:12:23 -0800408 if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) {
409 ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res);
410 jniThrowRuntimeException(env, "Failed to query Surface width");
411 return 0;
412 }
413 ctx->setBufferWidth(width);
414
415 if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) {
416 ALOGE("%s: Query Surface height failed: %s (%d)", __FUNCTION__, strerror(-res), res);
417 jniThrowRuntimeException(env, "Failed to query Surface height");
418 return 0;
419 }
420 ctx->setBufferHeight(height);
421
Zhijun He916d8ac2017-03-22 17:33:47 -0700422 // Query surface format if no valid user format is specified, otherwise, override surface format
423 // with user format.
424 if (userFormat == IMAGE_FORMAT_UNKNOWN) {
425 if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &surfaceFormat)) != OK) {
426 ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res);
427 jniThrowRuntimeException(env, "Failed to query Surface format");
428 return 0;
429 }
430 } else {
Yin-Chia Yeh6dc192e2019-07-30 15:29:16 -0700431 // Set consumer buffer format to user specified format
432 PublicFormat publicFormat = static_cast<PublicFormat>(userFormat);
433 int nativeFormat = mapPublicFormatToHalFormat(publicFormat);
434 android_dataspace nativeDataspace = mapPublicFormatToHalDataspace(publicFormat);
435 res = native_window_set_buffers_format(anw.get(), nativeFormat);
436 if (res != OK) {
437 ALOGE("%s: Unable to configure consumer native buffer format to %#x",
438 __FUNCTION__, nativeFormat);
439 jniThrowRuntimeException(env, "Failed to set Surface format");
440 return 0;
441 }
442
443 res = native_window_set_buffers_data_space(anw.get(), nativeDataspace);
444 if (res != OK) {
445 ALOGE("%s: Unable to configure consumer dataspace %#x",
446 __FUNCTION__, nativeDataspace);
447 jniThrowRuntimeException(env, "Failed to set Surface dataspace");
448 return 0;
449 }
Zhijun He916d8ac2017-03-22 17:33:47 -0700450 surfaceFormat = userFormat;
Zhijun Hef6a09e52015-02-24 18:12:23 -0800451 }
Yin-Chia Yeh6dc192e2019-07-30 15:29:16 -0700452
Zhijun He916d8ac2017-03-22 17:33:47 -0700453 ctx->setBufferFormat(surfaceFormat);
454 env->SetIntField(thiz,
455 gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(surfaceFormat));
Zhijun Hef6a09e52015-02-24 18:12:23 -0800456
Zhijun He916d8ac2017-03-22 17:33:47 -0700457 if (!isFormatOpaque(surfaceFormat)) {
Zhijun Hef6a09e52015-02-24 18:12:23 -0800458 res = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
459 if (res != OK) {
460 ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
Dan Albert1102e212015-06-09 17:35:38 -0700461 __FUNCTION__, static_cast<unsigned int>(GRALLOC_USAGE_SW_WRITE_OFTEN),
Zhijun He916d8ac2017-03-22 17:33:47 -0700462 surfaceFormat, strerror(-res), res);
Zhijun Hef6a09e52015-02-24 18:12:23 -0800463 jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage");
464 return 0;
465 }
466 }
467
468 int minUndequeuedBufferCount = 0;
469 res = anw->query(anw.get(),
470 NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufferCount);
471 if (res != OK) {
472 ALOGE("%s: Query producer undequeued buffer count failed: %s (%d)",
473 __FUNCTION__, strerror(-res), res);
474 jniThrowRuntimeException(env, "Query producer undequeued buffer count failed");
475 return 0;
476 }
477
478 size_t totalBufferCount = maxImages + minUndequeuedBufferCount;
479 res = native_window_set_buffer_count(anw.get(), totalBufferCount);
480 if (res != OK) {
481 ALOGE("%s: Set buffer count failed: %s (%d)", __FUNCTION__, strerror(-res), res);
482 jniThrowRuntimeException(env, "Set buffer count failed");
483 return 0;
484 }
485
486 if (ctx != 0) {
487 ctx->incStrong((void*)ImageWriter_init);
488 }
489 return nativeCtx;
490}
491
492static void ImageWriter_dequeueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image) {
493 ALOGV("%s", __FUNCTION__);
494 JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
495 if (ctx == NULL || thiz == NULL) {
496 jniThrowException(env, "java/lang/IllegalStateException",
497 "ImageWriterContext is not initialized");
498 return;
499 }
500
501 sp<ANativeWindow> anw = ctx->getProducer();
502 android_native_buffer_t *anb = NULL;
503 int fenceFd = -1;
504 status_t res = anw->dequeueBuffer(anw.get(), &anb, &fenceFd);
505 if (res != OK) {
Zhijun Hea5827142015-04-22 10:07:42 -0700506 ALOGE("%s: Dequeue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
Chien-Yu Chene0ee6302015-07-06 17:46:05 -0700507 switch (res) {
508 case NO_INIT:
509 jniThrowException(env, "java/lang/IllegalStateException",
510 "Surface has been abandoned");
511 break;
512 default:
513 // TODO: handle other error cases here.
514 jniThrowRuntimeException(env, "dequeue buffer failed");
515 }
Zhijun Hef6a09e52015-02-24 18:12:23 -0800516 return;
517 }
518 // New GraphicBuffer object doesn't own the handle, thus the native buffer
519 // won't be freed when this object is destroyed.
Mathias Agopian845eef05f2017-04-03 17:51:15 -0700520 sp<GraphicBuffer> buffer(GraphicBuffer::from(anb));
Zhijun Hef6a09e52015-02-24 18:12:23 -0800521
522 // Note that:
523 // 1. No need to lock buffer now, will only lock it when the first getPlanes() is called.
524 // 2. Fence will be saved to mNativeFenceFd, and will consumed by lock/queue/cancel buffer
525 // later.
526 // 3. need use lockAsync here, as it will handle the dequeued fence for us automatically.
527
528 // Finally, set the native info into image object.
529 Image_setNativeContext(env, image, buffer, fenceFd);
530}
531
532static void ImageWriter_close(JNIEnv* env, jobject thiz, jlong nativeCtx) {
533 ALOGV("%s:", __FUNCTION__);
534 JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
535 if (ctx == NULL || thiz == NULL) {
Chien-Yu Chen0375cb02015-06-18 11:55:18 -0700536 // ImageWriter is already closed.
Zhijun Hef6a09e52015-02-24 18:12:23 -0800537 return;
538 }
539
540 ANativeWindow* producer = ctx->getProducer();
541 if (producer != NULL) {
542 /**
543 * NATIVE_WINDOW_API_CPU isn't a good choice here, as it makes the bufferQueue not
544 * connectable after disconnect. MEDIA or CAMERA are treated the same internally.
545 * The producer listener will be cleared after disconnect call.
546 */
547 status_t res = native_window_api_disconnect(producer, /*api*/NATIVE_WINDOW_API_CAMERA);
548 /**
549 * This is not an error. if client calling process dies, the window will
550 * also die and all calls to it will return DEAD_OBJECT, thus it's already
551 * "disconnected"
552 */
553 if (res == DEAD_OBJECT) {
554 ALOGW("%s: While disconnecting ImageWriter from native window, the"
555 " native window died already", __FUNCTION__);
556 } else if (res != OK) {
557 ALOGE("%s: native window disconnect failed: %s (%d)",
558 __FUNCTION__, strerror(-res), res);
559 jniThrowRuntimeException(env, "Native window disconnect failed");
560 return;
561 }
562 }
563
564 ctx->decStrong((void*)ImageWriter_init);
565}
566
567static void ImageWriter_cancelImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image) {
568 ALOGV("%s", __FUNCTION__);
569 JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
570 if (ctx == NULL || thiz == NULL) {
Zhijun Hedc6bb242015-12-03 15:34:34 -0800571 ALOGW("ImageWriter#close called before Image#close, consider calling Image#close first");
Zhijun Hef6a09e52015-02-24 18:12:23 -0800572 return;
573 }
574
575 sp<ANativeWindow> anw = ctx->getProducer();
576
577 GraphicBuffer *buffer = NULL;
578 int fenceFd = -1;
579 Image_getNativeContext(env, image, &buffer, &fenceFd);
580 if (buffer == NULL) {
Zhijun Hedc6bb242015-12-03 15:34:34 -0800581 // Cancel an already cancelled image is harmless.
Zhijun Hef6a09e52015-02-24 18:12:23 -0800582 return;
583 }
584
585 // Unlock the image if it was locked
586 Image_unlockIfLocked(env, image);
587
588 anw->cancelBuffer(anw.get(), buffer, fenceFd);
589
590 Image_setNativeContext(env, image, NULL, -1);
591}
592
593static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image,
Emilian Peev750aec62018-04-06 12:55:00 +0100594 jlong timestampNs, jint left, jint top, jint right, jint bottom, jint transform,
595 jint scalingMode) {
Zhijun Hef6a09e52015-02-24 18:12:23 -0800596 ALOGV("%s", __FUNCTION__);
597 JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
598 if (ctx == NULL || thiz == NULL) {
599 jniThrowException(env, "java/lang/IllegalStateException",
600 "ImageWriterContext is not initialized");
601 return;
602 }
603
604 status_t res = OK;
605 sp<ANativeWindow> anw = ctx->getProducer();
606
607 GraphicBuffer *buffer = NULL;
608 int fenceFd = -1;
609 Image_getNativeContext(env, image, &buffer, &fenceFd);
610 if (buffer == NULL) {
611 jniThrowException(env, "java/lang/IllegalStateException",
612 "Image is not initialized");
613 return;
614 }
615
616 // Unlock image if it was locked.
617 Image_unlockIfLocked(env, image);
618
619 // Set timestamp
Zhijun Hed1cbc682015-03-23 10:10:04 -0700620 ALOGV("timestamp to be queued: %" PRId64, timestampNs);
Zhijun Hef6a09e52015-02-24 18:12:23 -0800621 res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
622 if (res != OK) {
623 jniThrowRuntimeException(env, "Set timestamp failed");
624 return;
625 }
626
627 // Set crop
628 android_native_rect_t cropRect;
629 cropRect.left = left;
630 cropRect.top = top;
631 cropRect.right = right;
632 cropRect.bottom = bottom;
633 res = native_window_set_crop(anw.get(), &cropRect);
634 if (res != OK) {
635 jniThrowRuntimeException(env, "Set crop rect failed");
636 return;
637 }
638
Emilian Peev450a5ff2018-03-19 16:05:10 +0000639 res = native_window_set_buffers_transform(anw.get(), transform);
640 if (res != OK) {
641 jniThrowRuntimeException(env, "Set transform failed");
642 return;
643 }
644
Emilian Peev750aec62018-04-06 12:55:00 +0100645 res = native_window_set_scaling_mode(anw.get(), scalingMode);
646 if (res != OK) {
647 jniThrowRuntimeException(env, "Set scaling mode failed");
648 return;
649 }
650
Shuzhen Wangb9adb572020-06-24 09:33:27 -0700651 // Finally, queue input buffer.
652 //
653 // Because onBufferReleased may be called before queueBuffer() returns,
654 // queue the "attached" flag before calling queueBuffer. In case
655 // queueBuffer() fails, remove it from the queue.
656 ctx->queueAttachedFlag(false);
Zhijun Hef6a09e52015-02-24 18:12:23 -0800657 res = anw->queueBuffer(anw.get(), buffer, fenceFd);
658 if (res != OK) {
Chien-Yu Chene0ee6302015-07-06 17:46:05 -0700659 ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
Shuzhen Wangb9adb572020-06-24 09:33:27 -0700660 ctx->dequeueAttachedFlag();
Chien-Yu Chene0ee6302015-07-06 17:46:05 -0700661 switch (res) {
662 case NO_INIT:
663 jniThrowException(env, "java/lang/IllegalStateException",
664 "Surface has been abandoned");
665 break;
666 default:
667 // TODO: handle other error cases here.
668 jniThrowRuntimeException(env, "Queue input buffer failed");
669 }
Zhijun Hef6a09e52015-02-24 18:12:23 -0800670 return;
671 }
672
Zhijun Hece9d6f92015-03-29 16:33:59 -0700673 // Clear the image native context: end of this image's lifecycle in public API.
Zhijun Hef6a09e52015-02-24 18:12:23 -0800674 Image_setNativeContext(env, image, NULL, -1);
675}
676
Emilian Peev9c778e12020-10-29 17:36:22 -0700677static status_t attachAndQeueuGraphicBuffer(JNIEnv* env, JNIImageWriterContext *ctx,
678 sp<Surface> surface, sp<GraphicBuffer> gb, jlong timestampNs, jint left, jint top,
Emilian Peev750aec62018-04-06 12:55:00 +0100679 jint right, jint bottom, jint transform, jint scalingMode) {
Zhijun Hece9d6f92015-03-29 16:33:59 -0700680 status_t res = OK;
Zhijun Hece9d6f92015-03-29 16:33:59 -0700681 // Step 1. Attach Image
Emilian Peev9c778e12020-10-29 17:36:22 -0700682 res = surface->attachBuffer(gb.get());
Zhijun Hece9d6f92015-03-29 16:33:59 -0700683 if (res != OK) {
Zhijun Hece9d6f92015-03-29 16:33:59 -0700684 ALOGE("Attach image failed: %s (%d)", strerror(-res), res);
Chien-Yu Chene0ee6302015-07-06 17:46:05 -0700685 switch (res) {
686 case NO_INIT:
687 jniThrowException(env, "java/lang/IllegalStateException",
688 "Surface has been abandoned");
689 break;
690 default:
691 // TODO: handle other error cases here.
692 jniThrowRuntimeException(env, "nativeAttachImage failed!!!");
693 }
Zhijun Hece9d6f92015-03-29 16:33:59 -0700694 return res;
695 }
696 sp < ANativeWindow > anw = surface;
697
Emilian Peev750aec62018-04-06 12:55:00 +0100698 // Step 2. Set timestamp, crop, transform and scaling mode. Note that we do not need unlock the
699 // image because it was not locked.
Zhijun Hece9d6f92015-03-29 16:33:59 -0700700 ALOGV("timestamp to be queued: %" PRId64, timestampNs);
701 res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
702 if (res != OK) {
703 jniThrowRuntimeException(env, "Set timestamp failed");
704 return res;
705 }
706
707 android_native_rect_t cropRect;
708 cropRect.left = left;
709 cropRect.top = top;
710 cropRect.right = right;
711 cropRect.bottom = bottom;
712 res = native_window_set_crop(anw.get(), &cropRect);
713 if (res != OK) {
714 jniThrowRuntimeException(env, "Set crop rect failed");
715 return res;
716 }
717
Emilian Peev450a5ff2018-03-19 16:05:10 +0000718 res = native_window_set_buffers_transform(anw.get(), transform);
719 if (res != OK) {
720 jniThrowRuntimeException(env, "Set transform failed");
721 return res;
722 }
723
Emilian Peev750aec62018-04-06 12:55:00 +0100724 res = native_window_set_scaling_mode(anw.get(), scalingMode);
725 if (res != OK) {
726 jniThrowRuntimeException(env, "Set scaling mode failed");
727 return res;
728 }
729
Zhijun Hece9d6f92015-03-29 16:33:59 -0700730 // Step 3. Queue Image.
Shuzhen Wangb9adb572020-06-24 09:33:27 -0700731 //
732 // Because onBufferReleased may be called before queueBuffer() returns,
733 // queue the "attached" flag before calling queueBuffer. In case
734 // queueBuffer() fails, remove it from the queue.
735 ctx->queueAttachedFlag(true);
Emilian Peev9c778e12020-10-29 17:36:22 -0700736 res = anw->queueBuffer(anw.get(), gb.get(), /*fenceFd*/
Zhijun Hece9d6f92015-03-29 16:33:59 -0700737 -1);
738 if (res != OK) {
Chien-Yu Chene0ee6302015-07-06 17:46:05 -0700739 ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
Shuzhen Wangb9adb572020-06-24 09:33:27 -0700740 ctx->dequeueAttachedFlag();
Chien-Yu Chene0ee6302015-07-06 17:46:05 -0700741 switch (res) {
742 case NO_INIT:
743 jniThrowException(env, "java/lang/IllegalStateException",
744 "Surface has been abandoned");
745 break;
746 default:
747 // TODO: handle other error cases here.
748 jniThrowRuntimeException(env, "Queue input buffer failed");
749 }
Zhijun Hece9d6f92015-03-29 16:33:59 -0700750 return res;
751 }
752
753 // Do not set the image native context. Since it would overwrite the existing native context
754 // of the image that is from ImageReader, the subsequent image close will run into issues.
755
756 return res;
Zhijun Hef6a09e52015-02-24 18:12:23 -0800757}
758
Emilian Peev9c778e12020-10-29 17:36:22 -0700759static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nativeCtx,
760 jlong nativeBuffer, jint imageFormat, jlong timestampNs, jint left, jint top,
761 jint right, jint bottom, jint transform, jint scalingMode) {
762 ALOGV("%s", __FUNCTION__);
763 JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
764 if (ctx == NULL || thiz == NULL) {
765 jniThrowException(env, "java/lang/IllegalStateException",
766 "ImageWriterContext is not initialized");
767 return -1;
768 }
769
770 sp<Surface> surface = ctx->getProducer();
771 if (isFormatOpaque(imageFormat) != isFormatOpaque(ctx->getBufferFormat())) {
772 jniThrowException(env, "java/lang/IllegalStateException",
773 "Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa");
774 return -1;
775 }
776
777 // Image is guaranteed to be from ImageReader at this point, so it is safe to
778 // cast to BufferItem pointer.
779 BufferItem* buffer = reinterpret_cast<BufferItem*>(nativeBuffer);
780 if (buffer == NULL) {
781 jniThrowException(env, "java/lang/IllegalStateException",
782 "Image is not initialized or already closed");
783 return -1;
784 }
785
786 return attachAndQeueuGraphicBuffer(env, ctx, surface, buffer->mGraphicBuffer, timestampNs, left,
787 top, right, bottom, transform, scalingMode);
788}
789
790static jint ImageWriter_attachAndQueueGraphicBuffer(JNIEnv* env, jobject thiz, jlong nativeCtx,
791 jobject buffer, jint format, jlong timestampNs, jint left, jint top,
792 jint right, jint bottom, jint transform, jint scalingMode) {
793 ALOGV("%s", __FUNCTION__);
794 JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
795 if (ctx == NULL || thiz == NULL) {
796 jniThrowException(env, "java/lang/IllegalStateException",
797 "ImageWriterContext is not initialized");
798 return -1;
799 }
800
801 sp<Surface> surface = ctx->getProducer();
802 if (isFormatOpaque(format) != isFormatOpaque(ctx->getBufferFormat())) {
803 jniThrowException(env, "java/lang/IllegalStateException",
804 "Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa");
805 return -1;
806 }
807
808 sp<GraphicBuffer> graphicBuffer = android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env,
809 buffer);
810 if (graphicBuffer.get() == NULL) {
811 jniThrowException(env, "java/lang/IllegalArgumentException",
812 "Trying to attach an invalid graphic buffer");
813 return -1;
814 }
815
816 return attachAndQeueuGraphicBuffer(env, ctx, surface, graphicBuffer, timestampNs, left,
817 top, right, bottom, transform, scalingMode);
818}
819
Zhijun Hef6a09e52015-02-24 18:12:23 -0800820// --------------------------Image methods---------------------------------------
821
822static void Image_getNativeContext(JNIEnv* env, jobject thiz,
823 GraphicBuffer** buffer, int* fenceFd) {
824 ALOGV("%s", __FUNCTION__);
825 if (buffer != NULL) {
826 GraphicBuffer *gb = reinterpret_cast<GraphicBuffer *>
827 (env->GetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer));
828 *buffer = gb;
829 }
830
831 if (fenceFd != NULL) {
832 *fenceFd = reinterpret_cast<jint>(env->GetIntField(
833 thiz, gSurfaceImageClassInfo.mNativeFenceFd));
834 }
835}
836
837static void Image_setNativeContext(JNIEnv* env, jobject thiz,
838 sp<GraphicBuffer> buffer, int fenceFd) {
839 ALOGV("%s:", __FUNCTION__);
840 GraphicBuffer* p = NULL;
841 Image_getNativeContext(env, thiz, &p, /*fenceFd*/NULL);
842 if (buffer != 0) {
843 buffer->incStrong((void*)Image_setNativeContext);
844 }
845 if (p) {
846 p->decStrong((void*)Image_setNativeContext);
847 }
848 env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer,
849 reinterpret_cast<jlong>(buffer.get()));
850
851 env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
852}
853
854static void Image_unlockIfLocked(JNIEnv* env, jobject thiz) {
855 ALOGV("%s", __FUNCTION__);
856 GraphicBuffer* buffer;
857 Image_getNativeContext(env, thiz, &buffer, NULL);
858 if (buffer == NULL) {
859 jniThrowException(env, "java/lang/IllegalStateException",
860 "Image is not initialized");
861 return;
862 }
863
864 // Is locked?
865 bool isLocked = false;
Zhijun Hece9d6f92015-03-29 16:33:59 -0700866 jobject planes = NULL;
867 if (!isFormatOpaque(buffer->getPixelFormat())) {
868 planes = env->GetObjectField(thiz, gSurfaceImageClassInfo.mPlanes);
869 }
Zhijun Hef6a09e52015-02-24 18:12:23 -0800870 isLocked = (planes != NULL);
871 if (isLocked) {
Zhijun Hece9d6f92015-03-29 16:33:59 -0700872 // no need to use fence here, as we it will be consumed by either cancel or queue buffer.
Zhijun Hef6a09e52015-02-24 18:12:23 -0800873 status_t res = buffer->unlock();
874 if (res != OK) {
875 jniThrowRuntimeException(env, "unlock buffer failed");
John Reckdcd4c582019-07-12 13:13:05 -0700876 return;
Zhijun Hef6a09e52015-02-24 18:12:23 -0800877 }
878 ALOGV("Successfully unlocked the image");
879 }
880}
881
882static jint Image_getWidth(JNIEnv* env, jobject thiz) {
883 ALOGV("%s", __FUNCTION__);
884 GraphicBuffer* buffer;
885 Image_getNativeContext(env, thiz, &buffer, NULL);
886 if (buffer == NULL) {
887 jniThrowException(env, "java/lang/IllegalStateException",
888 "Image is not initialized");
889 return -1;
890 }
891
892 return buffer->getWidth();
893}
894
895static jint Image_getHeight(JNIEnv* env, jobject thiz) {
896 ALOGV("%s", __FUNCTION__);
897 GraphicBuffer* buffer;
898 Image_getNativeContext(env, thiz, &buffer, NULL);
899 if (buffer == NULL) {
900 jniThrowException(env, "java/lang/IllegalStateException",
901 "Image is not initialized");
902 return -1;
903 }
904
905 return buffer->getHeight();
906}
907
Zhijun Hef6a09e52015-02-24 18:12:23 -0800908static jint Image_getFormat(JNIEnv* env, jobject thiz) {
909 ALOGV("%s", __FUNCTION__);
910 GraphicBuffer* buffer;
911 Image_getNativeContext(env, thiz, &buffer, NULL);
912 if (buffer == NULL) {
913 jniThrowException(env, "java/lang/IllegalStateException",
914 "Image is not initialized");
915 return 0;
916 }
917
Zhijun He0ab41622016-02-25 16:00:38 -0800918 // ImageWriter doesn't support data space yet, assuming it is unknown.
Fedor Kudasov4d027e92019-06-24 17:21:57 +0100919 PublicFormat publicFmt = mapHalFormatDataspaceToPublicFormat(buffer->getPixelFormat(),
920 HAL_DATASPACE_UNKNOWN);
Zhijun He0ab41622016-02-25 16:00:38 -0800921 return static_cast<jint>(publicFmt);
Zhijun Hef6a09e52015-02-24 18:12:23 -0800922}
923
rennbe092192018-05-07 10:18:05 -0700924static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
925 GraphicBuffer* buffer;
926 Image_getNativeContext(env, thiz, &buffer, NULL);
927 if (buffer == NULL) {
928 jniThrowException(env, "java/lang/IllegalStateException",
929 "Image is not initialized");
930 return NULL;
931 }
932 AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer);
933 // don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us
934 // to link against libandroid.so
935 return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env, b);
936}
937
Zhijun Hef6a09e52015-02-24 18:12:23 -0800938static void Image_setFenceFd(JNIEnv* env, jobject thiz, int fenceFd) {
939 ALOGV("%s:", __FUNCTION__);
940 env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
941}
942
943static void Image_getLockedImage(JNIEnv* env, jobject thiz, LockedImage *image) {
944 ALOGV("%s", __FUNCTION__);
945 GraphicBuffer* buffer;
946 int fenceFd = -1;
947 Image_getNativeContext(env, thiz, &buffer, &fenceFd);
948 if (buffer == NULL) {
949 jniThrowException(env, "java/lang/IllegalStateException",
950 "Image is not initialized");
951 return;
952 }
953
Zhijun He0ab41622016-02-25 16:00:38 -0800954 // ImageWriter doesn't use crop by itself, app sets it, use the no crop version.
955 const Rect noCrop(buffer->width, buffer->height);
956 status_t res = lockImageFromBuffer(
957 buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, noCrop, fenceFd, image);
958 // Clear the fenceFd as it is already consumed by lock call.
959 Image_setFenceFd(env, thiz, /*fenceFd*/-1);
960 if (res != OK) {
961 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
962 "lock buffer failed for format 0x%x",
963 buffer->getPixelFormat());
964 return;
Zhijun Hef6a09e52015-02-24 18:12:23 -0800965 }
966
Zhijun He0ab41622016-02-25 16:00:38 -0800967 ALOGV("%s: Successfully locked the image", __FUNCTION__);
Zhijun Hef6a09e52015-02-24 18:12:23 -0800968 // crop, transform, scalingMode, timestamp, and frameNumber should be set by producer,
969 // and we don't set them here.
970}
971
John Reckdcd4c582019-07-12 13:13:05 -0700972static bool Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx,
Zhijun Hef6a09e52015-02-24 18:12:23 -0800973 int32_t writerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) {
974 ALOGV("%s", __FUNCTION__);
Zhijun Hef6a09e52015-02-24 18:12:23 -0800975
Zhijun He0ab41622016-02-25 16:00:38 -0800976 status_t res = getLockedImageInfo(buffer, idx, writerFormat, base, size,
977 pixelStride, rowStride);
978 if (res != OK) {
979 jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
John Reck03bd07a2019-07-12 13:53:04 -0700980 "Pixel format: 0x%x is unsupported", writerFormat);
John Reckdcd4c582019-07-12 13:13:05 -0700981 return false;
Zhijun Hef6a09e52015-02-24 18:12:23 -0800982 }
John Reckdcd4c582019-07-12 13:13:05 -0700983 return true;
Zhijun Hef6a09e52015-02-24 18:12:23 -0800984}
985
986static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz,
987 int numPlanes, int writerFormat) {
988 ALOGV("%s: create SurfacePlane array with size %d", __FUNCTION__, numPlanes);
989 int rowStride, pixelStride;
990 uint8_t *pData;
991 uint32_t dataSize;
992 jobject byteBuffer;
993
994 int format = Image_getFormat(env, thiz);
Zhijun Hece9d6f92015-03-29 16:33:59 -0700995 if (isFormatOpaque(format) && numPlanes > 0) {
Zhijun Hef6a09e52015-02-24 18:12:23 -0800996 String8 msg;
997 msg.appendFormat("Format 0x%x is opaque, thus not writable, the number of planes (%d)"
998 " must be 0", format, numPlanes);
999 jniThrowException(env, "java/lang/IllegalArgumentException", msg.string());
1000 return NULL;
1001 }
1002
1003 jobjectArray surfacePlanes = env->NewObjectArray(numPlanes, gSurfacePlaneClassInfo.clazz,
1004 /*initial_element*/NULL);
1005 if (surfacePlanes == NULL) {
1006 jniThrowRuntimeException(env, "Failed to create SurfacePlane arrays,"
1007 " probably out of memory");
1008 return NULL;
1009 }
Zhijun Hece9d6f92015-03-29 16:33:59 -07001010 if (isFormatOpaque(format)) {
1011 return surfacePlanes;
1012 }
Zhijun Hef6a09e52015-02-24 18:12:23 -08001013
1014 // Buildup buffer info: rowStride, pixelStride and byteBuffers.
1015 LockedImage lockedImg = LockedImage();
1016 Image_getLockedImage(env, thiz, &lockedImg);
1017
1018 // Create all SurfacePlanes
Zhijun He0ab41622016-02-25 16:00:38 -08001019 PublicFormat publicWriterFormat = static_cast<PublicFormat>(writerFormat);
Fedor Kudasov4d027e92019-06-24 17:21:57 +01001020 writerFormat = mapPublicFormatToHalFormat(publicWriterFormat);
Zhijun Hef6a09e52015-02-24 18:12:23 -08001021 for (int i = 0; i < numPlanes; i++) {
John Reckdcd4c582019-07-12 13:13:05 -07001022 if (!Image_getLockedImageInfo(env, &lockedImg, i, writerFormat,
1023 &pData, &dataSize, &pixelStride, &rowStride)) {
1024 return NULL;
1025 }
Zhijun Hef6a09e52015-02-24 18:12:23 -08001026 byteBuffer = env->NewDirectByteBuffer(pData, dataSize);
1027 if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) {
1028 jniThrowException(env, "java/lang/IllegalStateException",
1029 "Failed to allocate ByteBuffer");
1030 return NULL;
1031 }
1032
1033 // Finally, create this SurfacePlane.
1034 jobject surfacePlane = env->NewObject(gSurfacePlaneClassInfo.clazz,
1035 gSurfacePlaneClassInfo.ctor, thiz, rowStride, pixelStride, byteBuffer);
1036 env->SetObjectArrayElement(surfacePlanes, i, surfacePlane);
1037 }
1038
1039 return surfacePlanes;
1040}
1041
Zhijun Hef6a09e52015-02-24 18:12:23 -08001042} // extern "C"
1043
1044// ----------------------------------------------------------------------------
1045
1046static JNINativeMethod gImageWriterMethods[] = {
1047 {"nativeClassInit", "()V", (void*)ImageWriter_classInit },
Zhijun He916d8ac2017-03-22 17:33:47 -07001048 {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;II)J",
Zhijun Hef6a09e52015-02-24 18:12:23 -08001049 (void*)ImageWriter_init },
Zhijun Hece9d6f92015-03-29 16:33:59 -07001050 {"nativeClose", "(J)V", (void*)ImageWriter_close },
Emilian Peev750aec62018-04-06 12:55:00 +01001051 {"nativeAttachAndQueueImage", "(JJIJIIIIII)I", (void*)ImageWriter_attachAndQueueImage },
Emilian Peev9c778e12020-10-29 17:36:22 -07001052 {"nativeAttachAndQueueGraphicBuffer",
1053 "(JLandroid/graphics/GraphicBuffer;IJIIIIII)I",
1054 (void*)ImageWriter_attachAndQueueGraphicBuffer },
Zhijun Hef6a09e52015-02-24 18:12:23 -08001055 {"nativeDequeueInputImage", "(JLandroid/media/Image;)V", (void*)ImageWriter_dequeueImage },
Emilian Peev750aec62018-04-06 12:55:00 +01001056 {"nativeQueueInputImage", "(JLandroid/media/Image;JIIIIII)V", (void*)ImageWriter_queueImage },
Zhijun Hef6a09e52015-02-24 18:12:23 -08001057 {"cancelImage", "(JLandroid/media/Image;)V", (void*)ImageWriter_cancelImage },
1058};
1059
1060static JNINativeMethod gImageMethods[] = {
1061 {"nativeCreatePlanes", "(II)[Landroid/media/ImageWriter$WriterSurfaceImage$SurfacePlane;",
rennbe092192018-05-07 10:18:05 -07001062 (void*)Image_createSurfacePlanes },
1063 {"nativeGetWidth", "()I", (void*)Image_getWidth },
1064 {"nativeGetHeight", "()I", (void*)Image_getHeight },
1065 {"nativeGetFormat", "()I", (void*)Image_getFormat },
1066 {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
1067 (void*)Image_getHardwareBuffer },
Zhijun Hef6a09e52015-02-24 18:12:23 -08001068};
1069
1070int register_android_media_ImageWriter(JNIEnv *env) {
1071
1072 int ret1 = AndroidRuntime::registerNativeMethods(env,
1073 "android/media/ImageWriter", gImageWriterMethods, NELEM(gImageWriterMethods));
1074
1075 int ret2 = AndroidRuntime::registerNativeMethods(env,
1076 "android/media/ImageWriter$WriterSurfaceImage", gImageMethods, NELEM(gImageMethods));
1077
1078 return (ret1 || ret2);
1079}