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