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