android_media_ImageReader.cpp revision 534046d2b12fd13776ad782b982649cb0bea9b79
1/* 2 * Copyright 2013 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 "ImageReader_JNI" 19#include <utils/Log.h> 20#include <utils/misc.h> 21#include <utils/List.h> 22 23#include <cstdio> 24 25#include <gui/CpuConsumer.h> 26#include <gui/Surface.h> 27#include <camera3.h> 28 29#include <android_runtime/AndroidRuntime.h> 30#include <android_runtime/android_view_Surface.h> 31 32#include <jni.h> 33#include <JNIHelp.h> 34 35#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) ) 36 37#define ANDROID_MEDIA_IMAGEREADER_JNI_ID "mCpuConsumer" 38#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext" 39#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mLockedBuffer" 40#define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID "mTimestamp" 41 42// ---------------------------------------------------------------------------- 43 44using namespace android; 45 46enum { 47 IMAGE_READER_MAX_NUM_PLANES = 3, 48}; 49 50struct fields_t { 51 // For ImageReader class 52 jfieldID imageReaderContext; 53 jmethodID postEvent; 54 // For SurfaceImage class 55 jfieldID buffer; 56 jfieldID timeStamp; 57}; 58 59struct classInfo_t { 60 jclass clazz; 61 jmethodID ctor; 62}; 63 64static fields_t fields; 65static classInfo_t surfPlaneClassInfo; 66 67// ---------------------------------------------------------------------------- 68 69class JNIImageReaderContext : public CpuConsumer::FrameAvailableListener 70{ 71public: 72 JNIImageReaderContext(JNIEnv* env, jobject weakThiz, jclass clazz, int maxImages); 73 74 virtual ~JNIImageReaderContext(); 75 76 virtual void onFrameAvailable(); 77 78 CpuConsumer::LockedBuffer* getLockedBuffer(); 79 80 void returnLockedBuffer(CpuConsumer::LockedBuffer* buffer); 81 82 CpuConsumer* getCpuConsumer() { return mConsumer.get(); } 83 84 void setCpuConsumer(sp<CpuConsumer> consumer) { mConsumer = consumer; } 85 86 void setBufferFormat(int format) { mFormat = format; } 87 int getBufferFormat() { return mFormat; } 88 89 void setBufferWidth(int width) { mWidth = width; } 90 int getBufferWidth() { return mWidth; } 91 92 void setBufferHeight(int height) { mHeight = height; } 93 int getBufferHeight() { return mHeight; } 94 95private: 96 static JNIEnv* getJNIEnv(bool* needsDetach); 97 static void detachJNI(); 98 99 List<CpuConsumer::LockedBuffer*> mBuffers; 100 sp<CpuConsumer> mConsumer; 101 jobject mWeakThiz; 102 jclass mClazz; 103 int mFormat; 104 int mWidth; 105 int mHeight; 106}; 107 108JNIImageReaderContext::JNIImageReaderContext(JNIEnv* env, 109 jobject weakThiz, jclass clazz, int maxImages) : 110 mWeakThiz(env->NewGlobalRef(weakThiz)), 111 mClazz((jclass)env->NewGlobalRef(clazz)) { 112 for (int i = 0; i < maxImages; i++) { 113 CpuConsumer::LockedBuffer *buffer = new CpuConsumer::LockedBuffer; 114 mBuffers.push_back(buffer); 115 } 116} 117 118JNIEnv* JNIImageReaderContext::getJNIEnv(bool* needsDetach) { 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 135void JNIImageReaderContext::detachJNI() { 136 JavaVM* vm = AndroidRuntime::getJavaVM(); 137 int result = vm->DetachCurrentThread(); 138 if (result != JNI_OK) { 139 ALOGE("thread detach failed: %#x", result); 140 } 141} 142 143CpuConsumer::LockedBuffer* JNIImageReaderContext::getLockedBuffer() { 144 if (mBuffers.empty()) { 145 return NULL; 146 } 147 // Return a LockedBuffer pointer and remove it from the list 148 List<CpuConsumer::LockedBuffer*>::iterator it = mBuffers.begin(); 149 CpuConsumer::LockedBuffer* buffer = *it; 150 mBuffers.erase(it); 151 return buffer; 152} 153 154void JNIImageReaderContext::returnLockedBuffer(CpuConsumer::LockedBuffer * buffer) { 155 mBuffers.push_back(buffer); 156} 157 158JNIImageReaderContext::~JNIImageReaderContext() { 159 bool needsDetach = false; 160 JNIEnv* env = getJNIEnv(&needsDetach); 161 if (env != NULL) { 162 env->DeleteGlobalRef(mWeakThiz); 163 env->DeleteGlobalRef(mClazz); 164 } else { 165 ALOGW("leaking JNI object references"); 166 } 167 if (needsDetach) { 168 detachJNI(); 169 } 170 171 // Delete LockedBuffers 172 for (List<CpuConsumer::LockedBuffer *>::iterator it = mBuffers.begin(); 173 it != mBuffers.end(); it++) { 174 delete *it; 175 } 176 mBuffers.clear(); 177 mConsumer.clear(); 178} 179 180void JNIImageReaderContext::onFrameAvailable() 181{ 182 ALOGV("%s: frame available", __FUNCTION__); 183 bool needsDetach = false; 184 JNIEnv* env = getJNIEnv(&needsDetach); 185 if (env != NULL) { 186 env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz); 187 } else { 188 ALOGW("onFrameAvailable event will not posted"); 189 } 190 if (needsDetach) { 191 detachJNI(); 192 } 193} 194 195// ---------------------------------------------------------------------------- 196 197extern "C" { 198 199static JNIImageReaderContext* ImageReader_getContext(JNIEnv* env, jobject thiz) 200{ 201 JNIImageReaderContext *ctx; 202 ctx = reinterpret_cast<JNIImageReaderContext *> 203 (env->GetLongField(thiz, fields.imageReaderContext)); 204 return ctx; 205} 206 207static CpuConsumer* ImageReader_getCpuConsumer(JNIEnv* env, jobject thiz) 208{ 209 ALOGV("%s:", __FUNCTION__); 210 JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz); 211 if (ctx == NULL) { 212 jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); 213 return NULL; 214 } 215 return ctx->getCpuConsumer(); 216} 217 218static void ImageReader_setNativeContext(JNIEnv* env, 219 jobject thiz, sp<JNIImageReaderContext> ctx) 220{ 221 ALOGV("%s:", __FUNCTION__); 222 JNIImageReaderContext* const p = ImageReader_getContext(env, thiz); 223 if (ctx != 0) { 224 ctx->incStrong((void*)ImageReader_setNativeContext); 225 } 226 if (p) { 227 p->decStrong((void*)ImageReader_setNativeContext); 228 } 229 env->SetLongField(thiz, fields.imageReaderContext, reinterpret_cast<jlong>(ctx.get())); 230} 231 232static CpuConsumer::LockedBuffer* Image_getLockedBuffer(JNIEnv* env, jobject image) 233{ 234 return reinterpret_cast<CpuConsumer::LockedBuffer*>(env->GetLongField(image, fields.buffer)); 235} 236 237static void Image_setBuffer(JNIEnv* env, jobject thiz, 238 const CpuConsumer::LockedBuffer* buffer) 239{ 240 env->SetLongField(thiz, fields.buffer, reinterpret_cast<jlong>(buffer)); 241} 242 243// Some formats like JPEG defined with different values between android.graphics.ImageFormat and 244// graphics.h, need convert to the one defined in graphics.h here. 245static int Image_getPixelFormat(JNIEnv* env, int format) 246{ 247 int jpegFormat, rawSensorFormat; 248 jfieldID fid; 249 250 ALOGV("%s: format = 0x%x", __FUNCTION__, format); 251 252 jclass imageFormatClazz = env->FindClass("android/graphics/ImageFormat"); 253 ALOG_ASSERT(imageFormatClazz != NULL); 254 255 fid = env->GetStaticFieldID(imageFormatClazz, "JPEG", "I"); 256 jpegFormat = env->GetStaticIntField(imageFormatClazz, fid); 257 fid = env->GetStaticFieldID(imageFormatClazz, "RAW_SENSOR", "I"); 258 rawSensorFormat = env->GetStaticIntField(imageFormatClazz, fid); 259 260 // Translate the JPEG to BLOB for camera purpose, an add more if more mismatch is found. 261 if (format == jpegFormat) { 262 format = HAL_PIXEL_FORMAT_BLOB; 263 } 264 // Same thing for RAW_SENSOR format 265 if (format == rawSensorFormat) { 266 format = HAL_PIXEL_FORMAT_RAW_SENSOR; 267 } 268 269 return format; 270} 271 272static uint32_t Image_getJpegSize(CpuConsumer::LockedBuffer* buffer) 273{ 274 ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!"); 275 uint32_t size = 0; 276 uint32_t width = buffer->width; 277 uint8_t* jpegBuffer = buffer->data; 278 279 // First check for JPEG transport header at the end of the buffer 280 uint8_t* header = jpegBuffer + (width - sizeof(struct camera3_jpeg_blob)); 281 struct camera3_jpeg_blob *blob = (struct camera3_jpeg_blob*)(header); 282 if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID) { 283 size = blob->jpeg_size; 284 ALOGV("%s: Jpeg size = %d", __FUNCTION__, size); 285 } 286 287 // failed to find size, default to whole buffer 288 if (size == 0) { 289 size = width; 290 } 291 292 return size; 293} 294 295static void Image_getLockedBufferInfo(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx, 296 uint8_t **base, uint32_t *size) 297{ 298 ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!"); 299 ALOG_ASSERT(base != NULL, "base is NULL!!!"); 300 ALOG_ASSERT(size != NULL, "size is NULL!!!"); 301 ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0)); 302 303 ALOGV("%s: buffer: 0x%p", __FUNCTION__, buffer); 304 305 uint32_t dataSize, ySize, cSize, cStride; 306 uint8_t *cb, *cr; 307 uint8_t *pData = NULL; 308 309 dataSize = ySize = cSize = cStride = 0; 310 int32_t fmt = buffer->format; 311 switch (fmt) { 312 case HAL_PIXEL_FORMAT_YCbCr_420_888: 313 pData = 314 (idx == 0) ? 315 buffer->data : 316 (idx == 1) ? 317 buffer->dataCb : 318 buffer->dataCr; 319 if (idx == 0) { 320 dataSize = buffer->stride * buffer->height; 321 } else { 322 dataSize = buffer->chromaStride * buffer->height / 2; 323 } 324 break; 325 // NV21 326 case HAL_PIXEL_FORMAT_YCrCb_420_SP: 327 cr = buffer->data + (buffer->stride * buffer->height); 328 cb = cr + 1; 329 ySize = buffer->width * buffer->height; 330 cSize = buffer->width * buffer->height / 2; 331 332 pData = 333 (idx == 0) ? 334 buffer->data : 335 (idx == 1) ? 336 cb: 337 cr; 338 339 dataSize = (idx == 0) ? ySize : cSize; 340 break; 341 case HAL_PIXEL_FORMAT_YV12: 342 // Y and C stride need to be 16 pixel aligned. 343 LOG_ALWAYS_FATAL_IF(buffer->stride % 16, 344 "Stride is not 16 pixel aligned %d", buffer->stride); 345 346 ySize = buffer->stride * buffer->height; 347 cStride = ALIGN(buffer->stride / 2, 16); 348 cr = buffer->data + ySize; 349 cSize = cStride * buffer->height / 2; 350 cb = cr + cSize; 351 352 pData = 353 (idx == 0) ? 354 buffer->data : 355 (idx == 1) ? 356 cb : 357 cr; 358 dataSize = (idx == 0) ? ySize : cSize; 359 break; 360 case HAL_PIXEL_FORMAT_Y8: 361 // Single plane, 8bpp. 362 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 363 364 pData = buffer->data; 365 dataSize = buffer->stride * buffer->height; 366 break; 367 case HAL_PIXEL_FORMAT_Y16: 368 // Single plane, 16bpp, strides are specified in pixels, not in bytes 369 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 370 371 pData = buffer->data; 372 dataSize = buffer->stride * buffer->height * 2; 373 break; 374 case HAL_PIXEL_FORMAT_BLOB: 375 // Used for JPEG data, height must be 1, width == size, single plane. 376 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 377 ALOG_ASSERT(buffer->height == 1, "JPEG should has height value %d", buffer->height); 378 379 pData = buffer->data; 380 dataSize = Image_getJpegSize(buffer); 381 break; 382 case HAL_PIXEL_FORMAT_RAW_SENSOR: 383 // Single plane 16bpp bayer data. 384 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 385 pData = buffer->data; 386 dataSize = buffer->width * 2 * buffer->height; 387 break; 388 default: 389 jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", 390 "Pixel format: 0x%x is unsupported", fmt); 391 break; 392 } 393 394 *base = pData; 395 *size = dataSize; 396} 397 398static jint Image_imageGetPixelStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx) 399{ 400 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 401 ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0), "Index is out of range:%d", idx); 402 403 int pixelStride = 0; 404 ALOG_ASSERT(buffer != NULL, "buffer is NULL"); 405 406 int32_t fmt = buffer->format; 407 switch (fmt) { 408 case HAL_PIXEL_FORMAT_YCbCr_420_888: 409 pixelStride = (idx == 0) ? 1 : buffer->chromaStep; 410 break; 411 case HAL_PIXEL_FORMAT_YCrCb_420_SP: 412 pixelStride = (idx == 0) ? 1 : 2; 413 break; 414 case HAL_PIXEL_FORMAT_Y8: 415 // Single plane 8bpp data. 416 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 417 pixelStride; 418 break; 419 case HAL_PIXEL_FORMAT_YV12: 420 pixelStride = 1; 421 break; 422 case HAL_PIXEL_FORMAT_BLOB: 423 // Used for JPEG data, single plane, row and pixel strides are 0 424 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 425 pixelStride = 0; 426 break; 427 case HAL_PIXEL_FORMAT_Y16: 428 case HAL_PIXEL_FORMAT_RAW_SENSOR: 429 // Single plane 16bpp data. 430 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 431 pixelStride = 2; 432 break; 433 default: 434 jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", 435 "Pixel format: 0x%x is unsupported", fmt); 436 break; 437 } 438 439 return pixelStride; 440} 441 442static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx) 443{ 444 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 445 ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0)); 446 447 int rowStride = 0; 448 ALOG_ASSERT(buffer != NULL, "buffer is NULL"); 449 450 int32_t fmt = buffer->format; 451 452 switch (fmt) { 453 case HAL_PIXEL_FORMAT_YCbCr_420_888: 454 rowStride = (idx == 0) ? buffer->stride : buffer->chromaStride; 455 break; 456 case HAL_PIXEL_FORMAT_YCrCb_420_SP: 457 rowStride = buffer->width; 458 break; 459 case HAL_PIXEL_FORMAT_YV12: 460 LOG_ALWAYS_FATAL_IF(buffer->stride % 16, 461 "Stride is not 16 pixel aligned %d", buffer->stride); 462 rowStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16); 463 break; 464 case HAL_PIXEL_FORMAT_BLOB: 465 // Used for JPEG data, single plane, row and pixel strides are 0 466 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 467 rowStride = 0; 468 break; 469 case HAL_PIXEL_FORMAT_Y8: 470 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 471 LOG_ALWAYS_FATAL_IF(buffer->stride % 16, 472 "Stride is not 16 pixel aligned %d", buffer->stride); 473 rowStride = buffer->stride; 474 break; 475 case HAL_PIXEL_FORMAT_Y16: 476 case HAL_PIXEL_FORMAT_RAW_SENSOR: 477 // In native side, strides are specified in pixels, not in bytes. 478 // Single plane 16bpp bayer data. even width/height, 479 // row stride multiple of 16 pixels (32 bytes) 480 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 481 LOG_ALWAYS_FATAL_IF(buffer->stride % 16, 482 "Stride is not 16 pixel aligned %d", buffer->stride); 483 rowStride = buffer->stride * 2; 484 break; 485 default: 486 ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt); 487 jniThrowException(env, "java/lang/UnsupportedOperationException", 488 "unsupported buffer format"); 489 break; 490 } 491 492 return rowStride; 493} 494 495// ---------------------------------------------------------------------------- 496 497static void ImageReader_classInit(JNIEnv* env, jclass clazz) 498{ 499 ALOGV("%s:", __FUNCTION__); 500 501 jclass imageClazz = env->FindClass("android/media/ImageReader$SurfaceImage"); 502 LOG_ALWAYS_FATAL_IF(imageClazz == NULL, 503 "can't find android/graphics/ImageReader$SurfaceImage"); 504 fields.buffer = env->GetFieldID(imageClazz, ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID, "J"); 505 LOG_ALWAYS_FATAL_IF(fields.buffer == NULL, 506 "can't find android/graphics/ImageReader.%s", 507 ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID); 508 509 fields.timeStamp = env->GetFieldID(imageClazz, ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID, "J"); 510 LOG_ALWAYS_FATAL_IF(fields.timeStamp == NULL, 511 "can't find android/graphics/ImageReader.%s", 512 ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID); 513 514 fields.imageReaderContext = env->GetFieldID(clazz, ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID, "J"); 515 LOG_ALWAYS_FATAL_IF(fields.imageReaderContext == NULL, 516 "can't find android/graphics/ImageReader.%s", 517 ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID); 518 519 fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative", 520 "(Ljava/lang/Object;)V"); 521 LOG_ALWAYS_FATAL_IF(fields.postEvent == NULL, 522 "can't find android/graphics/ImageReader.postEventFromNative"); 523 524 jclass planeClazz = env->FindClass("android/media/ImageReader$SurfaceImage$SurfacePlane"); 525 LOG_ALWAYS_FATAL_IF(planeClazz == NULL, "Can not find SurfacePlane class"); 526 // FindClass only gives a local reference of jclass object. 527 surfPlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz); 528 surfPlaneClassInfo.ctor = env->GetMethodID(surfPlaneClassInfo.clazz, "<init>", 529 "(Landroid/media/ImageReader$SurfaceImage;III)V"); 530 LOG_ALWAYS_FATAL_IF(surfPlaneClassInfo.ctor == NULL, "Can not find SurfacePlane constructor"); 531} 532 533static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, 534 jint width, jint height, jint format, jint maxImages) 535{ 536 status_t res; 537 int nativeFormat; 538 539 ALOGV("%s: width:%d, height: %d, format: 0x%x, maxImages:%d", 540 __FUNCTION__, width, height, format, maxImages); 541 542 nativeFormat = Image_getPixelFormat(env, format); 543 544 sp<CpuConsumer> consumer = new CpuConsumer(maxImages); 545 // TODO: throw dvm exOutOfMemoryError? 546 if (consumer == NULL) { 547 jniThrowRuntimeException(env, "Failed to allocate native CpuConsumer"); 548 return; 549 } 550 551 jclass clazz = env->GetObjectClass(thiz); 552 if (clazz == NULL) { 553 jniThrowRuntimeException(env, "Can't find android/graphics/ImageReader"); 554 return; 555 } 556 sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages)); 557 ctx->setCpuConsumer(consumer); 558 consumer->setFrameAvailableListener(ctx); 559 ImageReader_setNativeContext(env, thiz, ctx); 560 ctx->setBufferFormat(nativeFormat); 561 ctx->setBufferWidth(width); 562 ctx->setBufferHeight(height); 563 564 // Set the width/height/format to the CpuConsumer 565 res = consumer->setDefaultBufferSize(width, height); 566 if (res != OK) { 567 jniThrowException(env, "java/lang/IllegalStateException", 568 "Failed to set CpuConsumer buffer size"); 569 return; 570 } 571 res = consumer->setDefaultBufferFormat(nativeFormat); 572 if (res != OK) { 573 jniThrowException(env, "java/lang/IllegalStateException", 574 "Failed to set CpuConsumer buffer format"); 575 } 576} 577 578static void ImageReader_close(JNIEnv* env, jobject thiz) 579{ 580 ALOGV("%s:", __FUNCTION__); 581 582 JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz); 583 if (ctx == NULL) { 584 // ImageReader is already closed. 585 return; 586 } 587 588 CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz); 589 if (consumer != NULL) { 590 consumer->abandon(); 591 consumer->setFrameAvailableListener(NULL); 592 } 593 ImageReader_setNativeContext(env, thiz, NULL); 594} 595 596static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image) 597{ 598 ALOGV("%s:", __FUNCTION__); 599 JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); 600 if (ctx == NULL) { 601 ALOGW("ImageReader#close called before Image#close, consider calling Image#close first"); 602 return; 603 } 604 605 CpuConsumer* consumer = ctx->getCpuConsumer(); 606 CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, image); 607 if (!buffer) { 608 ALOGW("Image already released!!!"); 609 return; 610 } 611 consumer->unlockBuffer(*buffer); 612 Image_setBuffer(env, image, NULL); 613 ctx->returnLockedBuffer(buffer); 614} 615 616static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, 617 jobject image) 618{ 619 ALOGV("%s:", __FUNCTION__); 620 JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); 621 if (ctx == NULL) { 622 jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); 623 return false; 624 } 625 626 CpuConsumer* consumer = ctx->getCpuConsumer(); 627 CpuConsumer::LockedBuffer* buffer = ctx->getLockedBuffer(); 628 if (buffer == NULL) { 629 ALOGE("Unable to acquire a lockedBuffer, very likely client tries to lock more than" 630 "maxImages buffers"); 631 return false; 632 } 633 status_t res = consumer->lockNextBuffer(buffer); 634 if (res != NO_ERROR) { 635 ALOGE("%s Fail to lockNextBuffer with error: 0x%x ", __FUNCTION__, res); 636 return false; 637 } 638 639 640 // Check if the left-top corner of the crop rect is origin, we currently assume this point is 641 // zero, will revist this once this assumption turns out problematic. 642 Point lt = buffer->crop.leftTop(); 643 if (lt.x != 0 || lt.y != 0) { 644 ALOGE("crop left: %d, top = %d", lt.x, lt.y); 645 jniThrowException(env, "java/lang/UnsupportedOperationException", 646 "crop left top corner need to at origin"); 647 } 648 649 // Check if the producer buffer configurations match what ImageReader configured. 650 // We want to fail for the very first image because this case is too bad. 651 int outputWidth = buffer->width; 652 int outputHeight = buffer->height; 653 654 // Correct with/height when crop is set. 655 if (buffer->crop.getWidth() > 0) { 656 outputWidth = buffer->crop.getWidth() + 1; 657 } 658 if (buffer->crop.getHeight() > 0) { 659 outputHeight = buffer->crop.getHeight() + 1; 660 } 661 662 int imageReaderWidth = ctx->getBufferWidth(); 663 int imageReaderHeight = ctx->getBufferHeight(); 664 if ((imageReaderWidth != outputWidth) || 665 (imageReaderHeight != outputHeight)) { 666 // Spew warning for now, since MediaCodec decoder has a bug to setup the right crop 667 // TODO: make it throw exception once the decoder bug is fixed. 668 ALOGW("Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", 669 outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); 670 } 671 672 if (ctx->getBufferFormat() != buffer->format) { 673 // Return the buffer to the queue. 674 consumer->unlockBuffer(*buffer); 675 ctx->returnLockedBuffer(buffer); 676 677 // Throw exception 678 ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x", 679 buffer->format, ctx->getBufferFormat()); 680 jniThrowException(env, "java/lang/UnsupportedOperationException", 681 "The producer output buffer configuration doesn't match the ImageReader" 682 "configured"); 683 return false; 684 } 685 // Set SurfaceImage instance member variables 686 Image_setBuffer(env, image, buffer); 687 env->SetLongField(image, fields.timeStamp, static_cast<jlong>(buffer->timestamp)); 688 689 return true; 690} 691 692static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) 693{ 694 ALOGV("%s: ", __FUNCTION__); 695 696 CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz); 697 if (consumer == NULL) { 698 jniThrowRuntimeException(env, "CpuConsumer is uninitialized"); 699 return NULL; 700 } 701 702 // Wrap the IGBP in a Java-language Surface. 703 return android_view_Surface_createFromIGraphicBufferProducer(env, 704 consumer->getProducerInterface()); 705} 706 707static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx) 708{ 709 int rowStride, pixelStride; 710 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 711 712 CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); 713 714 ALOG_ASSERT(buffer != NULL); 715 if (buffer == NULL) { 716 jniThrowException(env, "java/lang/IllegalStateException", "Image was released"); 717 } 718 rowStride = Image_imageGetRowStride(env, buffer, idx); 719 pixelStride = Image_imageGetPixelStride(env, buffer, idx); 720 721 jobject surfPlaneObj = env->NewObject(surfPlaneClassInfo.clazz, surfPlaneClassInfo.ctor, 722 thiz, idx, rowStride, pixelStride); 723 724 return surfPlaneObj; 725} 726 727static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx) 728{ 729 uint8_t *base = NULL; 730 uint32_t size = 0; 731 jobject byteBuffer; 732 733 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 734 735 CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); 736 737 if (buffer == NULL) { 738 jniThrowException(env, "java/lang/IllegalStateException", "Image was released"); 739 } 740 741 // Create byteBuffer from native buffer 742 Image_getLockedBufferInfo(env, buffer, idx, &base, &size); 743 byteBuffer = env->NewDirectByteBuffer(base, size); 744 // TODO: throw dvm exOutOfMemoryError? 745 if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) { 746 jniThrowException(env, "java/lang/IllegalStateException", "Failed to allocate ByteBuffer"); 747 } 748 749 return byteBuffer; 750} 751 752} // extern "C" 753 754// ---------------------------------------------------------------------------- 755 756static JNINativeMethod gImageReaderMethods[] = { 757 {"nativeClassInit", "()V", (void*)ImageReader_classInit }, 758 {"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init }, 759 {"nativeClose", "()V", (void*)ImageReader_close }, 760 {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease }, 761 {"nativeImageSetup", "(Landroid/media/Image;)Z", (void*)ImageReader_imageSetup }, 762 {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface }, 763}; 764 765static JNINativeMethod gImageMethods[] = { 766 {"nativeImageGetBuffer", "(I)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer }, 767 {"nativeCreatePlane", "(I)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;", 768 (void*)Image_createSurfacePlane }, 769}; 770 771int register_android_media_ImageReader(JNIEnv *env) { 772 773 int ret1 = AndroidRuntime::registerNativeMethods(env, 774 "android/media/ImageReader", gImageReaderMethods, NELEM(gImageReaderMethods)); 775 776 int ret2 = AndroidRuntime::registerNativeMethods(env, 777 "android/media/ImageReader$SurfaceImage", gImageMethods, NELEM(gImageMethods)); 778 779 return (ret1 || ret2); 780} 781