android_media_ImageReader.cpp revision 708e3595031fa15f4ac26c5675a53c1ed495b895
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: %p", __FUNCTION__, buffer); 304 305 uint32_t dataSize, ySize, cSize, cStride; 306 uint8_t *cb, *cr; 307 uint8_t *pData = NULL; 308 int bytesPerPixel = 0; 309 310 dataSize = ySize = cSize = cStride = 0; 311 int32_t fmt = buffer->format; 312 switch (fmt) { 313 case HAL_PIXEL_FORMAT_YCbCr_420_888: 314 pData = 315 (idx == 0) ? 316 buffer->data : 317 (idx == 1) ? 318 buffer->dataCb : 319 buffer->dataCr; 320 if (idx == 0) { 321 dataSize = buffer->stride * buffer->height; 322 } else { 323 dataSize = buffer->chromaStride * buffer->height / 2; 324 } 325 break; 326 // NV21 327 case HAL_PIXEL_FORMAT_YCrCb_420_SP: 328 cr = buffer->data + (buffer->stride * buffer->height); 329 cb = cr + 1; 330 ySize = buffer->width * buffer->height; 331 cSize = buffer->width * buffer->height / 2; 332 333 pData = 334 (idx == 0) ? 335 buffer->data : 336 (idx == 1) ? 337 cb: 338 cr; 339 340 dataSize = (idx == 0) ? ySize : cSize; 341 break; 342 case HAL_PIXEL_FORMAT_YV12: 343 // Y and C stride need to be 16 pixel aligned. 344 LOG_ALWAYS_FATAL_IF(buffer->stride % 16, 345 "Stride is not 16 pixel aligned %d", buffer->stride); 346 347 ySize = buffer->stride * buffer->height; 348 cStride = ALIGN(buffer->stride / 2, 16); 349 cr = buffer->data + ySize; 350 cSize = cStride * buffer->height / 2; 351 cb = cr + cSize; 352 353 pData = 354 (idx == 0) ? 355 buffer->data : 356 (idx == 1) ? 357 cb : 358 cr; 359 dataSize = (idx == 0) ? ySize : cSize; 360 break; 361 case HAL_PIXEL_FORMAT_Y8: 362 // Single plane, 8bpp. 363 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 364 365 pData = buffer->data; 366 dataSize = buffer->stride * buffer->height; 367 break; 368 case HAL_PIXEL_FORMAT_Y16: 369 // Single plane, 16bpp, strides are specified in pixels, not in bytes 370 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 371 372 pData = buffer->data; 373 dataSize = buffer->stride * buffer->height * 2; 374 break; 375 case HAL_PIXEL_FORMAT_BLOB: 376 // Used for JPEG data, height must be 1, width == size, single plane. 377 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 378 ALOG_ASSERT(buffer->height == 1, "JPEG should has height value %d", buffer->height); 379 380 pData = buffer->data; 381 dataSize = Image_getJpegSize(buffer); 382 break; 383 case HAL_PIXEL_FORMAT_RAW_SENSOR: 384 // Single plane 16bpp bayer data. 385 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 386 pData = buffer->data; 387 dataSize = buffer->width * 2 * buffer->height; 388 break; 389 case HAL_PIXEL_FORMAT_RGBA_8888: 390 case HAL_PIXEL_FORMAT_RGBX_8888: 391 // Single plane, 32bpp. 392 bytesPerPixel = 4; 393 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 394 pData = buffer->data; 395 dataSize = buffer->stride * buffer->height * bytesPerPixel; 396 break; 397 case HAL_PIXEL_FORMAT_RGB_565: 398 // Single plane, 16bpp. 399 bytesPerPixel = 2; 400 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 401 pData = buffer->data; 402 dataSize = buffer->stride * buffer->height * bytesPerPixel; 403 break; 404 case HAL_PIXEL_FORMAT_RGB_888: 405 // Single plane, 24bpp. 406 bytesPerPixel = 3; 407 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 408 pData = buffer->data; 409 dataSize = buffer->stride * buffer->height * bytesPerPixel; 410 break; 411 default: 412 jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", 413 "Pixel format: 0x%x is unsupported", fmt); 414 break; 415 } 416 417 *base = pData; 418 *size = dataSize; 419} 420 421static jint Image_imageGetPixelStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx) 422{ 423 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 424 ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0), "Index is out of range:%d", idx); 425 426 int pixelStride = 0; 427 ALOG_ASSERT(buffer != NULL, "buffer is NULL"); 428 429 int32_t fmt = buffer->format; 430 switch (fmt) { 431 case HAL_PIXEL_FORMAT_YCbCr_420_888: 432 pixelStride = (idx == 0) ? 1 : buffer->chromaStep; 433 break; 434 case HAL_PIXEL_FORMAT_YCrCb_420_SP: 435 pixelStride = (idx == 0) ? 1 : 2; 436 break; 437 case HAL_PIXEL_FORMAT_Y8: 438 // Single plane 8bpp data. 439 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 440 pixelStride; 441 break; 442 case HAL_PIXEL_FORMAT_YV12: 443 pixelStride = 1; 444 break; 445 case HAL_PIXEL_FORMAT_BLOB: 446 // Used for JPEG data, single plane, row and pixel strides are 0 447 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 448 pixelStride = 0; 449 break; 450 case HAL_PIXEL_FORMAT_Y16: 451 case HAL_PIXEL_FORMAT_RAW_SENSOR: 452 case HAL_PIXEL_FORMAT_RGB_565: 453 // Single plane 16bpp data. 454 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 455 pixelStride = 2; 456 break; 457 case HAL_PIXEL_FORMAT_RGBA_8888: 458 case HAL_PIXEL_FORMAT_RGBX_8888: 459 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 460 pixelStride = 4; 461 break; 462 case HAL_PIXEL_FORMAT_RGB_888: 463 // Single plane, 24bpp. 464 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 465 pixelStride = 3; 466 break; 467 default: 468 jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", 469 "Pixel format: 0x%x is unsupported", fmt); 470 break; 471 } 472 473 return pixelStride; 474} 475 476static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx) 477{ 478 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 479 ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0)); 480 481 int rowStride = 0; 482 ALOG_ASSERT(buffer != NULL, "buffer is NULL"); 483 484 int32_t fmt = buffer->format; 485 486 switch (fmt) { 487 case HAL_PIXEL_FORMAT_YCbCr_420_888: 488 rowStride = (idx == 0) ? buffer->stride : buffer->chromaStride; 489 break; 490 case HAL_PIXEL_FORMAT_YCrCb_420_SP: 491 rowStride = buffer->width; 492 break; 493 case HAL_PIXEL_FORMAT_YV12: 494 LOG_ALWAYS_FATAL_IF(buffer->stride % 16, 495 "Stride is not 16 pixel aligned %d", buffer->stride); 496 rowStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16); 497 break; 498 case HAL_PIXEL_FORMAT_BLOB: 499 // Used for JPEG data, single plane, row and pixel strides are 0 500 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 501 rowStride = 0; 502 break; 503 case HAL_PIXEL_FORMAT_Y8: 504 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 505 LOG_ALWAYS_FATAL_IF(buffer->stride % 16, 506 "Stride is not 16 pixel aligned %d", buffer->stride); 507 rowStride = buffer->stride; 508 break; 509 case HAL_PIXEL_FORMAT_Y16: 510 case HAL_PIXEL_FORMAT_RAW_SENSOR: 511 // In native side, strides are specified in pixels, not in bytes. 512 // Single plane 16bpp bayer data. even width/height, 513 // row stride multiple of 16 pixels (32 bytes) 514 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 515 LOG_ALWAYS_FATAL_IF(buffer->stride % 16, 516 "Stride is not 16 pixel aligned %d", buffer->stride); 517 rowStride = buffer->stride * 2; 518 break; 519 case HAL_PIXEL_FORMAT_RGB_565: 520 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 521 rowStride = buffer->stride * 2; 522 break; 523 case HAL_PIXEL_FORMAT_RGBA_8888: 524 case HAL_PIXEL_FORMAT_RGBX_8888: 525 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 526 rowStride = buffer->stride * 4; 527 break; 528 case HAL_PIXEL_FORMAT_RGB_888: 529 // Single plane, 24bpp. 530 ALOG_ASSERT(idx == 0, "Wrong index: %d", idx); 531 rowStride = buffer->stride * 3; 532 break; 533 default: 534 ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt); 535 jniThrowException(env, "java/lang/UnsupportedOperationException", 536 "unsupported buffer format"); 537 break; 538 } 539 540 return rowStride; 541} 542 543// ---------------------------------------------------------------------------- 544 545static void ImageReader_classInit(JNIEnv* env, jclass clazz) 546{ 547 ALOGV("%s:", __FUNCTION__); 548 549 jclass imageClazz = env->FindClass("android/media/ImageReader$SurfaceImage"); 550 LOG_ALWAYS_FATAL_IF(imageClazz == NULL, 551 "can't find android/graphics/ImageReader$SurfaceImage"); 552 fields.buffer = env->GetFieldID(imageClazz, ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID, "J"); 553 LOG_ALWAYS_FATAL_IF(fields.buffer == NULL, 554 "can't find android/graphics/ImageReader.%s", 555 ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID); 556 557 fields.timeStamp = env->GetFieldID(imageClazz, ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID, "J"); 558 LOG_ALWAYS_FATAL_IF(fields.timeStamp == NULL, 559 "can't find android/graphics/ImageReader.%s", 560 ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID); 561 562 fields.imageReaderContext = env->GetFieldID(clazz, ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID, "J"); 563 LOG_ALWAYS_FATAL_IF(fields.imageReaderContext == NULL, 564 "can't find android/graphics/ImageReader.%s", 565 ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID); 566 567 fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative", 568 "(Ljava/lang/Object;)V"); 569 LOG_ALWAYS_FATAL_IF(fields.postEvent == NULL, 570 "can't find android/graphics/ImageReader.postEventFromNative"); 571 572 jclass planeClazz = env->FindClass("android/media/ImageReader$SurfaceImage$SurfacePlane"); 573 LOG_ALWAYS_FATAL_IF(planeClazz == NULL, "Can not find SurfacePlane class"); 574 // FindClass only gives a local reference of jclass object. 575 surfPlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz); 576 surfPlaneClassInfo.ctor = env->GetMethodID(surfPlaneClassInfo.clazz, "<init>", 577 "(Landroid/media/ImageReader$SurfaceImage;III)V"); 578 LOG_ALWAYS_FATAL_IF(surfPlaneClassInfo.ctor == NULL, "Can not find SurfacePlane constructor"); 579} 580 581static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, 582 jint width, jint height, jint format, jint maxImages) 583{ 584 status_t res; 585 int nativeFormat; 586 587 ALOGV("%s: width:%d, height: %d, format: 0x%x, maxImages:%d", 588 __FUNCTION__, width, height, format, maxImages); 589 590 nativeFormat = Image_getPixelFormat(env, format); 591 592 sp<BufferQueue> bq = new BufferQueue(); 593 sp<CpuConsumer> consumer = new CpuConsumer(bq, true, maxImages); 594 // TODO: throw dvm exOutOfMemoryError? 595 if (consumer == NULL) { 596 jniThrowRuntimeException(env, "Failed to allocate native CpuConsumer"); 597 return; 598 } 599 600 jclass clazz = env->GetObjectClass(thiz); 601 if (clazz == NULL) { 602 jniThrowRuntimeException(env, "Can't find android/graphics/ImageReader"); 603 return; 604 } 605 sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages)); 606 ctx->setCpuConsumer(consumer); 607 consumer->setFrameAvailableListener(ctx); 608 ImageReader_setNativeContext(env, thiz, ctx); 609 ctx->setBufferFormat(nativeFormat); 610 ctx->setBufferWidth(width); 611 ctx->setBufferHeight(height); 612 613 // Set the width/height/format to the CpuConsumer 614 res = consumer->setDefaultBufferSize(width, height); 615 if (res != OK) { 616 jniThrowException(env, "java/lang/IllegalStateException", 617 "Failed to set CpuConsumer buffer size"); 618 return; 619 } 620 res = consumer->setDefaultBufferFormat(nativeFormat); 621 if (res != OK) { 622 jniThrowException(env, "java/lang/IllegalStateException", 623 "Failed to set CpuConsumer buffer format"); 624 } 625} 626 627static void ImageReader_close(JNIEnv* env, jobject thiz) 628{ 629 ALOGV("%s:", __FUNCTION__); 630 631 JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz); 632 if (ctx == NULL) { 633 // ImageReader is already closed. 634 return; 635 } 636 637 CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz); 638 if (consumer != NULL) { 639 consumer->abandon(); 640 consumer->setFrameAvailableListener(NULL); 641 } 642 ImageReader_setNativeContext(env, thiz, NULL); 643} 644 645static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image) 646{ 647 ALOGV("%s:", __FUNCTION__); 648 JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); 649 if (ctx == NULL) { 650 ALOGW("ImageReader#close called before Image#close, consider calling Image#close first"); 651 return; 652 } 653 654 CpuConsumer* consumer = ctx->getCpuConsumer(); 655 CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, image); 656 if (!buffer) { 657 ALOGW("Image already released!!!"); 658 return; 659 } 660 consumer->unlockBuffer(*buffer); 661 Image_setBuffer(env, image, NULL); 662 ctx->returnLockedBuffer(buffer); 663} 664 665static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, 666 jobject image) 667{ 668 ALOGV("%s:", __FUNCTION__); 669 JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); 670 if (ctx == NULL) { 671 jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); 672 return false; 673 } 674 675 CpuConsumer* consumer = ctx->getCpuConsumer(); 676 CpuConsumer::LockedBuffer* buffer = ctx->getLockedBuffer(); 677 if (buffer == NULL) { 678 ALOGE("Unable to acquire a lockedBuffer, very likely client tries to lock more than" 679 "maxImages buffers"); 680 return false; 681 } 682 status_t res = consumer->lockNextBuffer(buffer); 683 if (res != NO_ERROR) { 684 ALOGE("%s Fail to lockNextBuffer with error: %d ", __FUNCTION__, res); 685 return false; 686 } 687 688 689 // Check if the left-top corner of the crop rect is origin, we currently assume this point is 690 // zero, will revist this once this assumption turns out problematic. 691 Point lt = buffer->crop.leftTop(); 692 if (lt.x != 0 || lt.y != 0) { 693 ALOGE("crop left: %d, top = %d", lt.x, lt.y); 694 jniThrowException(env, "java/lang/UnsupportedOperationException", 695 "crop left top corner need to at origin"); 696 } 697 698 // Check if the producer buffer configurations match what ImageReader configured. 699 // We want to fail for the very first image because this case is too bad. 700 int outputWidth = buffer->width; 701 int outputHeight = buffer->height; 702 703 // Correct with/height when crop is set. 704 if (buffer->crop.getWidth() > 0) { 705 outputWidth = buffer->crop.getWidth() + 1; 706 } 707 if (buffer->crop.getHeight() > 0) { 708 outputHeight = buffer->crop.getHeight() + 1; 709 } 710 711 int imageReaderWidth = ctx->getBufferWidth(); 712 int imageReaderHeight = ctx->getBufferHeight(); 713 if ((imageReaderWidth != outputWidth) || 714 (imageReaderHeight != outputHeight)) { 715 // Spew warning for now, since MediaCodec decoder has a bug to setup the right crop 716 // TODO: make it throw exception once the decoder bug is fixed. 717 ALOGW("Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", 718 outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); 719 } 720 721 if (ctx->getBufferFormat() != buffer->format) { 722 // Return the buffer to the queue. 723 consumer->unlockBuffer(*buffer); 724 ctx->returnLockedBuffer(buffer); 725 726 // Throw exception 727 ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x", 728 buffer->format, ctx->getBufferFormat()); 729 jniThrowException(env, "java/lang/UnsupportedOperationException", 730 "The producer output buffer configuration doesn't match the ImageReader" 731 "configured"); 732 return false; 733 } 734 // Set SurfaceImage instance member variables 735 Image_setBuffer(env, image, buffer); 736 env->SetLongField(image, fields.timeStamp, static_cast<jlong>(buffer->timestamp)); 737 738 return true; 739} 740 741static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) 742{ 743 ALOGV("%s: ", __FUNCTION__); 744 745 CpuConsumer* consumer = ImageReader_getCpuConsumer(env, thiz); 746 if (consumer == NULL) { 747 jniThrowRuntimeException(env, "CpuConsumer is uninitialized"); 748 return NULL; 749 } 750 751 // Wrap the IGBP in a Java-language Surface. 752 return android_view_Surface_createFromIGraphicBufferProducer(env, 753 consumer->getProducerInterface()); 754} 755 756static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx) 757{ 758 int rowStride, pixelStride; 759 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 760 761 CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); 762 763 ALOG_ASSERT(buffer != NULL); 764 if (buffer == NULL) { 765 jniThrowException(env, "java/lang/IllegalStateException", "Image was released"); 766 } 767 rowStride = Image_imageGetRowStride(env, buffer, idx); 768 pixelStride = Image_imageGetPixelStride(env, buffer, idx); 769 770 jobject surfPlaneObj = env->NewObject(surfPlaneClassInfo.clazz, surfPlaneClassInfo.ctor, 771 thiz, idx, rowStride, pixelStride); 772 773 return surfPlaneObj; 774} 775 776static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx) 777{ 778 uint8_t *base = NULL; 779 uint32_t size = 0; 780 jobject byteBuffer; 781 782 ALOGV("%s: buffer index: %d", __FUNCTION__, idx); 783 784 CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); 785 786 if (buffer == NULL) { 787 jniThrowException(env, "java/lang/IllegalStateException", "Image was released"); 788 } 789 790 // Create byteBuffer from native buffer 791 Image_getLockedBufferInfo(env, buffer, idx, &base, &size); 792 byteBuffer = env->NewDirectByteBuffer(base, size); 793 // TODO: throw dvm exOutOfMemoryError? 794 if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) { 795 jniThrowException(env, "java/lang/IllegalStateException", "Failed to allocate ByteBuffer"); 796 } 797 798 return byteBuffer; 799} 800 801} // extern "C" 802 803// ---------------------------------------------------------------------------- 804 805static JNINativeMethod gImageReaderMethods[] = { 806 {"nativeClassInit", "()V", (void*)ImageReader_classInit }, 807 {"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init }, 808 {"nativeClose", "()V", (void*)ImageReader_close }, 809 {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease }, 810 {"nativeImageSetup", "(Landroid/media/Image;)Z", (void*)ImageReader_imageSetup }, 811 {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface }, 812}; 813 814static JNINativeMethod gImageMethods[] = { 815 {"nativeImageGetBuffer", "(I)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer }, 816 {"nativeCreatePlane", "(I)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;", 817 (void*)Image_createSurfacePlane }, 818}; 819 820int register_android_media_ImageReader(JNIEnv *env) { 821 822 int ret1 = AndroidRuntime::registerNativeMethods(env, 823 "android/media/ImageReader", gImageReaderMethods, NELEM(gImageReaderMethods)); 824 825 int ret2 = AndroidRuntime::registerNativeMethods(env, 826 "android/media/ImageReader$SurfaceImage", gImageMethods, NELEM(gImageMethods)); 827 828 return (ret1 || ret2); 829} 830