/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "Legacy-CameraDevice-JNI" // #define LOG_NDEBUG 0 #include #include #include #include #include "jni.h" #include "JNIHelp.h" #include "core_jni_helpers.h" #include "android_runtime/android_view_Surface.h" #include "android_runtime/android_graphics_SurfaceTexture.h" #include #include #include #include #include #include #include #include using namespace android; // fully-qualified class name #define CAMERA_DEVICE_CLASS_NAME "android/hardware/camera2/legacy/LegacyCameraDevice" #define CAMERA_DEVICE_BUFFER_SLACK 3 #define DONT_CARE 0 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) ) /** * Convert from RGB 888 to Y'CbCr using the conversion specified in JFIF v1.02 */ static void rgbToYuv420(uint8_t* rgbBuf, size_t width, size_t height, uint8_t* yPlane, uint8_t* crPlane, uint8_t* cbPlane, size_t chromaStep, size_t yStride, size_t chromaStride) { uint8_t R, G, B; size_t index = 0; for (size_t j = 0; j < height; j++) { uint8_t* cr = crPlane; uint8_t* cb = cbPlane; uint8_t* y = yPlane; bool jEven = (j & 1) == 0; for (size_t i = 0; i < width; i++) { R = rgbBuf[index++]; G = rgbBuf[index++]; B = rgbBuf[index++]; *y++ = (77 * R + 150 * G + 29 * B) >> 8; if (jEven && (i & 1) == 0) { *cb = (( -43 * R - 85 * G + 128 * B) >> 8) + 128; *cr = (( 128 * R - 107 * G - 21 * B) >> 8) + 128; cr += chromaStep; cb += chromaStep; } // Skip alpha index++; } yPlane += yStride; if (jEven) { crPlane += chromaStride; cbPlane += chromaStride; } } } static void rgbToYuv420(uint8_t* rgbBuf, size_t width, size_t height, android_ycbcr* ycbcr) { size_t cStep = ycbcr->chroma_step; size_t cStride = ycbcr->cstride; size_t yStride = ycbcr->ystride; ALOGV("%s: yStride is: %zu, cStride is: %zu, cStep is: %zu", __FUNCTION__, yStride, cStride, cStep); rgbToYuv420(rgbBuf, width, height, reinterpret_cast(ycbcr->y), reinterpret_cast(ycbcr->cr), reinterpret_cast(ycbcr->cb), cStep, yStride, cStride); } static status_t configureSurface(const sp& anw, int32_t width, int32_t height, int32_t pixelFmt, int32_t maxBufferSlack) { status_t err = NO_ERROR; err = native_window_set_buffers_dimensions(anw.get(), width, height); if (err != NO_ERROR) { ALOGE("%s: Failed to set native window buffer dimensions, error %s (%d).", __FUNCTION__, strerror(-err), err); return err; } err = native_window_set_buffers_format(anw.get(), pixelFmt); if (err != NO_ERROR) { ALOGE("%s: Failed to set native window buffer format, error %s (%d).", __FUNCTION__, strerror(-err), err); return err; } err = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN); if (err != NO_ERROR) { ALOGE("%s: Failed to set native window usage flag, error %s (%d).", __FUNCTION__, strerror(-err), err); return err; } int minUndequeuedBuffers; err = anw.get()->query(anw.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers); if (err != NO_ERROR) { ALOGE("%s: Failed to get native window min undequeued buffers, error %s (%d).", __FUNCTION__, strerror(-err), err); return err; } ALOGV("%s: Setting buffer count to %d, size to (%dx%d), fmt (0x%x)", __FUNCTION__, maxBufferSlack + 1 + minUndequeuedBuffers, width, height, pixelFmt); err = native_window_set_buffer_count(anw.get(), maxBufferSlack + 1 + minUndequeuedBuffers); if (err != NO_ERROR) { ALOGE("%s: Failed to set native window buffer count, error %s (%d).", __FUNCTION__, strerror(-err), err); return err; } return NO_ERROR; } /** * Produce a frame in the given surface. * * Args: * anw - a surface to produce a frame in. * pixelBuffer - image buffer to generate a frame from. * width - width of the pixelBuffer in pixels. * height - height of the pixelBuffer in pixels. * pixelFmt - format of the pixelBuffer, one of: * HAL_PIXEL_FORMAT_YCrCb_420_SP, * HAL_PIXEL_FORMAT_YCbCr_420_888, * HAL_PIXEL_FORMAT_BLOB * bufSize - the size of the pixelBuffer in bytes. */ static status_t produceFrame(const sp& anw, uint8_t* pixelBuffer, int32_t bufWidth, // Width of the pixelBuffer int32_t bufHeight, // Height of the pixelBuffer int32_t pixelFmt, // Format of the pixelBuffer int32_t bufSize) { ATRACE_CALL(); status_t err = NO_ERROR; ANativeWindowBuffer* anb; ALOGV("%s: Dequeue buffer from %p %dx%d (fmt=%x, size=%x)", __FUNCTION__, anw.get(), bufWidth, bufHeight, pixelFmt, bufSize); if (anw == 0) { ALOGE("%s: anw must not be NULL", __FUNCTION__); return BAD_VALUE; } else if (pixelBuffer == NULL) { ALOGE("%s: pixelBuffer must not be NULL", __FUNCTION__); return BAD_VALUE; } else if (bufWidth < 0) { ALOGE("%s: width must be non-negative", __FUNCTION__); return BAD_VALUE; } else if (bufHeight < 0) { ALOGE("%s: height must be non-negative", __FUNCTION__); return BAD_VALUE; } else if (bufSize < 0) { ALOGE("%s: bufSize must be non-negative", __FUNCTION__); return BAD_VALUE; } size_t width = static_cast(bufWidth); size_t height = static_cast(bufHeight); size_t bufferLength = static_cast(bufSize); // TODO: Switch to using Surface::lock and Surface::unlockAndPost err = native_window_dequeue_buffer_and_wait(anw.get(), &anb); if (err != NO_ERROR) return err; sp buf(new GraphicBuffer(anb, /*keepOwnership*/false)); uint32_t grallocBufWidth = buf->getWidth(); uint32_t grallocBufHeight = buf->getHeight(); uint32_t grallocBufStride = buf->getStride(); if (grallocBufWidth != width || grallocBufHeight != height) { ALOGE("%s: Received gralloc buffer with bad dimensions %" PRIu32 "x%" PRIu32 ", expecting dimensions %zu x %zu", __FUNCTION__, grallocBufWidth, grallocBufHeight, width, height); return BAD_VALUE; } int32_t bufFmt = 0; err = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &bufFmt); if (err != NO_ERROR) { ALOGE("%s: Error while querying surface pixel format %s (%d).", __FUNCTION__, strerror(-err), err); return err; } uint64_t tmpSize = (pixelFmt == HAL_PIXEL_FORMAT_BLOB) ? grallocBufWidth : 4 * grallocBufHeight * grallocBufWidth; if (bufFmt != pixelFmt) { if (bufFmt == HAL_PIXEL_FORMAT_RGBA_8888 && pixelFmt == HAL_PIXEL_FORMAT_BLOB) { ALOGV("%s: Using BLOB to RGBA format override.", __FUNCTION__); tmpSize = 4 * (grallocBufWidth + grallocBufStride * (grallocBufHeight - 1)); } else { ALOGW("%s: Format mismatch in produceFrame: expecting format %#" PRIx32 ", but received buffer with format %#" PRIx32, __FUNCTION__, pixelFmt, bufFmt); } } if (tmpSize > SIZE_MAX) { ALOGE("%s: Overflow calculating size, buffer with dimens %zu x %zu is absurdly large...", __FUNCTION__, width, height); return BAD_VALUE; } size_t totalSizeBytes = tmpSize; ALOGV("%s: Pixel format chosen: %x", __FUNCTION__, pixelFmt); switch(pixelFmt) { case HAL_PIXEL_FORMAT_YCrCb_420_SP: { if (bufferLength < totalSizeBytes) { ALOGE("%s: PixelBuffer size %zu too small for given dimensions", __FUNCTION__, bufferLength); return BAD_VALUE; } uint8_t* img = NULL; ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get()); err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); if (err != NO_ERROR) return err; uint8_t* yPlane = img; uint8_t* uPlane = img + height * width; uint8_t* vPlane = uPlane + 1; size_t chromaStep = 2; size_t yStride = width; size_t chromaStride = width; rgbToYuv420(pixelBuffer, width, height, yPlane, uPlane, vPlane, chromaStep, yStride, chromaStride); break; } case HAL_PIXEL_FORMAT_YV12: { if (bufferLength < totalSizeBytes) { ALOGE("%s: PixelBuffer size %zu too small for given dimensions", __FUNCTION__, bufferLength); return BAD_VALUE; } if ((width & 1) || (height & 1)) { ALOGE("%s: Dimens %zu x %zu are not divisible by 2.", __FUNCTION__, width, height); return BAD_VALUE; } uint8_t* img = NULL; ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get()); err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); if (err != NO_ERROR) { ALOGE("%s: Error %s (%d) while locking gralloc buffer for write.", __FUNCTION__, strerror(-err), err); return err; } uint32_t stride = buf->getStride(); ALOGV("%s: stride is: %" PRIu32, __FUNCTION__, stride); LOG_ALWAYS_FATAL_IF(stride % 16, "Stride is not 16 pixel aligned %d", stride); uint32_t cStride = ALIGN(stride / 2, 16); size_t chromaStep = 1; uint8_t* yPlane = img; uint8_t* crPlane = img + static_cast(height) * stride; uint8_t* cbPlane = crPlane + cStride * static_cast(height) / 2; rgbToYuv420(pixelBuffer, width, height, yPlane, crPlane, cbPlane, chromaStep, stride, cStride); break; } case HAL_PIXEL_FORMAT_YCbCr_420_888: { // Software writes with YCbCr_420_888 format are unsupported // by the gralloc module for now if (bufferLength < totalSizeBytes) { ALOGE("%s: PixelBuffer size %zu too small for given dimensions", __FUNCTION__, bufferLength); return BAD_VALUE; } android_ycbcr ycbcr = android_ycbcr(); ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get()); err = buf->lockYCbCr(GRALLOC_USAGE_SW_WRITE_OFTEN, &ycbcr); if (err != NO_ERROR) { ALOGE("%s: Failed to lock ycbcr buffer, error %s (%d).", __FUNCTION__, strerror(-err), err); return err; } rgbToYuv420(pixelBuffer, width, height, &ycbcr); break; } case HAL_PIXEL_FORMAT_BLOB: { int8_t* img = NULL; struct camera3_jpeg_blob footer = { .jpeg_blob_id = CAMERA3_JPEG_BLOB_ID, .jpeg_size = (uint32_t)bufferLength }; size_t totalJpegSize = bufferLength + sizeof(footer); totalJpegSize = (totalJpegSize + 3) & ~0x3; // round up to nearest octonibble if (totalJpegSize > totalSizeBytes) { ALOGE("%s: Pixel buffer needs size %zu, cannot fit in gralloc buffer of size %zu", __FUNCTION__, totalJpegSize, totalSizeBytes); return BAD_VALUE; } err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); if (err != NO_ERROR) { ALOGE("%s: Failed to lock buffer, error %s (%d).", __FUNCTION__, strerror(-err), err); return err; } memcpy(img, pixelBuffer, bufferLength); memcpy(img + totalSizeBytes - sizeof(footer), &footer, sizeof(footer)); break; } default: { ALOGE("%s: Invalid pixel format in produceFrame: %x", __FUNCTION__, pixelFmt); return BAD_VALUE; } } ALOGV("%s: Unlock buffer from %p", __FUNCTION__, anw.get()); err = buf->unlock(); if (err != NO_ERROR) { ALOGE("%s: Failed to unlock buffer, error %s (%d).", __FUNCTION__, strerror(-err), err); return err; } ALOGV("%s: Queue buffer to %p", __FUNCTION__, anw.get()); err = anw->queueBuffer(anw.get(), buf->getNativeBuffer(), /*fenceFd*/-1); if (err != NO_ERROR) { ALOGE("%s: Failed to queue buffer, error %s (%d).", __FUNCTION__, strerror(-err), err); return err; } return NO_ERROR; } static sp getNativeWindow(JNIEnv* env, jobject surface) { sp anw; if (surface) { anw = android_view_Surface_getNativeWindow(env, surface); if (env->ExceptionCheck()) { return NULL; } } else { jniThrowNullPointerException(env, "surface"); return NULL; } if (anw == NULL) { ALOGE("%s: Surface had no valid native window.", __FUNCTION__); return NULL; } return anw; } static sp getNativeWindowFromTexture(JNIEnv* env, jobject surfaceTexture) { sp anw; if (surfaceTexture) { anw = android_SurfaceTexture_getNativeWindow(env, surfaceTexture); if (env->ExceptionCheck()) { return NULL; } } else { jniThrowNullPointerException(env, "surfaceTexture"); return NULL; } if (anw == NULL) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "SurfaceTexture had no valid native window."); return NULL; } return anw; } static sp getSurface(JNIEnv* env, jobject surface) { sp s; if (surface) { s = android_view_Surface_getSurface(env, surface); if (env->ExceptionCheck()) { return NULL; } } else { jniThrowNullPointerException(env, "surface"); return NULL; } if (s == NULL) { jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Surface had no valid native Surface."); return NULL; } return s; } extern "C" { static jint LegacyCameraDevice_nativeDetectSurfaceType(JNIEnv* env, jobject thiz, jobject surface) { ALOGV("nativeDetectSurfaceType"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } int32_t fmt = 0; status_t err = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &fmt); if(err != NO_ERROR) { ALOGE("%s: Error while querying surface pixel format %s (%d).", __FUNCTION__, strerror(-err), err); return err; } return fmt; } static jint LegacyCameraDevice_nativeDetectSurfaceDataspace(JNIEnv* env, jobject thiz, jobject surface) { ALOGV("nativeDetectSurfaceDataspace"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } int32_t fmt = 0; status_t err = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, &fmt); if(err != NO_ERROR) { ALOGE("%s: Error while querying surface dataspace %s (%d).", __FUNCTION__, strerror(-err), err); return err; } return fmt; } static jint LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz, jobject surface, jintArray dimens) { ALOGV("nativeGetSurfaceDimens"); if (dimens == NULL) { ALOGE("%s: Null dimens argument passed to nativeDetectSurfaceDimens", __FUNCTION__); return BAD_VALUE; } if (env->GetArrayLength(dimens) < 2) { ALOGE("%s: Invalid length of dimens argument in nativeDetectSurfaceDimens", __FUNCTION__); return BAD_VALUE; } sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } int32_t dimenBuf[2]; status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf); if(err != NO_ERROR) { ALOGE("%s: Error while querying surface width %s (%d).", __FUNCTION__, strerror(-err), err); return err; } err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1); if(err != NO_ERROR) { ALOGE("%s: Error while querying surface height %s (%d).", __FUNCTION__, strerror(-err), err); return err; } env->SetIntArrayRegion(dimens, /*start*/0, /*length*/ARRAY_SIZE(dimenBuf), dimenBuf); return NO_ERROR; } static jint LegacyCameraDevice_nativeDetectSurfaceUsageFlags(JNIEnv* env, jobject thiz, jobject surface) { ALOGV("nativeDetectSurfaceUsageFlags"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { jniThrowException(env, "Ljava/lang/UnsupportedOperationException;", "Could not retrieve native window from surface."); return BAD_VALUE; } int32_t usage = 0; status_t err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage); if(err != NO_ERROR) { jniThrowException(env, "Ljava/lang/UnsupportedOperationException;", "Error while querying surface usage bits"); return err; } return usage; } static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz, jobject surfaceTexture, jintArray dimens) { ALOGV("nativeDetectTextureDimens"); sp anw; if ((anw = getNativeWindowFromTexture(env, surfaceTexture)) == NULL) { ALOGE("%s: Could not retrieve native window from SurfaceTexture.", __FUNCTION__); return BAD_VALUE; } int32_t dimenBuf[2]; status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf); if(err != NO_ERROR) { ALOGE("%s: Error while querying SurfaceTexture width %s (%d)", __FUNCTION__, strerror(-err), err); return err; } err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1); if(err != NO_ERROR) { ALOGE("%s: Error while querying SurfaceTexture height %s (%d)", __FUNCTION__, strerror(-err), err); return err; } env->SetIntArrayRegion(dimens, /*start*/0, /*length*/ARRAY_SIZE(dimenBuf), dimenBuf); if (env->ExceptionCheck()) { return BAD_VALUE; } return NO_ERROR; } static jint LegacyCameraDevice_nativeConfigureSurface(JNIEnv* env, jobject thiz, jobject surface, jint width, jint height, jint pixelFormat) { ALOGV("nativeConfigureSurface"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } status_t err = configureSurface(anw, width, height, pixelFormat, CAMERA_DEVICE_BUFFER_SLACK); if (err != NO_ERROR) { ALOGE("%s: Error while configuring surface %s (%d).", __FUNCTION__, strerror(-err), err); return err; } return NO_ERROR; } static jint LegacyCameraDevice_nativeProduceFrame(JNIEnv* env, jobject thiz, jobject surface, jbyteArray pixelBuffer, jint width, jint height, jint pixelFormat) { ALOGV("nativeProduceFrame"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } if (pixelBuffer == NULL) { jniThrowNullPointerException(env, "pixelBuffer"); return DONT_CARE; } int32_t bufSize = static_cast(env->GetArrayLength(pixelBuffer)); jbyte* pixels = env->GetByteArrayElements(pixelBuffer, /*is_copy*/NULL); if (pixels == NULL) { jniThrowNullPointerException(env, "pixels"); return DONT_CARE; } status_t err = produceFrame(anw, reinterpret_cast(pixels), width, height, pixelFormat, bufSize); env->ReleaseByteArrayElements(pixelBuffer, pixels, JNI_ABORT); if (err != NO_ERROR) { ALOGE("%s: Error while producing frame %s (%d).", __FUNCTION__, strerror(-err), err); return err; } return NO_ERROR; } static jint LegacyCameraDevice_nativeSetSurfaceFormat(JNIEnv* env, jobject thiz, jobject surface, jint pixelFormat) { ALOGV("nativeSetSurfaceType"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } status_t err = native_window_set_buffers_format(anw.get(), pixelFormat); if (err != NO_ERROR) { ALOGE("%s: Error while setting surface format %s (%d).", __FUNCTION__, strerror(-err), err); return err; } return NO_ERROR; } static jint LegacyCameraDevice_nativeSetSurfaceDimens(JNIEnv* env, jobject thiz, jobject surface, jint width, jint height) { ALOGV("nativeSetSurfaceDimens"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } // Set user dimensions only // The producer dimensions are owned by GL status_t err = native_window_set_buffers_user_dimensions(anw.get(), width, height); if (err != NO_ERROR) { ALOGE("%s: Error while setting surface user dimens %s (%d).", __FUNCTION__, strerror(-err), err); return err; } return NO_ERROR; } static jlong LegacyCameraDevice_nativeGetSurfaceId(JNIEnv* env, jobject thiz, jobject surface) { ALOGV("nativeGetSurfaceId"); sp s; if ((s = getSurface(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native Surface from surface.", __FUNCTION__); return 0; } sp gbp = s->getIGraphicBufferProducer(); if (gbp == NULL) { ALOGE("%s: Could not retrieve IGraphicBufferProducer from surface.", __FUNCTION__); return 0; } sp b = IInterface::asBinder(gbp); if (b == NULL) { ALOGE("%s: Could not retrieve IBinder from surface.", __FUNCTION__); return 0; } /* * FIXME: Use better unique ID for surfaces than native IBinder pointer. Fix also in the camera * service (CameraDeviceClient.h). */ return reinterpret_cast(b.get()); } static jint LegacyCameraDevice_nativeSetSurfaceOrientation(JNIEnv* env, jobject thiz, jobject surface, jint facing, jint orientation) { ALOGV("nativeSetSurfaceOrientation"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } status_t err = NO_ERROR; CameraMetadata staticMetadata; int32_t orientVal = static_cast(orientation); uint8_t facingVal = static_cast(facing); staticMetadata.update(ANDROID_SENSOR_ORIENTATION, &orientVal, 1); staticMetadata.update(ANDROID_LENS_FACING, &facingVal, 1); int32_t transform = 0; if ((err = CameraUtils::getRotationTransform(staticMetadata, /*out*/&transform)) != NO_ERROR) { ALOGE("%s: Invalid rotation transform %s (%d)", __FUNCTION__, strerror(-err), err); return err; } ALOGV("%s: Setting buffer sticky transform to %d", __FUNCTION__, transform); if ((err = native_window_set_buffers_sticky_transform(anw.get(), transform)) != NO_ERROR) { ALOGE("%s: Unable to configure surface transform, error %s (%d)", __FUNCTION__, strerror(-err), err); return err; } return NO_ERROR; } static jint LegacyCameraDevice_nativeSetNextTimestamp(JNIEnv* env, jobject thiz, jobject surface, jlong timestamp) { ALOGV("nativeSetNextTimestamp"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } status_t err = NO_ERROR; if ((err = native_window_set_buffers_timestamp(anw.get(), static_cast(timestamp))) != NO_ERROR) { ALOGE("%s: Unable to set surface timestamp, error %s (%d)", __FUNCTION__, strerror(-err), err); return err; } return NO_ERROR; } static jint LegacyCameraDevice_nativeSetScalingMode(JNIEnv* env, jobject thiz, jobject surface, jint mode) { ALOGV("nativeSetScalingMode"); sp anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); return BAD_VALUE; } status_t err = NO_ERROR; if ((err = native_window_set_scaling_mode(anw.get(), static_cast(mode))) != NO_ERROR) { ALOGE("%s: Unable to set surface scaling mode, error %s (%d)", __FUNCTION__, strerror(-err), err); return err; } return NO_ERROR; } static jint LegacyCameraDevice_nativeGetJpegFooterSize(JNIEnv* env, jobject thiz) { ALOGV("nativeGetJpegFooterSize"); return static_cast(sizeof(struct camera3_jpeg_blob)); } } // extern "C" static JNINativeMethod gCameraDeviceMethods[] = { { "nativeDetectSurfaceType", "(Landroid/view/Surface;)I", (void *)LegacyCameraDevice_nativeDetectSurfaceType }, { "nativeDetectSurfaceDataspace", "(Landroid/view/Surface;)I", (void *)LegacyCameraDevice_nativeDetectSurfaceDataspace }, { "nativeDetectSurfaceDimens", "(Landroid/view/Surface;[I)I", (void *)LegacyCameraDevice_nativeDetectSurfaceDimens }, { "nativeConfigureSurface", "(Landroid/view/Surface;III)I", (void *)LegacyCameraDevice_nativeConfigureSurface }, { "nativeProduceFrame", "(Landroid/view/Surface;[BIII)I", (void *)LegacyCameraDevice_nativeProduceFrame }, { "nativeSetSurfaceFormat", "(Landroid/view/Surface;I)I", (void *)LegacyCameraDevice_nativeSetSurfaceFormat }, { "nativeSetSurfaceDimens", "(Landroid/view/Surface;II)I", (void *)LegacyCameraDevice_nativeSetSurfaceDimens }, { "nativeGetSurfaceId", "(Landroid/view/Surface;)J", (void *)LegacyCameraDevice_nativeGetSurfaceId }, { "nativeDetectTextureDimens", "(Landroid/graphics/SurfaceTexture;[I)I", (void *)LegacyCameraDevice_nativeDetectTextureDimens }, { "nativeSetSurfaceOrientation", "(Landroid/view/Surface;II)I", (void *)LegacyCameraDevice_nativeSetSurfaceOrientation }, { "nativeSetNextTimestamp", "(Landroid/view/Surface;J)I", (void *)LegacyCameraDevice_nativeSetNextTimestamp }, { "nativeGetJpegFooterSize", "()I", (void *)LegacyCameraDevice_nativeGetJpegFooterSize }, { "nativeDetectSurfaceUsageFlags", "(Landroid/view/Surface;)I", (void *)LegacyCameraDevice_nativeDetectSurfaceUsageFlags }, { "nativeSetScalingMode", "(Landroid/view/Surface;I)I", (void *)LegacyCameraDevice_nativeSetScalingMode }, }; // Get all the required offsets in java class and register native functions int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv* env) { // Register native functions return RegisterMethodsOrDie(env, CAMERA_DEVICE_CLASS_NAME, gCameraDeviceMethods, NELEM(gCameraDeviceMethods)); }