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