android_hardware_camera2_legacy_LegacyCameraDevice.cpp revision 91b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0
1/*
2 * Copyright (C) 2014 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_TAG "Legacy-CameraDevice-JNI"
18#include <utils/Log.h>
19#include <utils/Errors.h>
20#include <utils/Trace.h>
21
22#include "jni.h"
23#include "JNIHelp.h"
24#include "android_runtime/AndroidRuntime.h"
25#include "android_runtime/android_view_Surface.h"
26
27#include <ui/GraphicBuffer.h>
28#include <system/window.h>
29
30using namespace android;
31
32// fully-qualified class name
33#define CAMERA_DEVICE_CLASS_NAME "android/hardware/camera2/legacy/LegacyCameraDevice"
34#define CAMERA_DEVICE_BUFFER_SLACK  3
35
36#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
37
38#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
39
40/**
41 * Convert from RGB 888 to Y'CbCr using the conversion specified in ITU-R BT.601 for
42 * digital RGB with K_b = 0.114, and K_r = 0.299.
43 */
44static void rgbToYuv420(uint8_t* rgbBuf, int32_t width, int32_t height, uint8_t* yPlane,
45        uint8_t* uPlane, uint8_t* vPlane, size_t chromaStep, size_t yStride, size_t chromaStride) {
46    uint8_t R, G, B;
47    size_t index = 0;
48
49    int32_t cStrideDiff = chromaStride - width;
50
51    for (int32_t j = 0; j < height; j++) {
52        for (int32_t i = 0; i < width; i++) {
53            R = rgbBuf[index++];
54            G = rgbBuf[index++];
55            B = rgbBuf[index++];
56            *(yPlane + i) = ((66 * R + 129 * G +  25 * B + 128) >> 8) +  16;
57
58            if (j % 2 == 0 && i % 2 == 0){
59                *uPlane = (( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128;
60                *vPlane = (( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128;
61                uPlane += chromaStep;
62                vPlane += chromaStep;
63            }
64            // Skip alpha
65            index++;
66        }
67        yPlane += yStride;
68        if (j % 2 == 0) {
69            uPlane += cStrideDiff;
70            vPlane += cStrideDiff;
71        }
72    }
73}
74
75static void rgbToYuv420(uint8_t* rgbBuf, int32_t width, int32_t height, android_ycbcr* ycbcr) {
76    size_t cStep = ycbcr->chroma_step;
77    size_t cStride = ycbcr->cstride;
78    size_t yStride = ycbcr->ystride;
79    rgbToYuv420(rgbBuf, width, height, reinterpret_cast<uint8_t*>(ycbcr->y),
80            reinterpret_cast<uint8_t*>(ycbcr->cb), reinterpret_cast<uint8_t*>(ycbcr->cr),
81            cStep, yStride, cStride);
82}
83
84static status_t configureSurface(const sp<ANativeWindow>& anw,
85                                 int32_t width,
86                                 int32_t height,
87                                 int32_t pixelFmt,
88                                 int32_t maxBufferSlack) {
89    status_t err = NO_ERROR;
90    err = native_window_set_buffers_dimensions(anw.get(), width, height);
91    if (err != NO_ERROR) {
92        ALOGE("%s: Failed to set native window buffer dimensions, error %s (%d).", __FUNCTION__,
93                strerror(-err), err);
94        return err;
95    }
96
97    err = native_window_set_buffers_format(anw.get(), pixelFmt);
98    if (err != NO_ERROR) {
99        ALOGE("%s: Failed to set native window buffer format, error %s (%d).", __FUNCTION__,
100                strerror(-err), err);
101        return err;
102    }
103
104    err = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
105    if (err != NO_ERROR) {
106        ALOGE("%s: Failed to set native window usage flag, error %s (%d).", __FUNCTION__,
107                strerror(-err), err);
108        return err;
109    }
110
111    int minUndequeuedBuffers;
112    err = anw.get()->query(anw.get(),
113            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
114            &minUndequeuedBuffers);
115    if (err != NO_ERROR) {
116        ALOGE("%s: Failed to get native window min undequeued buffers, error %s (%d).",
117                __FUNCTION__, strerror(-err), err);
118        return err;
119    }
120
121    ALOGV("%s: Setting buffer count to %d", __FUNCTION__,
122          maxBufferSlack + 1 + minUndequeuedBuffers);
123    err = native_window_set_buffer_count(anw.get(), maxBufferSlack + 1 + minUndequeuedBuffers);
124    if (err != NO_ERROR) {
125        ALOGE("%s: Failed to set native window buffer count, error %s (%d).", __FUNCTION__,
126                strerror(-err), err);
127        return err;
128    }
129    return NO_ERROR;
130}
131
132/**
133 * Produce a frame in the given surface.
134 *
135 * Args:
136 *    anw - a surface to produce a frame in.
137 *    pixelBuffer - image buffer to generate a frame from.
138 *    width - width of the pixelBuffer in pixels.
139 *    height - height of the pixelBuffer in pixels.
140 *    pixelFmt - format of the pixelBuffer, one of:
141 *               HAL_PIXEL_FORMAT_YCrCb_420_SP,
142 *               HAL_PIXEL_FORMAT_YCbCr_420_888,
143 *               HAL_PIXEL_FORMAT_BLOB
144 *    bufSize - the size of the pixelBuffer in bytes.
145 */
146static status_t produceFrame(const sp<ANativeWindow>& anw,
147                             uint8_t* pixelBuffer,
148                             int32_t width, // Width of the pixelBuffer
149                             int32_t height, // Height of the pixelBuffer
150                             int32_t pixelFmt, // Format of the pixelBuffer
151                             int64_t bufSize) {
152    ATRACE_CALL();
153    status_t err = NO_ERROR;
154    ANativeWindowBuffer* anb;
155    ALOGV("%s: Dequeue buffer from %p",__FUNCTION__, anw.get());
156
157    if (width < 0 || height < 0 || bufSize < 0) {
158        ALOGE("%s: Illegal argument, negative dimension passed to produceFrame", __FUNCTION__);
159        return BAD_VALUE;
160    }
161
162    // TODO: Switch to using Surface::lock and Surface::unlockAndPost
163    err = native_window_dequeue_buffer_and_wait(anw.get(), &anb);
164    if (err != NO_ERROR) return err;
165
166    sp<GraphicBuffer> buf(new GraphicBuffer(anb, /*keepOwnership*/false));
167
168    switch(pixelFmt) {
169        case HAL_PIXEL_FORMAT_YCrCb_420_SP: {
170            if (bufSize < width * height * 4) {
171                ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
172                        bufSize);
173                return BAD_VALUE;
174            }
175            uint8_t* img = NULL;
176            ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
177            err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
178            if (err != NO_ERROR) return err;
179
180            uint8_t* yPlane = img;
181            uint8_t* uPlane = img + height * width;
182            uint8_t* vPlane = uPlane + 1;
183            size_t chromaStep = 2;
184            size_t yStride = width;
185            size_t chromaStride = width;
186
187            rgbToYuv420(pixelBuffer, width, height, yPlane,
188                    uPlane, vPlane, chromaStep, yStride, chromaStride);
189            break;
190        }
191        case HAL_PIXEL_FORMAT_YV12: {
192            if (bufSize < width * height * 4) {
193                ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
194                        bufSize);
195                return BAD_VALUE;
196            }
197
198            if ((width & 1) || (height & 1)) {
199                ALOGE("%s: Dimens %dx%d are not divisible by 2.", __FUNCTION__, width, height);
200                return BAD_VALUE;
201            }
202
203            uint8_t* img = NULL;
204            ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
205            err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
206            if (err != NO_ERROR) {
207                ALOGE("%s: Error %s (%d) while locking gralloc buffer for write.", __FUNCTION__,
208                        strerror(-err), err);
209                return err;
210            }
211
212            uint32_t stride = buf->getStride();
213            LOG_ALWAYS_FATAL_IF(stride % 16, "Stride is not 16 pixel aligned %d", stride);
214
215            uint32_t cStride = ALIGN(stride / 2, 16);
216            size_t chromaStep = 1;
217
218            uint8_t* yPlane = img;
219            uint8_t* crPlane = img + static_cast<uint32_t>(height) * stride;
220            uint8_t* cbPlane = crPlane + cStride * static_cast<uint32_t>(height) / 2;
221
222            rgbToYuv420(pixelBuffer, width, height, yPlane,
223                    crPlane, cbPlane, chromaStep, stride, cStride);
224            break;
225        }
226        case HAL_PIXEL_FORMAT_YCbCr_420_888: {
227            // Software writes with YCbCr_420_888 format are unsupported
228            // by the gralloc module for now
229            if (bufSize < width * height * 4) {
230                ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
231                      bufSize);
232                return BAD_VALUE;
233            }
234            android_ycbcr ycbcr = android_ycbcr();
235            ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
236
237            err = buf->lockYCbCr(GRALLOC_USAGE_SW_WRITE_OFTEN, &ycbcr);
238            if (err != NO_ERROR) {
239                ALOGE("%s: Failed to lock ycbcr buffer, error %s (%d).", __FUNCTION__,
240                        strerror(-err), err);
241                return err;
242            }
243            rgbToYuv420(pixelBuffer, width, height, &ycbcr);
244            break;
245        }
246        case HAL_PIXEL_FORMAT_BLOB: {
247            if (bufSize != width || height != 1) {
248                ALOGE("%s: Incorrect pixelBuffer size: %lld", __FUNCTION__, bufSize);
249                return BAD_VALUE;
250            }
251            int8_t* img = NULL;
252
253            ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
254            err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
255            if (err != NO_ERROR) {
256                ALOGE("%s: Failed to lock buffer, error %s (%d).", __FUNCTION__, strerror(-err),
257                        err);
258                return err;
259            }
260            memcpy(img, pixelBuffer, width);
261            break;
262        }
263        default: {
264            ALOGE("%s: Invalid pixel format in produceFrame: %x", __FUNCTION__, pixelFmt);
265            return BAD_VALUE;
266        }
267    }
268
269    ALOGV("%s: Unlock buffer from %p", __FUNCTION__, anw.get());
270    err = buf->unlock();
271    if (err != NO_ERROR) {
272        ALOGE("%s: Failed to unlock buffer, error %s (%d).", __FUNCTION__, strerror(-err), err);
273        return err;
274    }
275
276    ALOGV("%s: Queue buffer to %p", __FUNCTION__, anw.get());
277    err = anw->queueBuffer(anw.get(), buf->getNativeBuffer(), /*fenceFd*/-1);
278    if (err != NO_ERROR) {
279        ALOGE("%s: Failed to queue buffer, error %s (%d).", __FUNCTION__, strerror(-err), err);
280        return err;
281    }
282    return NO_ERROR;
283}
284
285static sp<ANativeWindow> getNativeWindow(JNIEnv* env, jobject surface) {
286    sp<ANativeWindow> anw;
287    if (surface) {
288        anw = android_view_Surface_getNativeWindow(env, surface);
289        if (env->ExceptionCheck()) {
290            return anw;
291        }
292    } else {
293        jniThrowNullPointerException(env, "surface");
294        return anw;
295    }
296    if (anw == NULL) {
297        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
298                "Surface had no valid native window.");
299        return anw;
300    }
301    return anw;
302}
303
304extern "C" {
305
306static jint LegacyCameraDevice_nativeDetectSurfaceType(JNIEnv* env, jobject thiz, jobject surface) {
307    ALOGV("nativeDetectSurfaceType");
308    sp<ANativeWindow> anw;
309    if ((anw = getNativeWindow(env, surface)) == NULL) {
310        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
311        return 0;
312    }
313    int32_t fmt = 0;
314    status_t err = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &fmt);
315    if(err != NO_ERROR) {
316        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
317                                "Error while querying surface pixel format (error code %d)", err);
318        return 0;
319    }
320    return fmt;
321}
322
323static void LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz,
324          jobject surface, jintArray dimens) {
325    ALOGV("nativeGetSurfaceDimens");
326    sp<ANativeWindow> anw;
327    if ((anw = getNativeWindow(env, surface)) == NULL) {
328        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
329        return;
330    }
331    int32_t dimenBuf[2];
332    status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf);
333    if(err != NO_ERROR) {
334        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
335                        "Error while querying surface width (error code %d)", err);
336        return;
337    }
338    err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1);
339    if(err != NO_ERROR) {
340        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
341                "Error while querying surface height (error code %d)", err);
342        return;
343    }
344    env->SetIntArrayRegion(dimens, /*start*/0, /*length*/ARRAY_SIZE(dimenBuf), dimenBuf);
345}
346
347static void LegacyCameraDevice_nativeConfigureSurface(JNIEnv* env, jobject thiz, jobject surface,
348        jint width, jint height, jint pixelFormat) {
349    ALOGV("nativeConfigureSurface");
350    sp<ANativeWindow> anw;
351    if ((anw = getNativeWindow(env, surface)) == NULL) {
352        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
353        return;
354    }
355    status_t err = configureSurface(anw, width, height, pixelFormat, CAMERA_DEVICE_BUFFER_SLACK);
356    if (err != NO_ERROR) {
357        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
358                "Error while producing frame (error code %d)", err);
359        return;
360    }
361}
362
363static void LegacyCameraDevice_nativeProduceFrame(JNIEnv* env, jobject thiz, jobject surface,
364        jbyteArray pixelBuffer, jint width, jint height, jint pixelFormat) {
365    ALOGV("nativeProduceFrame");
366    sp<ANativeWindow> anw;
367
368    if ((anw = getNativeWindow(env, surface)) == NULL) {
369        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
370        return;
371    }
372
373    if (pixelBuffer == NULL) {
374        jniThrowNullPointerException(env, "pixelBuffer");
375        return;
376    }
377
378    int32_t bufSize = static_cast<int32_t>(env->GetArrayLength(pixelBuffer));
379    jbyte* pixels = env->GetByteArrayElements(pixelBuffer, /*is_copy*/NULL);
380
381    if (pixels == NULL) {
382        jniThrowNullPointerException(env, "pixels");
383        return;
384    }
385
386    status_t err = produceFrame(anw, reinterpret_cast<uint8_t*>(pixels), width, height,
387            pixelFormat, bufSize);
388    env->ReleaseByteArrayElements(pixelBuffer, pixels, JNI_ABORT);
389
390    if (err != NO_ERROR) {
391        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
392                "Error while producing frame (error code %d)", err);
393        return;
394    }
395}
396
397static void LegacyCameraDevice_nativeSetSurfaceFormat(JNIEnv* env, jobject thiz, jobject surface,
398        jint pixelFormat) {
399    ALOGV("nativeSetSurfaceType");
400    sp<ANativeWindow> anw;
401    if ((anw = getNativeWindow(env, surface)) == NULL) {
402        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
403        return;
404    }
405    status_t err = native_window_set_buffers_format(anw.get(), pixelFormat);
406    if (err != NO_ERROR) {
407        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
408                "Error while setting surface format (error code %d)", err);
409        return;
410    }
411}
412
413static void LegacyCameraDevice_nativeSetSurfaceDimens(JNIEnv* env, jobject thiz, jobject surface,
414        jint width, jint height) {
415    ALOGV("nativeSetSurfaceDimens");
416    sp<ANativeWindow> anw;
417    if ((anw = getNativeWindow(env, surface)) == NULL) {
418        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
419        return;
420    }
421    status_t err = native_window_set_buffers_dimensions(anw.get(), width, height);
422    if (err != NO_ERROR) {
423        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
424                "Error while setting surface format (error code %d)", err);
425        return;
426    }
427}
428
429} // extern "C"
430
431static JNINativeMethod gCameraDeviceMethods[] = {
432    { "nativeDetectSurfaceType",
433    "(Landroid/view/Surface;)I",
434    (void *)LegacyCameraDevice_nativeDetectSurfaceType },
435    { "nativeDetectSurfaceDimens",
436    "(Landroid/view/Surface;[I)V",
437    (void *)LegacyCameraDevice_nativeDetectSurfaceDimens },
438    { "nativeConfigureSurface",
439    "(Landroid/view/Surface;III)V",
440    (void *)LegacyCameraDevice_nativeConfigureSurface },
441    { "nativeProduceFrame",
442    "(Landroid/view/Surface;[BIII)V",
443    (void *)LegacyCameraDevice_nativeProduceFrame },
444    { "nativeSetSurfaceFormat",
445    "(Landroid/view/Surface;I)V",
446    (void *)LegacyCameraDevice_nativeSetSurfaceFormat },
447    { "nativeSetSurfaceDimens",
448    "(Landroid/view/Surface;II)V",
449    (void *)LegacyCameraDevice_nativeSetSurfaceDimens },
450};
451
452// Get all the required offsets in java class and register native functions
453int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv* env)
454{
455    // Register native functions
456    return AndroidRuntime::registerNativeMethods(env,
457            CAMERA_DEVICE_CLASS_NAME,
458            gCameraDeviceMethods,
459            NELEM(gCameraDeviceMethods));
460}
461
462