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 68 void setProducer(const sp<Surface>& producer) { mProducer = producer; } 69 Surface* getProducer() { return mProducer.get(); } 70 71 void setBufferFormat(int format) { mFormat = format; } 72 int getBufferFormat() { return mFormat; } 73 74 void setBufferWidth(int width) { mWidth = width; } 75 int getBufferWidth() { return mWidth; } 76 77 void setBufferHeight(int height) { mHeight = height; } 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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