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