diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4c4f74f6..7f421715 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,6 +29,8 @@ + + w) { + // make it landscape + w_ = h; + h_ = w; + portrait_ = true; + } + } + DisplayDimension(const DisplayDimension& other) { + w_ = other.w_; + h_ = other.h_; + portrait_ = other.portrait_; + } + + DisplayDimension(void) { + w_ = 0; + h_ = 0; + portrait_ = false; + } + DisplayDimension& operator=(const DisplayDimension& other) { + w_ = other.w_; + h_ = other.h_; + portrait_ = other.portrait_; + + return (*this); + } + + bool IsSameRatio(DisplayDimension& other) { + return (w_ * other.h_ == h_ * other.w_); + } + bool operator>(DisplayDimension& other) { + return (w_ >= other.w_ & h_ >= other.h_); + } + bool operator==(DisplayDimension& other) { + return (w_ == other.w_ && h_ == other.h_ && portrait_ == other.portrait_); + } + DisplayDimension operator-(DisplayDimension& other) { + DisplayDimension delta(w_ - other.w_, h_ - other.h_); + return delta; + } + void Flip(void) { portrait_ = !portrait_; } + bool IsPortrait(void) { return portrait_; } + int32_t width(void) { return w_; } + int32_t height(void) { return h_; } + int32_t org_width(void) { return (portrait_ ? h_ : w_); } + int32_t org_height(void) { return (portrait_ ? w_ : h_); } + +private: + int32_t w_, h_; + bool portrait_; +}; + + +#endif /* __CAMERA2_HELPER_H__ */ diff --git a/app/src/main/cpp/MicroPhoto.cpp b/app/src/main/cpp/MicroPhoto.cpp index 2d471efc..565db9dd 100644 --- a/app/src/main/cpp/MicroPhoto.cpp +++ b/app/src/main/cpp/MicroPhoto.cpp @@ -32,6 +32,7 @@ bool GetJniEnv(JavaVM *vm, JNIEnv **env) { // Unsupported JNI version. Throw an exception if you want to. } + return did_attach_thread; } @@ -118,26 +119,21 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_init( } extern "C" JNIEXPORT jboolean JNICALL -Java_com_xinyingpower_microphoto_MicroPhotoService_takePhoto( +Java_com_xinyingpower_microphoto_MicroPhotoService_notifyToTakePhoto( JNIEnv* env, - jobject pThis, jint channel, jint preset, jstring path, jstring fileName, jboolean sendToCma) { + jobject pThis, jint channel, jint preset, jlong scheduleTime, jstring path, jstring fileName, jboolean sendToCma) { - CTerminal* pTerminal = CTermClient::GetService().GetTerminal(); - if (pTerminal == NULL) + if (channel < 1 || channel > 0xFF) { return JNI_FALSE; } - - if (channel < 1 || channel > 0xFF) + CTerminal* pTerminal = CTermClient::GetService().GetTerminal(); + if (pTerminal == NULL) { return JNI_FALSE; } - unsigned char id = (unsigned char)channel - 1; - // env->ReleaseStringUTFChars(fileName, fileNameStr); - // env->ReleaseStringUTFChars(path, pathStr); - - pTerminal->TakeAndSendPhoto(channel, preset, 0); + pTerminal->NotifyToTakePhoto((unsigned int)channel, (unsigned int)preset, 0, (unsigned long)scheduleTime); return JNI_TRUE; } diff --git a/app/src/main/cpp/PhoneDevice.cpp b/app/src/main/cpp/PhoneDevice.cpp index 73b14c6f..2a711d61 100644 --- a/app/src/main/cpp/PhoneDevice.cpp +++ b/app/src/main/cpp/PhoneDevice.cpp @@ -87,6 +87,7 @@ CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service) mRegisterTimerMid = env->GetMethodID(classService, "registerTimer", "(JI)Z"); mRegisterHeartbeatMid = env->GetMethodID(classService, "registerHeartbeatTimer", "(I)V"); mUnregisterTimerMid = env->GetMethodID(classService, "unregisterTimer", "(J)Z"); + mUpdateTimeMid = env->GetMethodID(classService, "updateTime", "(J)Z"); env->DeleteLocalRef(classService); @@ -116,6 +117,21 @@ void CPhoneDevice::SetListener(IListener* listener) m_listener = listener; } +bool CPhoneDevice::UpdateTime(time_t ts) +{ + JNIEnv* env = NULL; + jboolean ret = JNI_FALSE; + bool attached = GetJniEnv(m_vm, &env); + if (attached) + { + jlong timeInMillis = ((jlong)ts) * 1000; + ret = env->CallBooleanMethod(m_javaService, mUpdateTimeMid, timeInMillis); + m_vm->DetachCurrentThread(); + } + + return (ret == JNI_TRUE); +} + bool CPhoneDevice::Reboot() { return false; @@ -214,6 +230,8 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const string& mPhotoInfo = photoInfo; mPath = path; + mDisplayDimension = DisplayDimension(photoInfo.width, photoInfo.height); + LOGE("Image Buffer Size: %d", photoInfo.width * photoInfo.height * 4); imageBuffer_ = (uint8_t*)malloc(photoInfo.width * photoInfo.height * 4); ASSERT(imageBuffer_ != nullptr, "Failed to allocate imageBuffer_"); @@ -258,6 +276,27 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const string& LOGI("Failed to get camera meta data of ID:%s\n", selectedCameraId); } + ACameraMetadata_const_entry face, orientation; + camera_status = ACameraMetadata_getConstEntry(cameraMetadata, ACAMERA_LENS_FACING, &face); + uint32_t cameraFacing_ = static_cast(face.data.u8[0]); + + if (cameraFacing_ == ACAMERA_LENS_FACING_FRONT) + { + int aa = 0; + } + + camera_status = ACameraMetadata_getConstEntry(cameraMetadata, ACAMERA_SENSOR_ORIENTATION, &orientation); + + LOGI("====Current SENSOR_ORIENTATION: %8d", orientation.data.i32[0]); + uint32_t cameraOrientation_ = orientation.data.i32[0]; + if (cameraOrientation_ == 90 || cameraOrientation_ == 270) + { + mDisplayDimension.Flip(); + } + + ImageFormat resCap = {(int32_t)photoInfo.width, (int32_t)photoInfo.height, AIMAGE_FORMAT_YUV_420_888}; + MatchCaptureSizeRequest(cameraManager, selectedCameraId, photoInfo.width, photoInfo.height, cameraOrientation_, &resCap); + deviceStateCallbacks.onDisconnected = camera_device_on_disconnected; deviceStateCallbacks.onError = camera_device_on_error; @@ -287,7 +326,7 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const string& media_status_t status; // status = AImageReader_new(1920, 1080, AIMAGE_FORMAT_YUV_420_888, 5, &mAImageReader); - status = AImageReader_new(photoInfo.width, photoInfo.height, AIMAGE_FORMAT_YUV_420_888/*AIMAGE_FORMAT_JPEG*/, 5, &mAImageReader); + status = AImageReader_new(resCap.width, resCap.height, resCap.format, 5, &mAImageReader); if (status != AMEDIA_OK) { LOGI("AImageReader_new error\n"); @@ -311,6 +350,7 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const string& } LOGI("Surface is prepared in %p.\n", theNativeWindow); + // theNativeWindow ACameraOutputTarget_create(theNativeWindow, &cameraOutputTarget); ACaptureRequest_addTarget(captureRequest, cameraOutputTarget); @@ -347,11 +387,21 @@ void CPhoneDevice::ImageCallback(AImageReader *reader) media_status_t status = AImageReader_acquireNextImage(reader, &image); if (status == AMEDIA_OK && image) { + int32_t srcFormat = -1; + AImage_getFormat(image, &srcFormat); + ASSERT(AIMAGE_FORMAT_YUV_420_888 == srcFormat, "Failed to get format"); + int32_t srcPlanes = 0; + AImage_getNumberOfPlanes(image, &srcPlanes); + ASSERT(srcPlanes == 3, "Is not 3 planes"); + AImageCropRect srcRect; AImage_getCropRect(image, &srcRect); int32_t width = srcRect.right - srcRect.left; int32_t height = srcRect.bottom - srcRect.top; + // int32_t height = srcRect.right - srcRect.left; + // int32_t width = srcRect.bottom - srcRect.top; + uint8_t *yPixel = nullptr; uint8_t *uPixel = nullptr; uint8_t *vPixel = nullptr; @@ -360,8 +410,6 @@ void CPhoneDevice::ImageCallback(AImageReader *reader) int32_t uLen = 0; int32_t vLen = 0; - cv::Mat _yuv_rgb_img, _yuv_gray_img; - AImage_getPlaneData(image, 0, &yPixel, &yLen); AImage_getPlaneData(image, 1, &uPixel, &uLen); AImage_getPlaneData(image, 2, &vPixel, &vLen); @@ -371,15 +419,20 @@ void CPhoneDevice::ImageCallback(AImageReader *reader) memcpy(data+yLen, vPixel, vLen); memcpy(data+yLen+vLen, uPixel, uLen); - cv::Mat mYUV = cv::Mat(height * 1.5, width, CV_8UC1, data); + cv::Mat mYUV = cv::Mat(((height * 3) >> 1), width, CV_8UC1, data); - cv::cvtColor(mYUV, _yuv_rgb_img, cv::COLOR_YUV2RGB_NV21, 3); + // cv::cvtColor(mYUV, _yuv_rgb_img, cv::COLOR_YUV2RGB_NV21, 3); // cv::Mat mYUV = cv::Mat(height, yStride, CV_8UC4, data); + cv::Mat _yuv_rgb_img(height, width, CV_8UC4), _yuv_gray_img; cv::cvtColor(mYUV, _yuv_rgb_img, cv::COLOR_YUV2RGB_NV21, 3); - cv::rotate(_yuv_rgb_img, _yuv_rgb_img, cv::ROTATE_90_CLOCKWISE); + cv::rotate(_yuv_rgb_img, _yuv_rgb_img, cv::ROTATE_180); + + // cv::Mat rgbMat(height, width, CV_8UC3); + // 通过cv::cvtColor将yuv420转换为rgb格式 + // cvtColor(_yuv_rgb_img, rgbMat, cv::COLOR_YUV2RGB_I420); // cv::Mat mat = cv::Mat(buffer.height, buffer.stride, CV_8UC4, buffer.bits); @@ -389,7 +442,7 @@ void CPhoneDevice::ImageCallback(AImageReader *reader) vector compression_params; compression_params.push_back(cv::IMWRITE_JPEG_QUALITY); - compression_params.push_back(75); + compression_params.push_back(80); res = cv::imwrite(mPath.c_str(), _yuv_rgb_img, compression_params); @@ -400,7 +453,7 @@ void CPhoneDevice::ImageCallback(AImageReader *reader) int aa = 0; } - // bool res = WriteFile(image); + // res = WriteFile(image, GetFileName() + ".org.jpg"); AImage_delete(image); // delete pThis; @@ -417,7 +470,7 @@ void CPhoneDevice::OnImageCallback(void *ctx, AImageReader *reader) } -bool CPhoneDevice::WriteFile(AImage *image) +bool CPhoneDevice::WriteFile(AImage *image, const string& path) { int planeCount = 0; media_status_t status = AImage_getNumberOfPlanes(image, &planeCount); @@ -433,8 +486,6 @@ bool CPhoneDevice::WriteFile(AImage *image) int len = 0; AImage_getPlaneData(image, 0, &data, &len); - std::string path = GetFileName(); - bool res = false; FILE *file = fopen(path.c_str(), "wb"); if (file && data && len) @@ -458,13 +509,69 @@ bool CPhoneDevice::WriteFile(AImage *image) bool CPhoneDevice::WriteFile(CPhoneDevice* pThis, AImage *image) { - return pThis->WriteFile(image); + return pThis->WriteFile(image, pThis->GetFileName()); } std::string CPhoneDevice::GetFileName() const { return mPath; } +/* +const char *selectedCameraId = NULL; + +ACameraManager *cameraManager = ACameraManager_create(); +*/ + +bool CPhoneDevice::MatchCaptureSizeRequest(ACameraManager *cameraManager, const char *selectedCameraId, unsigned int width, unsigned int height, uint32_t cameraOrientation_, + ImageFormat* resCap) { + DisplayDimension disp(resCap->width,resCap->height); + if (cameraOrientation_ == 90 || cameraOrientation_ == 270) { + disp.Flip(); + } + + ACameraMetadata* metadata; + camera_status_t camera_status = ACAMERA_OK; + camera_status = ACameraManager_getCameraCharacteristics(cameraManager, selectedCameraId, &metadata); + ACameraMetadata_const_entry entry; + camera_status = ACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry); + // format of the data: format, width, height, input?, type int32 + bool foundIt = false; + DisplayDimension foundRes(16384, 16384); + DisplayDimension maxJPG(0, 0); + + for (int i = 0; i < entry.count; i += 4) { + int32_t input = entry.data.i32[i + 3]; + int32_t format = entry.data.i32[i + 0]; + if (input) continue; + + if (format == AIMAGE_FORMAT_YUV_420_888 || format == AIMAGE_FORMAT_JPEG) { + DisplayDimension res(entry.data.i32[i + 1], entry.data.i32[i + 2]); + LOGI("Camera Resolution: %d x %d fmt=%d", res.width(), res.height(), format); + if (!disp.IsSameRatio(res)) continue; + if (format == AIMAGE_FORMAT_YUV_420_888 && res > disp) { + foundIt = true; + foundRes = res; + } else if (format == AIMAGE_FORMAT_JPEG && res > maxJPG) { + maxJPG = res; + } + } + } + + if (foundIt) { + // resView->width = foundRes.org_width(); + // resView->height = foundRes.org_height(); + resCap->width = foundRes.org_width(); + resCap->height = foundRes.org_height(); + } else { + LOGI("Did not find any compatible camera resolution, taking 640x480"); + resCap->width = disp.org_width(); + resCap->height = disp.org_height(); + // *resCap = *resView; + } + // resView->format = AIMAGE_FORMAT_YUV_420_888; + // resCap->format = AIMAGE_FORMAT_JPEG; + return foundIt; +} /** * Convert yuv image inside AImage into ANativeWindow_Buffer @@ -533,6 +640,9 @@ void CPhoneDevice::PresentImage(ANativeWindow_Buffer *buf, AImage *image) { AImage_getPlaneData(image, 2, &uPixel, &uLen); AImage_getPlanePixelStride(image, 1, &uvPixelStride); + int32_t rowStride; + AImage_getPlaneRowStride(image, 0, &rowStride); + int32_t height = std::min(buf->height, (srcRect.bottom - srcRect.top)); int32_t width = std::min(buf->width, (srcRect.right - srcRect.left)); diff --git a/app/src/main/cpp/PhoneDevice.h b/app/src/main/cpp/PhoneDevice.h index 3fccf9a8..13d735ac 100644 --- a/app/src/main/cpp/PhoneDevice.h +++ b/app/src/main/cpp/PhoneDevice.h @@ -25,6 +25,8 @@ #include #include +#include "Camera2Helper.h" + class CPhoneDevice : public IDevice { public: @@ -32,6 +34,7 @@ public: virtual ~CPhoneDevice(); virtual void SetListener(IListener* listener); + virtual bool UpdateTime(time_t ts); virtual bool Reboot(); virtual timer_uid_t RegisterHeartbeat(unsigned int timerType, unsigned int timeout); virtual bool TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const string& path); @@ -45,7 +48,8 @@ protected: std::string GetFileName() const; bool SendBroadcastMessage(std::string action, int value); - + bool MatchCaptureSizeRequest(ACameraManager *cameraManager, const char *selectedCameraId, unsigned int width, unsigned int height, uint32_t cameraOrientation_, + ImageFormat* resCap); bool DisplayImage(ANativeWindow_Buffer* buf, AImage* image); void PresentImage(ANativeWindow_Buffer* buf, AImage* image); @@ -61,7 +65,7 @@ protected: void ImageCallback(AImageReader *reader); static void OnImageCallback(void *ctx, AImageReader *reader); - bool WriteFile(AImage *image); + bool WriteFile(AImage *image, const string& path); static bool WriteFile(CPhoneDevice* pThis, AImage *image); inline bool TakePhotoCb(bool res, const IDevice::PHOTO_INFO& photoInfo, const string& path, time_t photoTime) @@ -84,6 +88,7 @@ protected: jmethodID mRegisterTimerMid; jmethodID mRegisterHeartbeatMid; jmethodID mUnregisterTimerMid; + jmethodID mUpdateTimeMid; std::string mPath; IDevice::PHOTO_INFO mPhotoInfo; @@ -104,6 +109,7 @@ protected: ACameraDevice_StateCallbacks deviceStateCallbacks; ACameraCaptureSession_stateCallbacks captureSessionStateCallbacks; + DisplayDimension mDisplayDimension; int32_t presentRotation_; int32_t imageHeight_; diff --git a/app/src/main/cpp/camera2/ndkcamera.cpp b/app/src/main/cpp/camera2/ndkcamera.cpp new file mode 100644 index 00000000..17a54aae --- /dev/null +++ b/app/src/main/cpp/camera2/ndkcamera.cpp @@ -0,0 +1,418 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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. + +#include "ndkcamera.h" + +#include + +#include + +#include + +#include "mat.h" + +static void onDisconnected(void* context, ACameraDevice* device) +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onDisconnected %p", device); +} + +static void onError(void* context, ACameraDevice* device, int error) +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onError %p %d", device, error); +} + +static void onImageAvailable(void* context, AImageReader* reader) +{ +// __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onImageAvailable %p", reader); + + AImage* image = 0; + media_status_t status = AImageReader_acquireLatestImage(reader, &image); + + if (status != AMEDIA_OK) + { + // error + return; + } + + int32_t format; + AImage_getFormat(image, &format); + + // assert format == AIMAGE_FORMAT_YUV_420_888 + + int32_t width = 0; + int32_t height = 0; + AImage_getWidth(image, &width); + AImage_getHeight(image, &height); + + int32_t y_pixelStride = 0; + int32_t u_pixelStride = 0; + int32_t v_pixelStride = 0; + AImage_getPlanePixelStride(image, 0, &y_pixelStride); + AImage_getPlanePixelStride(image, 1, &u_pixelStride); + AImage_getPlanePixelStride(image, 2, &v_pixelStride); + + int32_t y_rowStride = 0; + int32_t u_rowStride = 0; + int32_t v_rowStride = 0; + AImage_getPlaneRowStride(image, 0, &y_rowStride); + AImage_getPlaneRowStride(image, 1, &u_rowStride); + AImage_getPlaneRowStride(image, 2, &v_rowStride); + + uint8_t* y_data = 0; + uint8_t* u_data = 0; + uint8_t* v_data = 0; + int y_len = 0; + int u_len = 0; + int v_len = 0; + AImage_getPlaneData(image, 0, &y_data, &y_len); + AImage_getPlaneData(image, 1, &u_data, &u_len); + AImage_getPlaneData(image, 2, &v_data, &v_len); + + if (u_data == v_data + 1 && v_data == y_data + width * height && y_pixelStride == 1 && u_pixelStride == 2 && v_pixelStride == 2 && y_rowStride == width && u_rowStride == width && v_rowStride == width) + { + // already nv21 :) + ((NdkCamera*)context)->on_image((unsigned char*)y_data, (int)width, (int)height); + } + else + { + // construct nv21 + unsigned char* nv21 = new unsigned char[width * height + width * height / 2]; + { + // Y + unsigned char* yptr = nv21; + for (int y=0; yon_image((unsigned char*)nv21, (int)width, (int)height); + + delete[] nv21; + } + + AImage_delete(image); +} + +static void onSessionActive(void* context, ACameraCaptureSession *session) +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onSessionActive %p", session); +} + +static void onSessionReady(void* context, ACameraCaptureSession *session) +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onSessionReady %p", session); +} + +static void onSessionClosed(void* context, ACameraCaptureSession *session) +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onSessionClosed %p", session); +} + +void onCaptureFailed(void* context, ACameraCaptureSession* session, ACaptureRequest* request, ACameraCaptureFailure* failure) +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onCaptureFailed %p %p %p", session, request, failure); +} + +void onCaptureSequenceCompleted(void* context, ACameraCaptureSession* session, int sequenceId, int64_t frameNumber) +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onCaptureSequenceCompleted %p %d %ld", session, sequenceId, frameNumber); +} + +void onCaptureSequenceAborted(void* context, ACameraCaptureSession* session, int sequenceId) +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onCaptureSequenceAborted %p %d", session, sequenceId); +} + +void onCaptureCompleted(void* context, ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result) +{ +// __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "onCaptureCompleted %p %p %p", session, request, result); +} + +NdkCamera::NdkCamera() +{ + camera_facing = 0; + camera_orientation = 0; + + camera_manager = 0; + camera_device = 0; + image_reader = 0; + image_reader_surface = 0; + image_reader_target = 0; + capture_request = 0; + capture_session_output_container = 0; + capture_session_output = 0; + capture_session = 0; + + + // setup imagereader and its surface + { + AImageReader_new(640, 480, AIMAGE_FORMAT_YUV_420_888, /*maxImages*/2, &image_reader); + + AImageReader_ImageListener listener; + listener.context = this; + listener.onImageAvailable = onImageAvailable; + + AImageReader_setImageListener(image_reader, &listener); + + AImageReader_getWindow(image_reader, &image_reader_surface); + + ANativeWindow_acquire(image_reader_surface); + } +} + +NdkCamera::~NdkCamera() +{ + close(); + + if (image_reader) + { + AImageReader_delete(image_reader); + image_reader = 0; + } + + if (image_reader_surface) + { + ANativeWindow_release(image_reader_surface); + image_reader_surface = 0; + } +} + +int NdkCamera::open(int _camera_facing) +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "open"); + + camera_facing = _camera_facing; + + camera_manager = ACameraManager_create(); + + // find front camera + std::string camera_id; + { + ACameraIdList* camera_id_list = 0; + ACameraManager_getCameraIdList(camera_manager, &camera_id_list); + + for (int i = 0; i < camera_id_list->numCameras; ++i) + { + const char* id = camera_id_list->cameraIds[i]; + ACameraMetadata* camera_metadata = 0; + ACameraManager_getCameraCharacteristics(camera_manager, id, &camera_metadata); + + // query faceing + acamera_metadata_enum_android_lens_facing_t facing = ACAMERA_LENS_FACING_FRONT; + { + ACameraMetadata_const_entry e = { 0 }; + ACameraMetadata_getConstEntry(camera_metadata, ACAMERA_LENS_FACING, &e); + facing = (acamera_metadata_enum_android_lens_facing_t)e.data.u8[0]; + } + + if (camera_facing == 0 && facing != ACAMERA_LENS_FACING_FRONT) + { + ACameraMetadata_free(camera_metadata); + continue; + } + + if (camera_facing == 1 && facing != ACAMERA_LENS_FACING_BACK) + { + ACameraMetadata_free(camera_metadata); + continue; + } + + camera_id = id; + + // query orientation + int orientation = 0; + { + ACameraMetadata_const_entry e = { 0 }; + ACameraMetadata_getConstEntry(camera_metadata, ACAMERA_SENSOR_ORIENTATION, &e); + + orientation = (int)e.data.i32[0]; + } + + camera_orientation = orientation; + + ACameraMetadata_free(camera_metadata); + + break; + } + + ACameraManager_deleteCameraIdList(camera_id_list); + } + + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "open %s %d", camera_id.c_str(), camera_orientation); + + // open camera + { + ACameraDevice_StateCallbacks camera_device_state_callbacks; + camera_device_state_callbacks.context = this; + camera_device_state_callbacks.onDisconnected = onDisconnected; + camera_device_state_callbacks.onError = onError; + + ACameraManager_openCamera(camera_manager, camera_id.c_str(), &camera_device_state_callbacks, &camera_device); + } + + // capture request + { + ACameraDevice_createCaptureRequest(camera_device, TEMPLATE_PREVIEW, &capture_request); + + ACameraOutputTarget_create(image_reader_surface, &image_reader_target); + ACaptureRequest_addTarget(capture_request, image_reader_target); + } + + // capture session + { + ACameraCaptureSession_stateCallbacks camera_capture_session_state_callbacks; + camera_capture_session_state_callbacks.context = this; + camera_capture_session_state_callbacks.onActive = onSessionActive; + camera_capture_session_state_callbacks.onReady = onSessionReady; + camera_capture_session_state_callbacks.onClosed = onSessionClosed; + + ACaptureSessionOutputContainer_create(&capture_session_output_container); + + ACaptureSessionOutput_create(image_reader_surface, &capture_session_output); + + ACaptureSessionOutputContainer_add(capture_session_output_container, capture_session_output); + + ACameraDevice_createCaptureSession(camera_device, capture_session_output_container, &camera_capture_session_state_callbacks, &capture_session); + + ACameraCaptureSession_captureCallbacks camera_capture_session_capture_callbacks; + camera_capture_session_capture_callbacks.context = this; + camera_capture_session_capture_callbacks.onCaptureStarted = 0; + camera_capture_session_capture_callbacks.onCaptureProgressed = 0; + camera_capture_session_capture_callbacks.onCaptureCompleted = onCaptureCompleted; + camera_capture_session_capture_callbacks.onCaptureFailed = onCaptureFailed; + camera_capture_session_capture_callbacks.onCaptureSequenceCompleted = onCaptureSequenceCompleted; + camera_capture_session_capture_callbacks.onCaptureSequenceAborted = onCaptureSequenceAborted; + camera_capture_session_capture_callbacks.onCaptureBufferLost = 0; + + ACameraCaptureSession_setRepeatingRequest(capture_session, &camera_capture_session_capture_callbacks, 1, &capture_request, nullptr); + } + + return 0; +} + +void NdkCamera::close() +{ + __android_log_print(ANDROID_LOG_WARN, "NdkCamera", "close"); + + if (capture_session) + { + ACameraCaptureSession_stopRepeating(capture_session); + ACameraCaptureSession_close(capture_session); + capture_session = 0; + } + + if (camera_device) + { + ACameraDevice_close(camera_device); + camera_device = 0; + } + + if (capture_session_output_container) + { + ACaptureSessionOutputContainer_free(capture_session_output_container); + capture_session_output_container = 0; + } + + if (capture_session_output) + { + ACaptureSessionOutput_free(capture_session_output); + capture_session_output = 0; + } + + if (capture_request) + { + ACaptureRequest_free(capture_request); + capture_request = 0; + } + + if (image_reader_target) + { + ACameraOutputTarget_free(image_reader_target); + image_reader_target = 0; + } + + if (camera_manager) + { + ACameraManager_delete(camera_manager); + camera_manager = 0; + } +} + +void NdkCamera::on_image(const cv::Mat& rgb) const +{ +} + +void NdkCamera::on_image(const unsigned char* nv21, int nv21_width, int nv21_height) const +{ + // rotate nv21 + int w = 0; + int h = 0; + int rotate_type = 0; + if (camera_orientation == 0) + { + w = nv21_width; + h = nv21_height; + rotate_type = camera_facing == 0 ? 2 : 1; + } + if (camera_orientation == 90) + { + w = nv21_height; + h = nv21_width; + rotate_type = camera_facing == 0 ? 5 : 6; + } + if (camera_orientation == 180) + { + w = nv21_width; + h = nv21_height; + rotate_type = camera_facing == 0 ? 4 : 3; + } + if (camera_orientation == 270) + { + w = nv21_height; + h = nv21_width; + rotate_type = camera_facing == 0 ? 7 : 8; + } + + cv::Mat nv21_rotated(h + h / 2, w, CV_8UC1); + ncnn::kanna_rotate_yuv420sp(nv21, nv21_width, nv21_height, nv21_rotated.data, w, h, rotate_type); + + // nv21_rotated to rgb + cv::Mat rgb(h, w, CV_8UC3); + ncnn::yuv420sp2rgb(nv21_rotated.data, w, h, rgb.data); + + on_image(rgb); +} diff --git a/app/src/main/cpp/camera2/ndkcamera.h b/app/src/main/cpp/camera2/ndkcamera.h new file mode 100644 index 00000000..04a062ad --- /dev/null +++ b/app/src/main/cpp/camera2/ndkcamera.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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. + +#ifndef NDKCAMERA_H +#define NDKCAMERA_H + +#include +#include +#include +#include + +#include + +class NdkCamera +{ +public: + NdkCamera(); + virtual ~NdkCamera(); + + // facing 0=front 1=back + int open(int camera_facing = 0); + void close(); + + virtual void on_image(const cv::Mat& rgb) const; + + virtual void on_image(const unsigned char* nv21, int nv21_width, int nv21_height) const; + +public: + int camera_facing; + int camera_orientation; + +private: + ACameraManager* camera_manager; + ACameraDevice* camera_device; + AImageReader* image_reader; + ANativeWindow* image_reader_surface; + ACameraOutputTarget* image_reader_target; + ACaptureRequest* capture_request; + ACaptureSessionOutputContainer* capture_session_output_container; + ACaptureSessionOutput* capture_session_output; + ACameraCaptureSession* capture_session; +}; + +#endif // NDKCAMERA_H diff --git a/app/src/main/java/com/xinyingpower/microphoto/MainActivity.java b/app/src/main/java/com/xinyingpower/microphoto/MainActivity.java index 7fda1372..82c73ed3 100644 --- a/app/src/main/java/com/xinyingpower/microphoto/MainActivity.java +++ b/app/src/main/java/com/xinyingpower/microphoto/MainActivity.java @@ -40,7 +40,7 @@ public class MainActivity extends AppCompatActivity { binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - this.binding.start.setOnClickListener(new View.OnClickListener() { + this.binding.startServBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -48,7 +48,8 @@ public class MainActivity extends AppCompatActivity { String[] accessPermissions = new String[] { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.FOREGROUND_SERVICE + Manifest.permission.FOREGROUND_SERVICE, + /*Manifest.permission.SET_TIME,*/ }; boolean needRequire = false; for(String access : accessPermissions) { @@ -80,15 +81,22 @@ public class MainActivity extends AppCompatActivity { MainActivity.this.startService(intent); } - binding.start.setEnabled(false); - binding.stop.setEnabled(true); + binding.startServBtn.setEnabled(false); + binding.stopServBtn.setEnabled(true); } }); - this.binding.stop.setOnClickListener(new View.OnClickListener() { + this.binding.takePhotoBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + MicroPhotoService.takePhoto(MainActivity.this, 1, 255, true); + } + }); + + this.binding.stopServBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -97,8 +105,8 @@ public class MainActivity extends AppCompatActivity { MainActivity.this.stopService(intent); - binding.start.setEnabled(true); - binding.stop.setEnabled(false); + binding.startServBtn.setEnabled(true); + binding.stopServBtn.setEnabled(false); } @@ -108,7 +116,7 @@ public class MainActivity extends AppCompatActivity { Runnable runnable=new Runnable(){ @Override public void run() { - binding.start.performClick(); + binding.startServBtn.performClick(); } }; handler.postDelayed(runnable, 1000); diff --git a/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java b/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java index b5107c7e..ca1ccc43 100644 --- a/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java +++ b/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java @@ -16,6 +16,7 @@ import android.os.SystemClock; import androidx.core.app.NotificationCompat; import android.text.TextUtils; +import android.text.format.DateFormat; import android.util.Log; import android.widget.RemoteViews; import android.widget.Toast; @@ -52,13 +53,16 @@ public class MicroPhotoService extends Service { private static String ACTION_HEARTBEAT = "ACT_HB"; private static String ACTION_TAKE_PHOTO = "ACT_TP"; + private static String ACTION_TAKE_PHOTO_MANUALLY = "ACT_TP_M"; private static String ACTION_TIMEOUT = "ACT_TIMEOUT"; private static String EXTRA_PARAM_CHANNEL = "Channel"; + private static String EXTRA_PARAM_PRESET = "Preset"; + private static String EXTRA_PARAM_PHOTO_OR_VIDEO = "PhotoOrVideo"; private static String EXTRA_PARAM_SCHEDULES = "Schedules"; private static String EXTRA_PARAM_SCHEDULE = "Schedule_"; - private static String EXTRA_PARAM_PRESET = "Preset"; + private static String EXTRA_PARAM_TIME = "Time"; // private static String EXTRA_PARAM_FILENAME = "FileName"; @@ -103,6 +107,7 @@ public class MicroPhotoService extends Service { IntentFilter intentFilter = new IntentFilter(ACTION_HEARTBEAT); intentFilter.addAction(ACTION_TAKE_PHOTO); intentFilter.addAction(ACTION_TIMEOUT); + intentFilter.addAction(ACTION_TAKE_PHOTO_MANUALLY); registerReceiver( alarmReceiver, intentFilter); AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); @@ -138,7 +143,7 @@ public class MicroPhotoService extends Service { } // File path = getApplicationContext().getFilesDir(); String appPath = path.getAbsolutePath(); - Log.i("XYMP", "AppPath=" + appPath); + Log.i(TAG, "AppPath=" + appPath); String ip = "180.166.218.222"; int port = 40032; String cmdid = "XYDEV100230100012"; @@ -183,7 +188,7 @@ public class MicroPhotoService extends Service { int channel = (int)((val & 0xFF0000L) >> 16); int preset = (int)((val & 0xFF00L) >> 8); - mService.takePhoto(channel, preset, mService.buildPhotoDir(channel), mService.buildPhotoFileName(channel, preset, ts), true); + mService.notifyToTakePhoto(channel, preset, ts, mService.buildPhotoDir(channel), mService.buildPhotoFileName(channel, preset, ts), true); } } @@ -198,6 +203,15 @@ public class MicroPhotoService extends Service { mService.registerCaptureSchedule(startTime, baseTime); } + else if(TextUtils.equals(ACTION_TAKE_PHOTO_MANUALLY, action)) { + int channel = intent.getIntExtra(EXTRA_PARAM_CHANNEL, 0); + int preset = intent.getIntExtra(EXTRA_PARAM_PRESET, 0); + // long ts = intent.getLongExtra(EXTRA_PARAM_TIME, 0); + boolean photoOrVideo = intent.getBooleanExtra(EXTRA_PARAM_PHOTO_OR_VIDEO, true); + long ts = System.currentTimeMillis() / 1000; + + mService.notifyToTakePhoto(channel, preset, ts, mService.buildPhotoDir(channel), mService.buildPhotoFileName(channel, preset, ts), photoOrVideo); + } else if(TextUtils.equals(ACTION_TIMEOUT, action)) { long uid = intent.getLongExtra(EXTRA_PARAM_TIMER_UID, 0); Log.i(TAG, "Timeout:" + uid); @@ -256,7 +270,8 @@ public class MicroPhotoService extends Service { long currentTimeMillis = System.currentTimeMillis(); Date date = new Date(currentTimeMillis + timeout); - Log.d(TAG, "Register Photo Timer: " + date.toString() + " currentTimeMillis=" + currentTimeMillis + " timeout=" + timeout + " Channels=" + channelStr); + String dateStr = (String) DateFormat.format("MM-dd kk:mm:ss", date); + Log.d(TAG, "Register Photo Timer: " + dateStr + " currentTimeMillis=" + currentTimeMillis + " timeout=" + timeout + " Channels=" + channelStr); alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent); } @@ -354,6 +369,15 @@ public class MicroPhotoService extends Service { return true; } + public static void takePhoto(Context context, int channel, int preset, boolean photoOrVideo) { + + Intent intent = new Intent(ACTION_TAKE_PHOTO_MANUALLY); + intent.putExtra(EXTRA_PARAM_CHANNEL, channel); + intent.putExtra(EXTRA_PARAM_PRESET, preset); + intent.putExtra(EXTRA_PARAM_PHOTO_OR_VIDEO, photoOrVideo); + context.sendBroadcast(intent); + } + @Override public int onStartCommand(Intent intent, int flags, int startId) { @@ -501,12 +525,25 @@ public class MicroPhotoService extends Service { stateService = STATE_SERVICE.NOT_CONNECTED; - uninit(); DSCameraManager.getInstace().unInit(); super.onDestroy(); } + protected boolean updateTime(long timeInMillis) { + boolean res = false; + try { + // Calendar c = Calendar.getInstance(); + // c.set(2010, 1, 1, 12, 00, 00); + AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); + am.setTime(timeInMillis); + res = true; + } catch (Exception ex) { + int aa = 0; + } + return true; + } + public String buildPhotoDir(int channel) { File path = new File(Environment.getExternalStorageDirectory(), "com.xyp.mp/photos/"); @@ -532,7 +569,7 @@ public class MicroPhotoService extends Service { protected native boolean init(String appPath, String ip, int port, String cmdid); protected native long getHeartbeatDuration(); protected native long[] getPhotoTimeData(); - protected native boolean takePhoto(int channel, int preset, String path, String fileName, boolean sendToCma); + protected native boolean notifyToTakePhoto(int channel, int preset, long scheduleTime, String path, String fileName, boolean sendToCma); protected native boolean sendHeartbeat(); protected native boolean fireTimeout(long uid); protected native boolean uninit(); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f8192bd4..a133d1e0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -17,7 +17,7 @@ app:layout_constraintTop_toTopOf="parent" />