android_media_ImageReader.cpp revision e32632682ca9207bd247ca27012cf670b5c23f54
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<BufferQueue> bq = new BufferQueue(); 545 sp<CpuConsumer> consumer = new CpuConsumer(bq, true, maxImages); 546 // TODO: throw dvm exOutOfMemoryError? 547 if (consumer == NULL) { 548 jniThrowRuntimeException(env, "Failed to allocate native CpuConsumer"); 549 return; 550 } 551 552 jclass clazz = env->GetObjectClass(thiz); 553 if (clazz == NULL) { 554 jniThrowRuntimeException(env, "Can't find android/graphics/ImageReader"); 555 return; 556 } 557 sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages)); 558 ctx->setCpuConsumer(consumer); 559 consumer->setFrameAvailableListener(ctx); 560 ImageReader_setNativeContext(env, thiz, ctx); 561 ctx->setBufferFormat(nativeFormat); 562 ctx->setBufferWidth(width); 563 ctx->setBufferHeight(height); 564 565 // Set the width/height/format to the CpuConsumer 566 res = consumer->setDefaultBufferSize(width, height); 567 if (res != OK) { 568 jniThrowException(env, "java/lang/IllegalStateException", 569 "Failed to set CpuConsumer buffer size"); 570 return; 571 } 572 res = consumer->setDefaultBufferFormat(nativeFormat); 573 if (res != OK) { 574 jniThrowException(env, "java/lang/IllegalStateException", 575 "Failed to set CpuConsumer buffer format"); 576 } 577} 578 579static void ImageReader_close(JNIEnv* env, jobject thiz) 580{ 581 ALOGV("%s:", __FUNCTION__); 582 583 JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz); 584 if (ctx == NULL) { 585 // ImageReader is already closed. 586 return; 587 } 588 589 CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz); 590 if (consumer != NULL) { 591 consumer->abandon(); 592 consumer->setFrameAvailableListener(NULL); 593 } 594 ImageReader_setNativeContext(env, thiz, NULL); 595} 596 597static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image) 598{ 599 ALOGV("%s:", __FUNCTION__); 600 JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); 601 if (ctx == NULL) { 602 ALOGW("ImageReader#close called before Image#close, consider calling Image#close first"); 603 return; 604 } 605 606 CpuConsumer* consumer = ctx->getCpuConsumer(); 607 CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, image); 608 if (!buffer) { 609 ALOGW("Image already released!!!"); 610 return; 611 } 612 consumer->unlockBuffer(*buffer); 613 Image_setBuffer(env, image, NULL); 614 ctx->returnLockedBuffer(buffer); 615} 616 617static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, 618 jobject image) 619{ 620 ALOGV("%s:", __FUNCTION__); 621 JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); 622 if (ctx == NULL) { 623 jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); 624 return false; 625 } 626 627 CpuConsumer* consumer = ctx->getCpuConsumer(); 628 CpuConsumer::LockedBuffer* buffer = ctx->getLockedBuffer(); 629 if (buffer == NULL) { 630 ALOGE("Unable to acquire a lockedBuffer, very likely client tries to lock more than" 631 "maxImages buffers"); 632 return false; 633 } 634 status_t res = consumer->lockNextBuffer(buffer); 635 if (res != NO_ERROR) { 636 ALOGE("%s Fail to lockNextBuffer with error: 0x%x ", __FUNCTION__, res); 637 return false; 638 } 639 640 641 // Check if the left-top corner of the crop rect is origin, we currently assume this point is 642 // zero, will revist this once this assumption turns out problematic. 643 Point lt = buffer->crop.leftTop(); 644 if (lt.x != 0 || lt.y != 0) { 645 ALOGE("crop left: %d, top = %d", lt.x, lt.y); 646 jniThrowException(env, "java/lang/UnsupportedOperationException", 647 "crop left top corner need to at origin"); 648 } 649 650 // Check if the producer buffer configurations match what ImageReader configured. 651 // We want to fail for the very first image because this case is too bad. 652 int outputWidth = buffer->width; 653 int outputHeight = buffer->height; 654 655 // Correct with/height when crop is set. 656 if (buffer->crop.getWidth() > 0) { 657 outputWidth = buffer->crop.getWidth() + 1; 658 } 659 if (buffer->crop.getHeight() > 0) { 660 outputHeight = buffer->crop.getHeight() + 1; 661 } 662 663 int imageReaderWidth = ctx->getBufferWidth(); 664 int imageReaderHeight = ctx->getBufferHeight(); 665 if ((imageReaderWidth != outputWidth) || 666 (imageReaderHeight != outputHeight)) { 667 // Spew warning for now, since MediaCodec decoder has a bug to setup the right crop 668 // TODO: make it throw exception once the decoder bug is fixed. 669 ALOGW("Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", 670 outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); 671 } 672 673 if (ctx->getBufferFormat() != buffer->format) { 674 // Return the buffer to the queue. 675 consumer->unlockBuffer(*buffer); 676 ctx->returnLockedBuffer(buffer); 677 678 // Throw exception 679 ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x", 680 buffer->format, ctx->getBufferFormat()); 681 jniThrowException(env, "java/lang/UnsupportedOperationException", 682 "The producer output buffer configuration doesn't match the ImageReader" 683 "configured"); 684 return false; 685 } 686 // Set SurfaceImage instance member variables 687 Image_setBuffer(env, image, buffer); 688 env->SetLongField(image, fields.timeStamp, static_cast<jlong>(buffer->timestamp)); 689 690 return true; 691} 692 693static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) 694{ 695 ALOGV("%s: ", __FUNCTION__); 696 697 CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz); 698 if (consumer == NULL) { 699 jniThrowRuntimeException(env, "CpuConsumer is uninitialized"); 700 return NULL; 701 } 702 703 // Wrap the IGBP in a Java-language Surface. 704 return android_view_Surface_createFromIGraphicBufferProducer(env, 705 consumer->getProducerInterface()); 706} 707 708static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx) 709{ 710 int rowStride, pixelStride; 711 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 712 713 CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); 714 715 ALOG_ASSERT(buffer != NULL); 716 if (buffer == NULL) { 717 jniThrowException(env, "java/lang/IllegalStateException", "Image was released"); 718 } 719 rowStride = Image_imageGetRowStride(env, buffer, idx); 720 pixelStride = Image_imageGetPixelStride(env, buffer, idx); 721 722 jobject surfPlaneObj = env->NewObject(surfPlaneClassInfo.clazz, surfPlaneClassInfo.ctor, 723 thiz, idx, rowStride, pixelStride); 724 725 return surfPlaneObj; 726} 727 728static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx) 729{ 730 uint8_t *base = NULL; 731 uint32_t size = 0; 732 jobject byteBuffer; 733 734 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 735 736 CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); 737 738 if (buffer == NULL) { 739 jniThrowException(env, "java/lang/IllegalStateException", "Image was released"); 740 } 741 742 // Create byteBuffer from native buffer 743 Image_getLockedBufferInfo(env, buffer, idx, &base, &size); 744 byteBuffer = env->NewDirectByteBuffer(base, size); 745 // TODO: throw dvm exOutOfMemoryError? 746 if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) { 747 jniThrowException(env, "java/lang/IllegalStateException", "Failed to allocate ByteBuffer"); 748 } 749 750 return byteBuffer; 751} 752 753} // extern "C" 754 755// ---------------------------------------------------------------------------- 756 757static JNINativeMethod gImageReaderMethods[] = { 758 {"nativeClassInit", "()V", (void*)ImageReader_classInit }, 759 {"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init }, 760 {"nativeClose", "()V", (void*)ImageReader_close }, 761 {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease }, 762 {"nativeImageSetup", "(Landroid/media/Image;)Z", (void*)ImageReader_imageSetup }, 763 {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface }, 764}; 765 766static JNINativeMethod gImageMethods[] = { 767 {"nativeImageGetBuffer", "(I)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer }, 768 {"nativeCreatePlane", "(I)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;", 769 (void*)Image_createSurfacePlane }, 770}; 771 772int register_android_media_ImageReader(JNIEnv *env) { 773 774 int ret1 = AndroidRuntime::registerNativeMethods(env, 775 "android/media/ImageReader", gImageReaderMethods, NELEM(gImageReaderMethods)); 776 777 int ret2 = AndroidRuntime::registerNativeMethods(env, 778 "android/media/ImageReader$SurfaceImage", gImageMethods, NELEM(gImageMethods)); 779 780 return (ret1 || ret2); 781} 782