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