• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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"
19 #include "android_media_Utils.h"
20 
21 #include <utils/Log.h>
22 #include <utils/String8.h>
23 
24 #include <gui/IProducerListener.h>
25 #include <gui/Surface.h>
26 #include <android_runtime/AndroidRuntime.h>
27 #include <android_runtime/android_view_Surface.h>
28 #include <jni.h>
29 #include <nativehelper/JNIHelp.h>
30 
31 #include <stdint.h>
32 #include <inttypes.h>
33 
34 #define IMAGE_BUFFER_JNI_ID           "mNativeBuffer"
35 #define IMAGE_FORMAT_UNKNOWN          0 // This is the same value as ImageFormat#UNKNOWN.
36 // ----------------------------------------------------------------------------
37 
38 using namespace android;
39 
40 static struct {
41     jmethodID postEventFromNative;
42     jfieldID mWriterFormat;
43 } gImageWriterClassInfo;
44 
45 static struct {
46     jfieldID mNativeBuffer;
47     jfieldID mNativeFenceFd;
48     jfieldID mPlanes;
49 } gSurfaceImageClassInfo;
50 
51 static struct {
52     jclass clazz;
53     jmethodID ctor;
54 } gSurfacePlaneClassInfo;
55 
56 // ----------------------------------------------------------------------------
57 
58 class JNIImageWriterContext : public BnProducerListener {
59 public:
60     JNIImageWriterContext(JNIEnv* env, jobject weakThiz, jclass clazz);
61 
62     virtual ~JNIImageWriterContext();
63 
64     // Implementation of IProducerListener, used to notify the ImageWriter that the consumer
65     // has returned a buffer and it is ready for ImageWriter to dequeue.
66     virtual void onBufferReleased();
67 
setProducer(const sp<Surface> & producer)68     void setProducer(const sp<Surface>& producer) { mProducer = producer; }
getProducer()69     Surface* getProducer() { return mProducer.get(); }
70 
setBufferFormat(int format)71     void setBufferFormat(int format) { mFormat = format; }
getBufferFormat()72     int getBufferFormat() { return mFormat; }
73 
setBufferWidth(int width)74     void setBufferWidth(int width) { mWidth = width; }
getBufferWidth()75     int getBufferWidth() { return mWidth; }
76 
setBufferHeight(int height)77     void setBufferHeight(int height) { mHeight = height; }
getBufferHeight()78     int getBufferHeight() { return mHeight; }
79 
80 private:
81     static JNIEnv* getJNIEnv(bool* needsDetach);
82     static void detachJNI();
83 
84     sp<Surface> mProducer;
85     jobject mWeakThiz;
86     jclass mClazz;
87     int mFormat;
88     int mWidth;
89     int mHeight;
90 };
91 
JNIImageWriterContext(JNIEnv * env,jobject weakThiz,jclass clazz)92 JNIImageWriterContext::JNIImageWriterContext(JNIEnv* env, jobject weakThiz, jclass clazz) :
93     mWeakThiz(env->NewGlobalRef(weakThiz)),
94     mClazz((jclass)env->NewGlobalRef(clazz)),
95     mFormat(0),
96     mWidth(-1),
97     mHeight(-1) {
98 }
99 
~JNIImageWriterContext()100 JNIImageWriterContext::~JNIImageWriterContext() {
101     ALOGV("%s", __FUNCTION__);
102     bool needsDetach = false;
103     JNIEnv* env = getJNIEnv(&needsDetach);
104     if (env != NULL) {
105         env->DeleteGlobalRef(mWeakThiz);
106         env->DeleteGlobalRef(mClazz);
107     } else {
108         ALOGW("leaking JNI object references");
109     }
110     if (needsDetach) {
111         detachJNI();
112     }
113 
114     mProducer.clear();
115 }
116 
getJNIEnv(bool * needsDetach)117 JNIEnv* JNIImageWriterContext::getJNIEnv(bool* needsDetach) {
118     ALOGV("%s", __FUNCTION__);
119     LOG_ALWAYS_FATAL_IF(needsDetach == NULL, "needsDetach is null!!!");
120     *needsDetach = false;
121     JNIEnv* env = AndroidRuntime::getJNIEnv();
122     if (env == NULL) {
123         JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
124         JavaVM* vm = AndroidRuntime::getJavaVM();
125         int result = vm->AttachCurrentThread(&env, (void*) &args);
126         if (result != JNI_OK) {
127             ALOGE("thread attach failed: %#x", result);
128             return NULL;
129         }
130         *needsDetach = true;
131     }
132     return env;
133 }
134 
detachJNI()135 void JNIImageWriterContext::detachJNI() {
136     ALOGV("%s", __FUNCTION__);
137     JavaVM* vm = AndroidRuntime::getJavaVM();
138     int result = vm->DetachCurrentThread();
139     if (result != JNI_OK) {
140         ALOGE("thread detach failed: %#x", result);
141     }
142 }
143 
onBufferReleased()144 void JNIImageWriterContext::onBufferReleased() {
145     ALOGV("%s: buffer released", __FUNCTION__);
146     bool needsDetach = false;
147     JNIEnv* env = getJNIEnv(&needsDetach);
148     if (env != NULL) {
149         // Detach the buffer every time when a buffer consumption is done,
150         // need let this callback give a BufferItem, then only detach if it was attached to this
151         // Writer. Do the detach unconditionally for opaque format now. see b/19977520
152         if (mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
153             sp<Fence> fence;
154             sp<GraphicBuffer> buffer;
155             ALOGV("%s: One buffer is detached", __FUNCTION__);
156             mProducer->detachNextBuffer(&buffer, &fence);
157         }
158 
159         env->CallStaticVoidMethod(mClazz, gImageWriterClassInfo.postEventFromNative, mWeakThiz);
160     } else {
161         ALOGW("onBufferReleased event will not posted");
162     }
163 
164     if (needsDetach) {
165         detachJNI();
166     }
167 }
168 
169 // ----------------------------------------------------------------------------
170 
171 extern "C" {
172 
173 // -------------------------------Private method declarations--------------
174 
175 static void Image_setNativeContext(JNIEnv* env, jobject thiz,
176         sp<GraphicBuffer> buffer, int fenceFd);
177 static void Image_getNativeContext(JNIEnv* env, jobject thiz,
178         GraphicBuffer** buffer, int* fenceFd);
179 static void Image_unlockIfLocked(JNIEnv* env, jobject thiz);
180 
181 // --------------------------ImageWriter methods---------------------------------------
182 
ImageWriter_classInit(JNIEnv * env,jclass clazz)183 static void ImageWriter_classInit(JNIEnv* env, jclass clazz) {
184     ALOGV("%s:", __FUNCTION__);
185     jclass imageClazz = env->FindClass("android/media/ImageWriter$WriterSurfaceImage");
186     LOG_ALWAYS_FATAL_IF(imageClazz == NULL,
187             "can't find android/media/ImageWriter$WriterSurfaceImage");
188     gSurfaceImageClassInfo.mNativeBuffer = env->GetFieldID(
189             imageClazz, IMAGE_BUFFER_JNI_ID, "J");
190     LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mNativeBuffer == NULL,
191             "can't find android/media/ImageWriter$WriterSurfaceImage.%s", IMAGE_BUFFER_JNI_ID);
192 
193     gSurfaceImageClassInfo.mNativeFenceFd = env->GetFieldID(
194             imageClazz, "mNativeFenceFd", "I");
195     LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mNativeFenceFd == NULL,
196             "can't find android/media/ImageWriter$WriterSurfaceImage.mNativeFenceFd");
197 
198     gSurfaceImageClassInfo.mPlanes = env->GetFieldID(
199             imageClazz, "mPlanes", "[Landroid/media/ImageWriter$WriterSurfaceImage$SurfacePlane;");
200     LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mPlanes == NULL,
201             "can't find android/media/ImageWriter$WriterSurfaceImage.mPlanes");
202 
203     gImageWriterClassInfo.postEventFromNative = env->GetStaticMethodID(
204             clazz, "postEventFromNative", "(Ljava/lang/Object;)V");
205     LOG_ALWAYS_FATAL_IF(gImageWriterClassInfo.postEventFromNative == NULL,
206                         "can't find android/media/ImageWriter.postEventFromNative");
207 
208     gImageWriterClassInfo.mWriterFormat = env->GetFieldID(
209             clazz, "mWriterFormat", "I");
210     LOG_ALWAYS_FATAL_IF(gImageWriterClassInfo.mWriterFormat == NULL,
211                         "can't find android/media/ImageWriter.mWriterFormat");
212 
213     jclass planeClazz = env->FindClass("android/media/ImageWriter$WriterSurfaceImage$SurfacePlane");
214     LOG_ALWAYS_FATAL_IF(planeClazz == NULL, "Can not find SurfacePlane class");
215     // FindClass only gives a local reference of jclass object.
216     gSurfacePlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz);
217     gSurfacePlaneClassInfo.ctor = env->GetMethodID(gSurfacePlaneClassInfo.clazz, "<init>",
218             "(Landroid/media/ImageWriter$WriterSurfaceImage;IILjava/nio/ByteBuffer;)V");
219     LOG_ALWAYS_FATAL_IF(gSurfacePlaneClassInfo.ctor == NULL,
220             "Can not find SurfacePlane constructor");
221 }
222 
ImageWriter_init(JNIEnv * env,jobject thiz,jobject weakThiz,jobject jsurface,jint maxImages,jint userFormat)223 static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface,
224         jint maxImages, jint userFormat) {
225     status_t res;
226 
227     ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages);
228 
229     sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
230     if (surface == NULL) {
231         jniThrowException(env,
232                 "java/lang/IllegalArgumentException",
233                 "The surface has been released");
234         return 0;
235      }
236     sp<IGraphicBufferProducer> bufferProducer = surface->getIGraphicBufferProducer();
237 
238     jclass clazz = env->GetObjectClass(thiz);
239     if (clazz == NULL) {
240         jniThrowRuntimeException(env, "Can't find android/graphics/ImageWriter");
241         return 0;
242     }
243     sp<JNIImageWriterContext> ctx(new JNIImageWriterContext(env, weakThiz, clazz));
244 
245     sp<Surface> producer = new Surface(bufferProducer, /*controlledByApp*/false);
246     ctx->setProducer(producer);
247     /**
248      * NATIVE_WINDOW_API_CPU isn't a good choice here, as it makes the bufferQueue not connectable
249      * after disconnect. MEDIA or CAMERA are treated the same internally. The producer listener
250      * will be cleared after disconnect call.
251      */
252     producer->connect(/*api*/NATIVE_WINDOW_API_CAMERA, /*listener*/ctx);
253     jlong nativeCtx = reinterpret_cast<jlong>(ctx.get());
254 
255     // Get the dimension and format of the producer.
256     sp<ANativeWindow> anw = producer;
257     int32_t width, height, surfaceFormat;
258     if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) {
259         ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res);
260         jniThrowRuntimeException(env, "Failed to query Surface width");
261         return 0;
262     }
263     ctx->setBufferWidth(width);
264 
265     if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) {
266         ALOGE("%s: Query Surface height failed: %s (%d)", __FUNCTION__, strerror(-res), res);
267         jniThrowRuntimeException(env, "Failed to query Surface height");
268         return 0;
269     }
270     ctx->setBufferHeight(height);
271 
272     // Query surface format if no valid user format is specified, otherwise, override surface format
273     // with user format.
274     if (userFormat == IMAGE_FORMAT_UNKNOWN) {
275         if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &surfaceFormat)) != OK) {
276             ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res);
277             jniThrowRuntimeException(env, "Failed to query Surface format");
278             return 0;
279         }
280     } else {
281         surfaceFormat = userFormat;
282     }
283     ctx->setBufferFormat(surfaceFormat);
284     env->SetIntField(thiz,
285             gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(surfaceFormat));
286 
287     if (!isFormatOpaque(surfaceFormat)) {
288         res = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
289         if (res != OK) {
290             ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
291                   __FUNCTION__, static_cast<unsigned int>(GRALLOC_USAGE_SW_WRITE_OFTEN),
292                   surfaceFormat, strerror(-res), res);
293             jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage");
294             return 0;
295         }
296     }
297 
298     int minUndequeuedBufferCount = 0;
299     res = anw->query(anw.get(),
300                 NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufferCount);
301     if (res != OK) {
302         ALOGE("%s: Query producer undequeued buffer count failed: %s (%d)",
303                 __FUNCTION__, strerror(-res), res);
304         jniThrowRuntimeException(env, "Query producer undequeued buffer count failed");
305         return 0;
306      }
307 
308     size_t totalBufferCount = maxImages + minUndequeuedBufferCount;
309     res = native_window_set_buffer_count(anw.get(), totalBufferCount);
310     if (res != OK) {
311         ALOGE("%s: Set buffer count failed: %s (%d)", __FUNCTION__, strerror(-res), res);
312         jniThrowRuntimeException(env, "Set buffer count failed");
313         return 0;
314     }
315 
316     if (ctx != 0) {
317         ctx->incStrong((void*)ImageWriter_init);
318     }
319     return nativeCtx;
320 }
321 
ImageWriter_dequeueImage(JNIEnv * env,jobject thiz,jlong nativeCtx,jobject image)322 static void ImageWriter_dequeueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image) {
323     ALOGV("%s", __FUNCTION__);
324     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
325     if (ctx == NULL || thiz == NULL) {
326         jniThrowException(env, "java/lang/IllegalStateException",
327                 "ImageWriterContext is not initialized");
328         return;
329     }
330 
331     sp<ANativeWindow> anw = ctx->getProducer();
332     android_native_buffer_t *anb = NULL;
333     int fenceFd = -1;
334     status_t res = anw->dequeueBuffer(anw.get(), &anb, &fenceFd);
335     if (res != OK) {
336         ALOGE("%s: Dequeue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
337         switch (res) {
338             case NO_INIT:
339                 jniThrowException(env, "java/lang/IllegalStateException",
340                     "Surface has been abandoned");
341                 break;
342             default:
343                 // TODO: handle other error cases here.
344                 jniThrowRuntimeException(env, "dequeue buffer failed");
345         }
346         return;
347     }
348     // New GraphicBuffer object doesn't own the handle, thus the native buffer
349     // won't be freed when this object is destroyed.
350     sp<GraphicBuffer> buffer(GraphicBuffer::from(anb));
351 
352     // Note that:
353     // 1. No need to lock buffer now, will only lock it when the first getPlanes() is called.
354     // 2. Fence will be saved to mNativeFenceFd, and will consumed by lock/queue/cancel buffer
355     //    later.
356     // 3. need use lockAsync here, as it will handle the dequeued fence for us automatically.
357 
358     // Finally, set the native info into image object.
359     Image_setNativeContext(env, image, buffer, fenceFd);
360 }
361 
ImageWriter_close(JNIEnv * env,jobject thiz,jlong nativeCtx)362 static void ImageWriter_close(JNIEnv* env, jobject thiz, jlong nativeCtx) {
363     ALOGV("%s:", __FUNCTION__);
364     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
365     if (ctx == NULL || thiz == NULL) {
366         // ImageWriter is already closed.
367         return;
368     }
369 
370     ANativeWindow* producer = ctx->getProducer();
371     if (producer != NULL) {
372         /**
373          * NATIVE_WINDOW_API_CPU isn't a good choice here, as it makes the bufferQueue not
374          * connectable after disconnect. MEDIA or CAMERA are treated the same internally.
375          * The producer listener will be cleared after disconnect call.
376          */
377         status_t res = native_window_api_disconnect(producer, /*api*/NATIVE_WINDOW_API_CAMERA);
378         /**
379          * This is not an error. if client calling process dies, the window will
380          * also die and all calls to it will return DEAD_OBJECT, thus it's already
381          * "disconnected"
382          */
383         if (res == DEAD_OBJECT) {
384             ALOGW("%s: While disconnecting ImageWriter from native window, the"
385                     " native window died already", __FUNCTION__);
386         } else if (res != OK) {
387             ALOGE("%s: native window disconnect failed: %s (%d)",
388                     __FUNCTION__, strerror(-res), res);
389             jniThrowRuntimeException(env, "Native window disconnect failed");
390             return;
391         }
392     }
393 
394     ctx->decStrong((void*)ImageWriter_init);
395 }
396 
ImageWriter_cancelImage(JNIEnv * env,jobject thiz,jlong nativeCtx,jobject image)397 static void ImageWriter_cancelImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image) {
398     ALOGV("%s", __FUNCTION__);
399     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
400     if (ctx == NULL || thiz == NULL) {
401         ALOGW("ImageWriter#close called before Image#close, consider calling Image#close first");
402         return;
403     }
404 
405     sp<ANativeWindow> anw = ctx->getProducer();
406 
407     GraphicBuffer *buffer = NULL;
408     int fenceFd = -1;
409     Image_getNativeContext(env, image, &buffer, &fenceFd);
410     if (buffer == NULL) {
411         // Cancel an already cancelled image is harmless.
412         return;
413     }
414 
415     // Unlock the image if it was locked
416     Image_unlockIfLocked(env, image);
417 
418     anw->cancelBuffer(anw.get(), buffer, fenceFd);
419 
420     Image_setNativeContext(env, image, NULL, -1);
421 }
422 
ImageWriter_queueImage(JNIEnv * env,jobject thiz,jlong nativeCtx,jobject image,jlong timestampNs,jint left,jint top,jint right,jint bottom)423 static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image,
424         jlong timestampNs, jint left, jint top, jint right, jint bottom) {
425     ALOGV("%s", __FUNCTION__);
426     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
427     if (ctx == NULL || thiz == NULL) {
428         jniThrowException(env, "java/lang/IllegalStateException",
429                 "ImageWriterContext is not initialized");
430         return;
431     }
432 
433     status_t res = OK;
434     sp<ANativeWindow> anw = ctx->getProducer();
435 
436     GraphicBuffer *buffer = NULL;
437     int fenceFd = -1;
438     Image_getNativeContext(env, image, &buffer, &fenceFd);
439     if (buffer == NULL) {
440         jniThrowException(env, "java/lang/IllegalStateException",
441                 "Image is not initialized");
442         return;
443     }
444 
445     // Unlock image if it was locked.
446     Image_unlockIfLocked(env, image);
447 
448     // Set timestamp
449     ALOGV("timestamp to be queued: %" PRId64, timestampNs);
450     res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
451     if (res != OK) {
452         jniThrowRuntimeException(env, "Set timestamp failed");
453         return;
454     }
455 
456     // Set crop
457     android_native_rect_t cropRect;
458     cropRect.left = left;
459     cropRect.top = top;
460     cropRect.right = right;
461     cropRect.bottom = bottom;
462     res = native_window_set_crop(anw.get(), &cropRect);
463     if (res != OK) {
464         jniThrowRuntimeException(env, "Set crop rect failed");
465         return;
466     }
467 
468     // Finally, queue input buffer
469     res = anw->queueBuffer(anw.get(), buffer, fenceFd);
470     if (res != OK) {
471         ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
472         switch (res) {
473             case NO_INIT:
474                 jniThrowException(env, "java/lang/IllegalStateException",
475                     "Surface has been abandoned");
476                 break;
477             default:
478                 // TODO: handle other error cases here.
479                 jniThrowRuntimeException(env, "Queue input buffer failed");
480         }
481         return;
482     }
483 
484     // Clear the image native context: end of this image's lifecycle in public API.
485     Image_setNativeContext(env, image, NULL, -1);
486 }
487 
ImageWriter_attachAndQueueImage(JNIEnv * env,jobject thiz,jlong nativeCtx,jlong nativeBuffer,jint imageFormat,jlong timestampNs,jint left,jint top,jint right,jint bottom)488 static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nativeCtx,
489         jlong nativeBuffer, jint imageFormat, jlong timestampNs, jint left, jint top,
490         jint right, jint bottom) {
491     ALOGV("%s", __FUNCTION__);
492     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
493     if (ctx == NULL || thiz == NULL) {
494         jniThrowException(env, "java/lang/IllegalStateException",
495                 "ImageWriterContext is not initialized");
496         return -1;
497     }
498 
499     sp<Surface> surface = ctx->getProducer();
500     status_t res = OK;
501     if (isFormatOpaque(imageFormat) != isFormatOpaque(ctx->getBufferFormat())) {
502         jniThrowException(env, "java/lang/IllegalStateException",
503                 "Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa");
504         return -1;
505     }
506 
507     // Image is guaranteed to be from ImageReader at this point, so it is safe to
508     // cast to BufferItem pointer.
509     BufferItem* buffer = reinterpret_cast<BufferItem*>(nativeBuffer);
510     if (buffer == NULL) {
511         jniThrowException(env, "java/lang/IllegalStateException",
512                 "Image is not initialized or already closed");
513         return -1;
514     }
515 
516     // Step 1. Attach Image
517     res = surface->attachBuffer(buffer->mGraphicBuffer.get());
518     if (res != OK) {
519         ALOGE("Attach image failed: %s (%d)", strerror(-res), res);
520         switch (res) {
521             case NO_INIT:
522                 jniThrowException(env, "java/lang/IllegalStateException",
523                     "Surface has been abandoned");
524                 break;
525             default:
526                 // TODO: handle other error cases here.
527                 jniThrowRuntimeException(env, "nativeAttachImage failed!!!");
528         }
529         return res;
530     }
531     sp < ANativeWindow > anw = surface;
532 
533     // Step 2. Set timestamp and crop. Note that we do not need unlock the image because
534     // it was not locked.
535     ALOGV("timestamp to be queued: %" PRId64, timestampNs);
536     res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
537     if (res != OK) {
538         jniThrowRuntimeException(env, "Set timestamp failed");
539         return res;
540     }
541 
542     android_native_rect_t cropRect;
543     cropRect.left = left;
544     cropRect.top = top;
545     cropRect.right = right;
546     cropRect.bottom = bottom;
547     res = native_window_set_crop(anw.get(), &cropRect);
548     if (res != OK) {
549         jniThrowRuntimeException(env, "Set crop rect failed");
550         return res;
551     }
552 
553     // Step 3. Queue Image.
554     res = anw->queueBuffer(anw.get(), buffer->mGraphicBuffer.get(), /*fenceFd*/
555             -1);
556     if (res != OK) {
557         ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
558         switch (res) {
559             case NO_INIT:
560                 jniThrowException(env, "java/lang/IllegalStateException",
561                     "Surface has been abandoned");
562                 break;
563             default:
564                 // TODO: handle other error cases here.
565                 jniThrowRuntimeException(env, "Queue input buffer failed");
566         }
567         return res;
568     }
569 
570     // Do not set the image native context. Since it would overwrite the existing native context
571     // of the image that is from ImageReader, the subsequent image close will run into issues.
572 
573     return res;
574 }
575 
576 // --------------------------Image methods---------------------------------------
577 
Image_getNativeContext(JNIEnv * env,jobject thiz,GraphicBuffer ** buffer,int * fenceFd)578 static void Image_getNativeContext(JNIEnv* env, jobject thiz,
579         GraphicBuffer** buffer, int* fenceFd) {
580     ALOGV("%s", __FUNCTION__);
581     if (buffer != NULL) {
582         GraphicBuffer *gb = reinterpret_cast<GraphicBuffer *>
583                   (env->GetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer));
584         *buffer = gb;
585     }
586 
587     if (fenceFd != NULL) {
588         *fenceFd = reinterpret_cast<jint>(env->GetIntField(
589                 thiz, gSurfaceImageClassInfo.mNativeFenceFd));
590     }
591 }
592 
Image_setNativeContext(JNIEnv * env,jobject thiz,sp<GraphicBuffer> buffer,int fenceFd)593 static void Image_setNativeContext(JNIEnv* env, jobject thiz,
594         sp<GraphicBuffer> buffer, int fenceFd) {
595     ALOGV("%s:", __FUNCTION__);
596     GraphicBuffer* p = NULL;
597     Image_getNativeContext(env, thiz, &p, /*fenceFd*/NULL);
598     if (buffer != 0) {
599         buffer->incStrong((void*)Image_setNativeContext);
600     }
601     if (p) {
602         p->decStrong((void*)Image_setNativeContext);
603     }
604     env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer,
605             reinterpret_cast<jlong>(buffer.get()));
606 
607     env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
608 }
609 
Image_unlockIfLocked(JNIEnv * env,jobject thiz)610 static void Image_unlockIfLocked(JNIEnv* env, jobject thiz) {
611     ALOGV("%s", __FUNCTION__);
612     GraphicBuffer* buffer;
613     Image_getNativeContext(env, thiz, &buffer, NULL);
614     if (buffer == NULL) {
615         jniThrowException(env, "java/lang/IllegalStateException",
616                 "Image is not initialized");
617         return;
618     }
619 
620     // Is locked?
621     bool isLocked = false;
622     jobject planes = NULL;
623     if (!isFormatOpaque(buffer->getPixelFormat())) {
624         planes = env->GetObjectField(thiz, gSurfaceImageClassInfo.mPlanes);
625     }
626     isLocked = (planes != NULL);
627     if (isLocked) {
628         // no need to use fence here, as we it will be consumed by either cancel or queue buffer.
629         status_t res = buffer->unlock();
630         if (res != OK) {
631             jniThrowRuntimeException(env, "unlock buffer failed");
632         }
633         ALOGV("Successfully unlocked the image");
634     }
635 }
636 
Image_getWidth(JNIEnv * env,jobject thiz)637 static jint Image_getWidth(JNIEnv* env, jobject thiz) {
638     ALOGV("%s", __FUNCTION__);
639     GraphicBuffer* buffer;
640     Image_getNativeContext(env, thiz, &buffer, NULL);
641     if (buffer == NULL) {
642         jniThrowException(env, "java/lang/IllegalStateException",
643                 "Image is not initialized");
644         return -1;
645     }
646 
647     return buffer->getWidth();
648 }
649 
Image_getHeight(JNIEnv * env,jobject thiz)650 static jint Image_getHeight(JNIEnv* env, jobject thiz) {
651     ALOGV("%s", __FUNCTION__);
652     GraphicBuffer* buffer;
653     Image_getNativeContext(env, thiz, &buffer, NULL);
654     if (buffer == NULL) {
655         jniThrowException(env, "java/lang/IllegalStateException",
656                 "Image is not initialized");
657         return -1;
658     }
659 
660     return buffer->getHeight();
661 }
662 
Image_getFormat(JNIEnv * env,jobject thiz)663 static jint Image_getFormat(JNIEnv* env, jobject thiz) {
664     ALOGV("%s", __FUNCTION__);
665     GraphicBuffer* buffer;
666     Image_getNativeContext(env, thiz, &buffer, NULL);
667     if (buffer == NULL) {
668         jniThrowException(env, "java/lang/IllegalStateException",
669                 "Image is not initialized");
670         return 0;
671     }
672 
673     // ImageWriter doesn't support data space yet, assuming it is unknown.
674     PublicFormat publicFmt = android_view_Surface_mapHalFormatDataspaceToPublicFormat(
675             buffer->getPixelFormat(), HAL_DATASPACE_UNKNOWN);
676     return static_cast<jint>(publicFmt);
677 }
678 
Image_setFenceFd(JNIEnv * env,jobject thiz,int fenceFd)679 static void Image_setFenceFd(JNIEnv* env, jobject thiz, int fenceFd) {
680     ALOGV("%s:", __FUNCTION__);
681     env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
682 }
683 
Image_getLockedImage(JNIEnv * env,jobject thiz,LockedImage * image)684 static void Image_getLockedImage(JNIEnv* env, jobject thiz, LockedImage *image) {
685     ALOGV("%s", __FUNCTION__);
686     GraphicBuffer* buffer;
687     int fenceFd = -1;
688     Image_getNativeContext(env, thiz, &buffer, &fenceFd);
689     if (buffer == NULL) {
690         jniThrowException(env, "java/lang/IllegalStateException",
691                 "Image is not initialized");
692         return;
693     }
694 
695     // ImageWriter doesn't use crop by itself, app sets it, use the no crop version.
696     const Rect noCrop(buffer->width, buffer->height);
697     status_t res = lockImageFromBuffer(
698             buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, noCrop, fenceFd, image);
699     // Clear the fenceFd as it is already consumed by lock call.
700     Image_setFenceFd(env, thiz, /*fenceFd*/-1);
701     if (res != OK) {
702         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
703                 "lock buffer failed for format 0x%x",
704                 buffer->getPixelFormat());
705         return;
706     }
707 
708     ALOGV("%s: Successfully locked the image", __FUNCTION__);
709     // crop, transform, scalingMode, timestamp, and frameNumber should be set by producer,
710     // and we don't set them here.
711 }
712 
Image_getLockedImageInfo(JNIEnv * env,LockedImage * buffer,int idx,int32_t writerFormat,uint8_t ** base,uint32_t * size,int * pixelStride,int * rowStride)713 static void Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx,
714         int32_t writerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) {
715     ALOGV("%s", __FUNCTION__);
716 
717     status_t res = getLockedImageInfo(buffer, idx, writerFormat, base, size,
718             pixelStride, rowStride);
719     if (res != OK) {
720         jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
721                              "Pixel format: 0x%x is unsupported", buffer->flexFormat);
722     }
723 }
724 
Image_createSurfacePlanes(JNIEnv * env,jobject thiz,int numPlanes,int writerFormat)725 static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz,
726         int numPlanes, int writerFormat) {
727     ALOGV("%s: create SurfacePlane array with size %d", __FUNCTION__, numPlanes);
728     int rowStride, pixelStride;
729     uint8_t *pData;
730     uint32_t dataSize;
731     jobject byteBuffer;
732 
733     int format = Image_getFormat(env, thiz);
734     if (isFormatOpaque(format) && numPlanes > 0) {
735         String8 msg;
736         msg.appendFormat("Format 0x%x is opaque, thus not writable, the number of planes (%d)"
737                 " must be 0", format, numPlanes);
738         jniThrowException(env, "java/lang/IllegalArgumentException", msg.string());
739         return NULL;
740     }
741 
742     jobjectArray surfacePlanes = env->NewObjectArray(numPlanes, gSurfacePlaneClassInfo.clazz,
743             /*initial_element*/NULL);
744     if (surfacePlanes == NULL) {
745         jniThrowRuntimeException(env, "Failed to create SurfacePlane arrays,"
746                 " probably out of memory");
747         return NULL;
748     }
749     if (isFormatOpaque(format)) {
750         return surfacePlanes;
751     }
752 
753     // Buildup buffer info: rowStride, pixelStride and byteBuffers.
754     LockedImage lockedImg = LockedImage();
755     Image_getLockedImage(env, thiz, &lockedImg);
756 
757     // Create all SurfacePlanes
758     PublicFormat publicWriterFormat = static_cast<PublicFormat>(writerFormat);
759     writerFormat = android_view_Surface_mapPublicFormatToHalFormat(publicWriterFormat);
760     for (int i = 0; i < numPlanes; i++) {
761         Image_getLockedImageInfo(env, &lockedImg, i, writerFormat,
762                 &pData, &dataSize, &pixelStride, &rowStride);
763         byteBuffer = env->NewDirectByteBuffer(pData, dataSize);
764         if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) {
765             jniThrowException(env, "java/lang/IllegalStateException",
766                     "Failed to allocate ByteBuffer");
767             return NULL;
768         }
769 
770         // Finally, create this SurfacePlane.
771         jobject surfacePlane = env->NewObject(gSurfacePlaneClassInfo.clazz,
772                     gSurfacePlaneClassInfo.ctor, thiz, rowStride, pixelStride, byteBuffer);
773         env->SetObjectArrayElement(surfacePlanes, i, surfacePlane);
774     }
775 
776     return surfacePlanes;
777 }
778 
779 } // extern "C"
780 
781 // ----------------------------------------------------------------------------
782 
783 static JNINativeMethod gImageWriterMethods[] = {
784     {"nativeClassInit",         "()V",                        (void*)ImageWriter_classInit },
785     {"nativeInit",              "(Ljava/lang/Object;Landroid/view/Surface;II)J",
786                                                               (void*)ImageWriter_init },
787     {"nativeClose",              "(J)V",                      (void*)ImageWriter_close },
788     {"nativeAttachAndQueueImage", "(JJIJIIII)I",          (void*)ImageWriter_attachAndQueueImage },
789     {"nativeDequeueInputImage", "(JLandroid/media/Image;)V",  (void*)ImageWriter_dequeueImage },
790     {"nativeQueueInputImage",   "(JLandroid/media/Image;JIIII)V",  (void*)ImageWriter_queueImage },
791     {"cancelImage",             "(JLandroid/media/Image;)V",   (void*)ImageWriter_cancelImage },
792 };
793 
794 static JNINativeMethod gImageMethods[] = {
795     {"nativeCreatePlanes",      "(II)[Landroid/media/ImageWriter$WriterSurfaceImage$SurfacePlane;",
796                                                               (void*)Image_createSurfacePlanes },
797     {"nativeGetWidth",         "()I",                         (void*)Image_getWidth },
798     {"nativeGetHeight",        "()I",                         (void*)Image_getHeight },
799     {"nativeGetFormat",        "()I",                         (void*)Image_getFormat },
800 };
801 
register_android_media_ImageWriter(JNIEnv * env)802 int register_android_media_ImageWriter(JNIEnv *env) {
803 
804     int ret1 = AndroidRuntime::registerNativeMethods(env,
805                    "android/media/ImageWriter", gImageWriterMethods, NELEM(gImageWriterMethods));
806 
807     int ret2 = AndroidRuntime::registerNativeMethods(env,
808                    "android/media/ImageWriter$WriterSurfaceImage", gImageMethods, NELEM(gImageMethods));
809 
810     return (ret1 || ret2);
811 }
812