#include "TerminalDevice.h" #include #include "PhoneDevice.h" #include #include #include #include "ncnn/yolov5ncnn.h" #include "GPIOControl.h" #include "CvText.h" #include #include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; #define CMD_SET_485_EN_STATE 131 #define CMD_SET_CAM_3V3_EN_STATE 132 #define CMD_SET_12V_EN_STATE 133 extern bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread); #define WAKELOCK_NAME "NDK_WK_" // This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their // ranges // are normalized to eight bits. static const int kMaxChannelValue = 262143; static long getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num) { int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC); if (fd < 0) { ALOGW("Unable to open /proc/meminfo"); return -1; } char buffer[2048]; const int len = read(fd, buffer, sizeof(buffer)-1); close(fd); if (len < 0) { ALOGW("Unable to read /proc/meminfo"); return -1; } buffer[len] = 0; size_t numFound = 0; jlong mem = 0; char* p = buffer; while (*p && numFound < num) { int i = 0; while (sums[i]) { if (strncmp(p, sums[i], sumsLen[i]) == 0) { p += sumsLen[i]; while (*p == ' ') p++; char* num = p; while (*p >= '0' && *p <= '9') p++; if (*p != 0) { *p = 0; p++; if (*p == 0) p--; } mem += atoll(num) * 1024; numFound++; break; } i++; } p++; } return numFound > 0 ? mem : -1; } static jlong android_os_Process_getFreeMemory() { static const char* const sums[] = { "MemFree:", "Cached:", NULL }; static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 }; return getFreeMemoryImpl(sums, sumsLen, 2); } static jlong android_os_Process_getTotalMemory() { static const char* const sums[] = { "MemTotal:", NULL }; static const size_t sumsLen[] = { strlen("MemTotal:"), 0 }; return getFreeMemoryImpl(sums, sumsLen, 1); } static inline uint32_t YUV2RGB(int nY, int nU, int nV) { nY -= 16; nU -= 128; nV -= 128; if (nY < 0) nY = 0; // This is the floating point equivalent. We do the conversion in integer // because some Android devices do not have floating point in hardware. // nR = (int)(1.164 * nY + 1.596 * nV); // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU); // nB = (int)(1.164 * nY + 2.018 * nU); int nR = (int)(1192 * nY + 1634 * nV); int nG = (int)(1192 * nY - 833 * nV - 400 * nU); int nB = (int)(1192 * nY + 2066 * nU); nR = std::min(kMaxChannelValue, std::max(0, nR)); nG = std::min(kMaxChannelValue, std::max(0, nG)); nB = std::min(kMaxChannelValue, std::max(0, nB)); nR = (nR >> 10) & 0xff; nG = (nG >> 10) & 0xff; nB = (nB >> 10) & 0xff; return 0xff000000 | (nR << 16) | (nG << 8) | nB; } CPhoneDevice::CPhoneCamera::CPhoneCamera(CPhoneDevice* dev, int32_t width, int32_t height, const NdkCamera::CAMERA_PARAMS& params) : NdkCamera(width, height, params), m_dev(dev) { } CPhoneDevice::CPhoneCamera::~CPhoneCamera() { m_dev = NULL; } bool CPhoneDevice::CPhoneCamera::on_image(cv::Mat& rgb) { if (m_dev != NULL) { return m_dev->OnImageReady(rgb); } return false; } void CPhoneDevice::CPhoneCamera::on_error(const std::string& msg) { if (m_dev != NULL) { m_dev->onError(msg); } } CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service, const std::string& appPath, unsigned int netId, unsigned int versionCode) : mCameraPowerCount(0), mOtgCount(0), mVersionCode(versionCode) { mCamera = NULL; m_listener = NULL; m_pRecognizationCfg = NULL; mHeartbeatStartTime = 0; mHeartbeatDuration = 0; m_javaService = NULL; m_appPath = appPath; mNetId = netId; m_signalLevel = 0; m_signalLevelUpdateTime = time(NULL); m_sysApiClass = NULL; RegisterHandlerForSignal(SIGUSR2); m_vm = vm; JNIEnv* env = NULL; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); } m_javaService = env->NewGlobalRef(service); jclass classService = env->GetObjectClass(m_javaService); mRegisterHeartbeatMid = env->GetMethodID(classService, "registerHeartbeatTimer", "(I)V"); mUpdateTimeMid = env->GetMethodID(classService, "updateTime", "(J)Z"); mUpdateCaptureScheduleMid = env->GetMethodID(classService, "updateCaptureSchedule", "(J)Z"); mStartRecordingMid = env->GetMethodID(classService, "startRecording", "(IJIIIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); mRequestWakelockMid = env->GetMethodID(classService, "requestWakelock", "(Ljava/lang/String;J)V"); mReleaseWakelockMid = env->GetMethodID(classService, "releaseWakelock", "(Ljava/lang/String;)V"); mGetSystemInfoMid = env->GetMethodID(classService, "getSystemInfo", "()Ljava/lang/String;"); mInstallAppMid = env->GetMethodID(classService, "installApp", "(Ljava/lang/String;J)Z"); mRebootMid = env->GetMethodID(classService, "reboot", "(I)V"); mEnableGpsMid = env->GetMethodID(classService, "enableGps", "(Z)V"); mRequestPositionMid = env->GetMethodID(classService, "requestPosition", "()Z"); env->DeleteLocalRef(classService); jclass classSysApi = env->FindClass("com/dev/devapi/api/SysApi"); if(classSysApi != NULL) { m_sysApiClass = (jclass)env->NewGlobalRef(classSysApi); mTurnOtgMid = env->GetStaticMethodID(classSysApi, "setOtgState", "(Z)V"); mSetCam3V3EnableMid = env->GetStaticMethodID(classSysApi, "setCam3V3Enable", "(Z)V"); env->DeleteLocalRef(classSysApi); } if (didAttachThread) { vm->DetachCurrentThread(); } m_timerUidFeed = time(NULL) * 1000; m_wakelockIdFeed = (unsigned long)m_timerUidFeed; } CPhoneDevice::~CPhoneDevice() { m_devLocker.lock(); for (auto it = mTimers.begin(); it != mTimers.end(); ++it) { timer_delete((timer_t)it->first); delete it->second; } mTimers.clear(); m_devLocker.unlock(); JNIEnv* env = NULL; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); } env->DeleteGlobalRef(m_javaService); if (m_sysApiClass != NULL) { env->DeleteGlobalRef(m_sysApiClass); } if (didAttachThread) { m_vm->DetachCurrentThread(); } m_javaService = NULL; m_sysApiClass = NULL; if (m_pRecognizationCfg != NULL) { ncnn_uninit(); m_pRecognizationCfg = NULL; } } void CPhoneDevice::SetListener(IListener* listener) { m_listener = listener; } void CPhoneDevice::SetRecognizationCfg(const IDevice::CFG_RECOGNIZATION* pRecognizationCfg) { if (m_pRecognizationCfg == NULL && pRecognizationCfg != NULL && (pRecognizationCfg->enabled != 0)) { // TODO XYLOG(XYLOG_SEVERITY_DEBUG, "Start init ncnn"); ncnn_init(); std::string paramFile = m_appPath + (APP_PATH_RECOG_PARAM); std::string binFile = m_appPath + (APP_PATH_RECOG_BIN); bool res = YoloV5Ncnn_Init(paramFile, binFile); if (res) { XYLOG(XYLOG_SEVERITY_INFO, "Succeeded to Init NCNN"); } else { XYLOG(XYLOG_SEVERITY_ERROR, "Failed to Init NCNN"); } } m_pRecognizationCfg = pRecognizationCfg; } bool CPhoneDevice::BindNetwork(int sock) { return true; } bool CPhoneDevice::UpdateTime(time_t ts) { JNIEnv* env = NULL; jboolean ret = JNI_FALSE; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); } jlong timeInMillis = ((jlong)ts) * 1000; ret = env->CallBooleanMethod(m_javaService, mUpdateTimeMid, timeInMillis); if (didAttachThread) { m_vm->DetachCurrentThread(); } return (ret == JNI_TRUE); } bool CPhoneDevice::UpdateSchedules() { JNIEnv* env = NULL; jboolean ret = JNI_FALSE; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); } time_t ts = time(NULL); ret = env->CallBooleanMethod(m_javaService, mUpdateCaptureScheduleMid, ts); if (didAttachThread) { m_vm->DetachCurrentThread(); } return (ret == JNI_TRUE); } bool CPhoneDevice::QuerySystemProperties(std::map& properties) { char value[PROP_VALUE_MAX] = { 0 }; std::map powerInfo; for (std::map::iterator it = properties.begin(); it != properties.end(); ++it) { if (!(it->second.empty())) { continue; } if (it->first == PROP_EQUIP_NAME) { __system_property_get("ro.product.name", value); it->second = value; } else if (it->first == PROP_MODEL) { __system_property_get("ro.product.model", value); it->second = value; } else if (it->first == PROP_BS_MANU) { __system_property_get("ro.product.manufacturer", value); it->second = value; } else if (it->first == PROP_VERSION) { snprintf(value, sizeof(value), "%u.%03u", (mVersionCode / 1000), (mVersionCode % 1000)); // __system_property_get("ro.build.version.release", value); it->second = value; } else if (it->first == PROP_PROD_DATE) { __system_property_get("ro.build.date.utc", value); it->second = value; } else if (it->first == PROP_SN) { __system_property_get("ro.serialno", value); it->second = value; } else if (it->first == PROP_IMEI) { __system_property_get("phone.imei", value); it->second = value; } else if (it->first == PROP_OPERATION_TEMP) { it->second = QueryCpuTemperature(); } else if (it->first == PROP_BS_ID) { it->second = "SHXY"; } else if (it->first == PROP_FREE_ROM) { fs::space_info si = fs::space("/data"); it->second = std::to_string(si.available); // Unit: M } else if (it->first == PROP_TOTAL_ROM) { fs::space_info si = fs::space("/data"); it->second = std::to_string(si.capacity); // Unit: M } else if (it->first == PROP_FREE_MEMORY) { fs::space_info si = fs::space("/data"); it->second = std::to_string(android_os_Process_getFreeMemory()); // Unit: M } else if (it->first == PROP_TOTAL_MEMORY) { fs::space_info si = fs::space("/data"); it->second = std::to_string(android_os_Process_getTotalMemory()); // Unit: M } else if (it->first == (PROP_CHARGING_VOLTAGE)) { double val = GpioControl::getChargingVoltage() / 200.0; // ChargeVol *5/1000 char str[32] = { 0 }; snprintf(str, sizeof(str), "%.1f", val); it->second = str; } else if (it->first == (PROP_CHARGING_CURRENT)) { it->second = std::to_string(GpioControl::getChargingCurrent()); } else if (it->first == (PROP_CHARGING_POWER)) { it->second = std::to_string(GpioControl::getChargingPower()); } else if (it->first == (PROP_CHARGING_BUS_VOL)) { double val = -1; char str[32] = { 0 }; for (int idx = 0; idx < 3; idx++) { val = GpioControl::getChargingBusVoltage(); if (val < 0) { continue; } snprintf(str, sizeof(str), "%.1f", (val / 1000.0)); it->second = str; break; } } else if (it->first == (PROP_BATTERY_VOLTAGE)) { // double val = GpioControl::getBatteryVoltage() * 3.0 / 1000.0; // // BatVol double val = GpioControl::getBatteryVoltage() / 1000.0; // // BatVol char str[32] = { 0 }; snprintf(str, sizeof(str), "%.1f", val); it->second = str; } else if (it->first == (PROP_BATTERY_CURRENT)) { it->second = std::to_string(GpioControl::getBatteryCurrent()); } else if (it->first == (PROP_BATTERY_POWER)) { it->second = std::to_string(GpioControl::getBatteryPower()); } else if (it->first == (PROP_BATTERY_BUS_VOL)) { it->second = std::to_string(GpioControl::getBatteryBusVoltage()); } else if ((it->first == (PROP_SIGNAL_4G)) || (it->first == (PROP_SIGNAL_2G)) || (it->first == (PROP_SIGNAL_LEVEL))) { it->second = std::to_string(m_signalLevel); } else if (startsWith(it->first, PROP_JAVA_PREFIX)) { if (powerInfo.empty()) { QueryPowerInfo(powerInfo); } auto it2 = powerInfo.find(it->first); if (it2 != powerInfo.cend()) { it->second = it2->second; } } } // __system_property_get("ro.telephony.default_network", value); return true; } std::string CPhoneDevice::QueryCpuTemperature() { // /sys/devices/virtual/thermal/thermal_zone0/temp std::vector data; if (readFile("/sys/devices/virtual/thermal/thermal_zone0/temp", data) && !data.empty()) { data.push_back(0); int temp = atoi((const char*)(&data[0])); return std::to_string(temp / 1000); } return ""; } void CPhoneDevice::QueryPowerInfo(std::map& powerInfo) { JNIEnv* env = NULL; jboolean ret = JNI_FALSE; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); } jobject jobj = env->CallObjectMethod(m_javaService, mGetSystemInfoMid); std::string str = jstring2string(env, (jstring)jobj); if (didAttachThread) { m_vm->DetachCurrentThread(); } if (!str.empty()) { std::map queries = parseQuery(str); powerInfo.swap(queries); } } bool CPhoneDevice::GetNextScheduleItem(uint32_t tsBasedZero, uint32_t scheduleTime, vector& items) { return false; } bool CPhoneDevice::InstallAPP(const std::string& path, unsigned int delayedTime) { JNIEnv* env = NULL; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); } jstring jpath = env->NewStringUTF(path.c_str()); env->CallVoidMethod(m_javaService, mInstallAppMid, jpath, (jlong)delayedTime); // env->ReleaseStringUTFChars(jpath, path.c_str()); env->DeleteLocalRef(jpath); if (didAttachThread) { m_vm->DetachCurrentThread(); } return true; } bool CPhoneDevice::Reboot(int resetType) { if (resetType == 1) { // reboot the device GpioControl::reboot(); return true; } JNIEnv* env = NULL; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); } env->CallVoidMethod(m_javaService, mRebootMid, resetType); if (didAttachThread) { m_vm->DetachCurrentThread(); } return true; } bool CPhoneDevice::EnableGPS(bool enabled) { JNIEnv* env = NULL; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); return false; } jboolean jenabled = enabled ? JNI_TRUE : JNI_FALSE; env->CallVoidMethod(m_javaService, mEnableGpsMid, jenabled); if (didAttachThread) { m_vm->DetachCurrentThread(); } return true; } bool CPhoneDevice::RequestPosition() { JNIEnv* env = NULL; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); return false; } jboolean ret = env->CallBooleanMethod(m_javaService, mRequestPositionMid); if (didAttachThread) { m_vm->DetachCurrentThread(); } return (ret == JNI_TRUE); } void CPhoneDevice::handleSignal(int sig, siginfo_t *si, void *uc) { TIMER_CONTEXT* context = (TIMER_CONTEXT*)(si->si_value.sival_ptr); context->device->handleTimerImpl(context); } bool CPhoneDevice::RegisterHandlerForSignal(int sig) { return true; // Establish handler for timer signal struct sigaction sa; sigset_t mask; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = CPhoneDevice::handleSignal; sigemptyset(&sa.sa_mask); if (sigaction(sig, &sa, NULL) == -1) { return false; } return true; // Block timer signal temporarily // printf("Blocking signal %d\n", SIG); sigemptyset(&mask); sigaddset(&mask, sig); if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) { return false; } return true; } void CPhoneDevice::handleTimer(union sigval v) { TIMER_CONTEXT* context = (TIMER_CONTEXT*)(v.sival_ptr); context->device->handleTimerImpl(context); } void CPhoneDevice::handleTimerImpl(CPhoneDevice::TIMER_CONTEXT* context) { context->times++; if (context->expectedTimes == 0 || context->times <= context->expectedTimes) { if (m_listener != NULL) { m_listener->OnTimeout(context->uid, context->timerType, context->data, context->times); } } } IDevice::timer_uid_t CPhoneDevice::RegisterTimer(unsigned int timerType, unsigned int timeout, void* data, unsigned long times/* = 0*/) { struct sigevent evp = { 0 }; struct itimerspec ts = { 0 }; timer_t timer; int ret; TIMER_CONTEXT* context = new TIMER_CONTEXT(); context->device = this; context->data = data; context->timerType = timerType; context->expectedTimes = times; evp.sigev_value.sival_ptr = context; evp.sigev_notify = SIGEV_THREAD; //SIGEV_THREAD_ID; evp.sigev_notify_function = CPhoneDevice::handleTimer; // evp.sigev_notify_thread_id = gettid(); // evp.sigev_notify = SIGEV_SIGNAL; // evp.sigev_signo = SIGUSR2; ret = timer_create(CLOCK_REALTIME, &evp, &timer); if( ret) { int err = errno; delete context; return INVALID_TIMER_UID; } context->uid = (unsigned long)timer; ts.it_value.tv_sec = (timeout / 1000); ts.it_value.tv_nsec = (timeout % 1000) * 1000; if (times != 1) { ts.it_interval.tv_sec = ts.it_value.tv_sec; ts.it_interval.tv_nsec = ts.it_value.tv_nsec; } ret = timer_settime(timer, 0, &ts, NULL); if(ret) { timer_delete(timer); delete context; return INVALID_TIMER_UID; } m_devLocker.lock(); mTimers.insert(mTimers.end(), std::pair((IDevice::timer_uid_t)timer, context)); m_devLocker.unlock(); return (IDevice::timer_uid_t)timer; } bool CPhoneDevice::UnregisterTimer(IDevice::timer_uid_t uid) { timer_t timer = (timer_t)uid; int res = timer_delete(timer); m_devLocker.lock(); std::map::iterator it = mTimers.find(uid); if (it != mTimers.end()) { delete it->second; mTimers.erase(it); m_devLocker.unlock(); return true; } m_devLocker.unlock(); return false; } unsigned long CPhoneDevice::RequestWakelock(unsigned long timeout) { unsigned long wakelockId = m_wakelockIdFeed.fetch_add(1); std::string name = WAKELOCK_NAME; name += to_string(wakelockId); // ALOGI("RequestWakelock=%lld",wakelockId); jboolean ret = JNI_FALSE; JNIEnv* env = NULL; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); return 0; } jstring jname = env->NewStringUTF(name.c_str()); jlong jtimeout = (jlong)timeout; env->CallVoidMethod(m_javaService, mRequestWakelockMid, jname, jtimeout); // env->ReleaseStringUTFChars(jname, name.c_str()); env->DeleteLocalRef(jname); if (didAttachThread) { m_vm->DetachCurrentThread(); } return wakelockId; } bool CPhoneDevice::ReleaseWakelock(unsigned long wakelock) { // ALOGI("ReleaseWakelock=%lld", wakelock); std::string name = WAKELOCK_NAME; name += to_string(wakelock); jboolean ret = JNI_FALSE; JNIEnv* env = NULL; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); return false; } jstring jname = env->NewStringUTF(name.c_str()); env->CallVoidMethod(m_javaService, mReleaseWakelockMid, jname); env->DeleteLocalRef(jname); // env->ReleaseStringUTFChars(jname, name.c_str()); if (didAttachThread) { m_vm->DetachCurrentThread(); } return true; } IDevice::timer_uid_t CPhoneDevice::RegisterHeartbeat(unsigned int timerType, unsigned int timeout) { mHeartbeatStartTime = time(NULL); mHeartbeatDuration = timeout; IDevice::timer_uid_t uid = m_timerUidFeed.fetch_add(1); jboolean ret = JNI_FALSE; JNIEnv* env = NULL; bool didAttachThread = false; bool res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); return 0; } env->CallVoidMethod(m_javaService, mRegisterHeartbeatMid, (jint)timeout); if (didAttachThread) { m_vm->DetachCurrentThread(); } return uid; } bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector& osds, const std::string& path) { if (m_threadClose.joinable()) { XYLOG(XYLOG_SEVERITY_INFO, "TP: Wait Prev Thread CH=%u PR=%X PHOTOID=%u", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset, photoInfo.photoId); m_threadClose.join(); XYLOG(XYLOG_SEVERITY_INFO, "TP: Wait Prev Thread End CH=%u PR=%X PHOTOID=%u", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset, photoInfo.photoId); } if (mCamera != NULL) { XYLOG(XYLOG_SEVERITY_INFO, "TP: mCamera ISNOT null CH=%u PR=%X PHOTOID=%u", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset, photoInfo.photoId); delete mCamera; mCamera = NULL; } XYLOG(XYLOG_SEVERITY_INFO, "TP: CH=%u PR=%X PHOTOID=%u", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset, photoInfo.photoId); mPhotoInfo = photoInfo; mPath = path; mOsds = osds; NdkCamera::CAMERA_PARAMS params; params.hdrMode = mPhotoInfo.hdrMode; params.nightMode = mPhotoInfo.nightMode; params.autoFocus = mPhotoInfo.autoFocus; params.autoExposure = mPhotoInfo.autoExposure; params.focusTimeout = mPhotoInfo.focusTimeout * 1000; params.exposureTime = mPhotoInfo.exposureTime; params.sensibility = mPhotoInfo.sensibility; params.orientation = mPhotoInfo.orientation; // GpioControl::EnableGpio(CMD_SET_CAM_3V3_EN_STATE, true); bool res = false; if (photoInfo.usbCamera) { TurnOnOtg(NULL); } TurnOnCameraPower(NULL); res = true; if (mPhotoInfo.mediaType == 0) { mCamera = new CPhoneCamera(this, photoInfo.width, photoInfo.height, params); if (mCamera->open(to_string(mPhotoInfo.cameraId)) == 0) { XYLOG(XYLOG_SEVERITY_DEBUG, "TP: Succeeded to OpenCamera CH=%u PR=%X PHOTOID=%u", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset, photoInfo.photoId); } else { XYLOG(XYLOG_SEVERITY_DEBUG, "TP: Failed to OpenCamera CH=%u PR=%X PHOTOID=%u", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset, photoInfo.photoId); delete mCamera; mCamera = NULL; res = false; TurnOffCameraPower(NULL); if (photoInfo.usbCamera) { TurnOffOtg(NULL); } } } else { JNIEnv* env = NULL; bool didAttachThread = false; res = GetJniEnv(m_vm, &env, didAttachThread); if (!res) { ALOGE("Failed to get JNI Env"); return false; } jstring leftTopOSD = NULL; jstring rightTopOSD = NULL; jstring rightBottomOSD = NULL; jstring leftBottomOSD = NULL; for (vector::const_iterator it = mOsds.cbegin(); it != mOsds.cend(); ++it) { if (it->text.empty()) { continue; } switch (it->alignment) { case OSD_ALIGNMENT_TOP_LEFT: leftTopOSD = env->NewStringUTF(it->text.c_str()); break; case OSD_ALIGNMENT_TOP_RIGHT: rightTopOSD = env->NewStringUTF(it->text.c_str()); break; case OSD_ALIGNMENT_BOTTOM_RIGHT: rightBottomOSD = env->NewStringUTF(it->text.c_str()); break; case OSD_ALIGNMENT_BOTTOM_LEFT: leftBottomOSD = env->NewStringUTF(it->text.c_str()); break; } } int orientation = mPhotoInfo.orientation == 0 ? -1 : (mPhotoInfo.orientation - 1) * 90; env->CallVoidMethod(m_javaService, mStartRecordingMid, mPhotoInfo.cameraId, (unsigned long)mPhotoInfo.photoId, mPhotoInfo.duration, mPhotoInfo.width, mPhotoInfo.height, mPhotoInfo.duration, orientation, leftTopOSD, rightTopOSD, rightBottomOSD, leftBottomOSD); if (leftTopOSD) env->DeleteLocalRef(leftTopOSD); if (rightTopOSD) env->DeleteLocalRef(rightTopOSD); if (rightBottomOSD) env->DeleteLocalRef(rightBottomOSD); if (leftBottomOSD) env->DeleteLocalRef(leftBottomOSD); if (didAttachThread) { m_vm->DetachCurrentThread(); } } return res; } bool CPhoneDevice::CloseCamera() { if (mCamera != NULL) { auto camera = mCamera; mCamera = NULL; camera->close(); delete camera; } return true; } void CPhoneDevice::CloseCamera2(CPhoneDevice::CPhoneCamera* camera, unsigned int photoId, bool turnOffOtg) { XYLOG(XYLOG_SEVERITY_DEBUG, "TP: Start CloseCamera PHOTOID=%u", photoId); // std::this_thread::sleep_for(std::chrono::milliseconds(16)); if (camera != NULL) { camera->close(); delete camera; } XYLOG(XYLOG_SEVERITY_DEBUG, "TP: Will Turn Off Power=%u", photoId); if (turnOffOtg) { TurnOffOtg(NULL); } TurnOffCameraPower(NULL); XYLOG(XYLOG_SEVERITY_DEBUG, "TP: End Turn Off Power=%u", photoId); XYLOG(XYLOG_SEVERITY_DEBUG, "TP: CloseCamera PHOTOID=%u", photoId); } void visualize(const char* filename, const ncnn::Mat& m) { cv::Mat a(m.h, m.w, CV_8UC3); m.to_pixels(a.data, ncnn::Mat::PIXEL_BGR2RGB); cv::imwrite(filename, a); } void DrawOutlineText(cv::Ptr ft2, cv::Mat& mat, const std::string& str, cv::Point startPoint, int fontSize, cv::Scalar clr, int thickness) { std::vector lines = split(str, "\n"); int lineHeight = 0; cv::Point pt = startPoint; cv::Size textSize; int baseline = 0; for (std::vector::const_iterator it = lines.cbegin(); it != lines.cend(); ++it ) { textSize = ft2->getTextSize(*it, fontSize, thickness, &baseline); lineHeight = std::max(fontSize, textSize.height + baseline); ft2->putText(mat, *it, pt, fontSize, clr, thickness, cv::LINE_AA, false, true); pt.x = startPoint.x; pt.y += lineHeight + (lineHeight >> 2); // 125% } } bool CPhoneDevice::OnImageReady(cv::Mat& mat) { if (mCamera == NULL) { // int aa = 0; return false; } mPhotoInfo.photoTime = time(NULL); int baseline = 0; cv::Size textSize; double height = mat.size().height; double width = mat.size().width; // double ratio = std::min(height / 1024, width / 1920); double ratio = height / 1024.0; int thickness = round(1.2 * ratio); if (thickness < 1) thickness = 1; else if (thickness > 4) thickness = 4; cv::Scalar scalar(255, 255, 255); // white int fontSize = (int)(24.0 * ratio); std::string fontPath; if (existsFile("/system/fonts/NotoSansCJK-Regular.ttc")) { fontPath = "/system/fonts/NotoSansCJK-Regular.ttc"; } else if (existsFile("/system/fonts/NotoSerifCJK-Regular.ttc")) { fontPath = "/system/fonts/NotoSerifCJK-Regular.ttc"; } else { fontPath = m_appPath+ "fonts/Noto.otf"; } cv::Ptr ft2; ft2 = cv::ft::createFreeType2(); ft2->loadFontData(fontPath.c_str(), 0); // cv::Rect rc(0, 0, mat.cols, mat.rows); // cv::rectangle (mat, rc, cv::Scalar(255, 255, 255), cv::FILLED); std::vector objs; if ((m_pRecognizationCfg != NULL) && (m_pRecognizationCfg->enabled != 0) && (mPhotoInfo.recognization != 0)) { // visualize(ncnnPath.c_str(), in); #ifdef _DEBUG double startTime = ncnn::get_current_time(); #endif // _DEBUG bool detected = YoloV5NcnnDetect(mat, true, m_pRecognizationCfg->blobName8, m_pRecognizationCfg->blobName16, m_pRecognizationCfg->blobName32, objs); #ifdef _DEBUG double elasped = ncnn::get_current_time() - startTime; // __android_log_print(ANDROID_LOG_DEBUG, "YoloV5Ncnn", "%.2fms detect", elasped); #endif // _DEBUG #ifdef _DEBUG ALOGI( "NCNN recognization: %.2fms res=%d", elasped, ((detected && !objs.empty()) ? 1 : 0)); #endif if (detected && !objs.empty()) { #if 0 static const char* class_names[] = { "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush" }; #endif cv::Scalar borderColor(m_pRecognizationCfg->borderColor & 0xFF, (m_pRecognizationCfg->borderColor & 0xFF00) >> 8, (m_pRecognizationCfg->borderColor & 0xFF0000) >> 16); for (std::vector::const_iterator it = objs.cbegin(); it != objs.cend(); ++it) { if (it->label >= m_pRecognizationCfg->items.size()) { continue; } const IDevice::CFG_RECOGNIZATION::ITEM& item = m_pRecognizationCfg->items[it->label]; if (item.enabled == 0 || it->prob < item.prob) { continue; } #ifdef _DEBUG ALOGD("Label: %d=%s (%f,%f)-(%f,%f)", it->label, item.name.c_str(), it->x, it->y, it->w, it->h); #endif cv::Rect rc(it->x, it->y, it->w, it->h); cv::rectangle(mat, rc, borderColor, m_pRecognizationCfg->thickness); // putText } } } #ifdef _DEBUG cv::Scalar scalarRed(0, 0, 255); // red NdkCamera::CAPTURE_RESULT captureResult = mCamera->getCaptureResult(); char str[128] = { 0 }; snprintf(str, sizeof(str), "AE=%u EXPS=%ums ISO=%d AF=%u FD=%.3f AFS=%u HDR=%d", captureResult.autoExposure, (unsigned int)(captureResult.exposureTime / 1000000), captureResult.sensitibity, captureResult.autoFocus, isnan(captureResult.FocusDistance) ? 0 : captureResult.FocusDistance, (unsigned int)captureResult.afState, captureResult.hdrMode); // cv::putText(mat, str, cv::Point(0, mat.rows - 20), cv::FONT_HERSHEY_COMPLEX, fontScale, scalar, thickness1, cv::LINE_AA); textSize = ft2->getTextSize(str, fontSize, -1, &baseline); ft2->putText(mat, str, cv::Point(0, mat.rows - fontSize - 20 * ratio), fontSize, scalarRed, -1, cv::LINE_AA, false); // text.putText(mat, str.c_str(), cv::Point(0, mat.rows - 20), scalar); // puttext::Pixel32 colorText = {255, 255, 255}; // puttext::Pixel32 colorBorder = {0, 0, 0}; // textPaint.DrawText((puttext::Pixel32*)mat.data, wstr.c_str(), 16, colorText, colorBorder, 3); #endif cv::Point pt; for (vector::const_iterator it = mOsds.cbegin(); it != mOsds.cend(); ++it) { if (it->text.empty()) { continue; } #ifdef _DEBUG if (it->alignment == OSD_ALIGNMENT_BOTTOM_RIGHT) { int aa = 0; } #endif textSize = ft2->getTextSize(it->text, fontSize, thickness, &baseline); XYLOG(XYLOG_SEVERITY_DEBUG, "%s font Size=%d height: %d baseline=%d", it->text.c_str(), fontSize, textSize.height, baseline); if (it->alignment == OSD_ALIGNMENT_TOP_LEFT) { pt.x = it->x * ratio; pt.y = it->y * ratio; } else if (it->alignment == OSD_ALIGNMENT_TOP_RIGHT) { pt.x = width - textSize.width - it->x * ratio; pt.y= it->y * ratio; } else if (it->alignment == OSD_ALIGNMENT_BOTTOM_RIGHT) { pt.x = width - textSize.width - it->x * ratio; pt.y = height - it->y * ratio - textSize.height - baseline; } else if (it->alignment == OSD_ALIGNMENT_BOTTOM_LEFT) { pt.x = it->x * ratio; pt.y = height - it->y * ratio - textSize.height - baseline; } // cv::Rect rc(pt.x, pt.y, textSize.width, textSize.height); // cv::rectangle(mat, rc, cv::Scalar(0,255,255), 2); DrawOutlineText(ft2, mat, it->text, pt, fontSize, scalar, thickness); } vector params; params.push_back(cv::IMWRITE_JPEG_QUALITY); params.push_back(mPhotoInfo.quality); bool res = false; std::string fullPath = mPath + CTerminal::BuildPhotoFileName(mPhotoInfo); if (!std::filesystem::exists(std::filesystem::path(fullPath))) { bool res = cv::imwrite(fullPath.c_str(), mat, params); if (!res) { XYLOG(XYLOG_SEVERITY_ERROR, "Failed to write photo: %s", fullPath.c_str()); } else { XYLOG(XYLOG_SEVERITY_INFO, "Succeeded to write photo: %s", fullPath.c_str()); } TakePhotoCb(res, mPhotoInfo, fullPath, time(NULL), objs); } else { ALOGI("Photo file exists: %s", mPath.c_str()); } CPhoneCamera* pCamera = mCamera; mCamera = NULL; bool turnOffOtg = (mPhotoInfo.usbCamera != 0); std::thread closeThread(&CPhoneDevice::CloseCamera2, this, pCamera, mPhotoInfo.photoId, turnOffOtg); m_threadClose.swap(closeThread); if (closeThread.joinable()) { closeThread.detach(); } return res; } bool CPhoneDevice::OnVideoReady(bool result, const char* path, unsigned int photoId) { mPhotoInfo.photoTime = time(NULL); CPhoneCamera* pCamera = NULL; std::vector objs; std::string fullPath = mPath + CTerminal::BuildPhotoFileName(mPhotoInfo); if (result) { std::rename(path, fullPath.c_str()); } TakePhotoCb(result, mPhotoInfo, fullPath, time(NULL), objs); bool turnOffOtg = (mPhotoInfo.usbCamera != 0); std::thread closeThread(&CPhoneDevice::CloseCamera2, this, pCamera, mPhotoInfo.photoId, turnOffOtg); m_threadClose.swap(closeThread); return result; } void CPhoneDevice::onError(const std::string& msg) { if (mCamera == NULL) { int aa = 0; return; } XYLOG(XYLOG_SEVERITY_ERROR, "Failed to Take Photo (IMGID=%u): %s", mPhotoInfo.photoId, msg.c_str()); CPhoneCamera* pCamera = mCamera; mCamera = NULL; TakePhotoCb(false, mPhotoInfo, mPath, 0); bool turnOffOtg = (mPhotoInfo.usbCamera != 0); std::thread closeThread(&CPhoneDevice::CloseCamera2, this, pCamera, mPhotoInfo.photoId, turnOffOtg); // closeThread.detach(); m_threadClose.swap(closeThread); } std::string CPhoneDevice::GetFileName() const { return mPath; } void CPhoneDevice::UpdatePosition(double lon, double lat, time_t ts) { if (m_listener != NULL) { return m_listener->OnPositionDataArrived(lon, lat, ts); } } void CPhoneDevice::TurnOnCameraPower(JNIEnv* env) { m_devLocker.lock(); if (mCameraPowerCount == 0) { GpioControl::setCam3V3Enable(true); } mCameraPowerCount++; m_devLocker.unlock(); } void CPhoneDevice::TurnOffCameraPower(JNIEnv* env) { m_devLocker.lock(); if (mCameraPowerCount > 0) { mCameraPowerCount--; if (mCameraPowerCount == 0) { GpioControl::setCam3V3Enable(false); } } m_devLocker.unlock(); } void CPhoneDevice::TurnOnOtg(JNIEnv* env) { m_devLocker.lock(); if (mOtgCount == 0) { ALOGD("setOtgState 1"); GpioControl::setOtgState(true); } mOtgCount++; m_devLocker.unlock(); } void CPhoneDevice::TurnOffOtg(JNIEnv* env) { m_devLocker.lock(); if (mOtgCount > 0) { mOtgCount--; if (mOtgCount == 0) { ALOGD("setOtgState 0"); GpioControl::setOtgState(false); } } m_devLocker.unlock(); } void CPhoneDevice::UpdateSignalLevel(int signalLevel) { m_signalLevel = signalLevel; m_signalLevelUpdateTime = time(NULL); }