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