You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
TermApp/app/src/main/cpp/PhoneDevice.cpp

1843 lines
55 KiB
C++

#include "PhoneDevice.h"
1 year ago
#include <AndroidHelper.h>
#include <SpecData_JSON.h>
#include <Client/Terminal.h>
#include <Utils.h>
#include <LogThread.h>
#include "ncnn/yolov5ncnn.h"
#include "GPIOControl.h"
#include "CvText.h"
2 years ago
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
1 year ago
2 years ago
#include <opencv2/core/types.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <android/log.h>
#include <android/thermal.h>
#include <sys/system_properties.h>
#include <Mat.h>
#include <filesystem>
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_"
2 years ago
// 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);
}
2 years ago
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);
}
}
void CPhoneDevice::CPhoneCamera::onDisconnected(ACameraDevice* device)
{
if (m_dev != NULL)
{
m_dev->onDisconnected(device);
}
}
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;
mAIInitialized = false;
mHeartbeatStartTime = 0;
mHeartbeatDuration = 0;
m_javaService = NULL;
m_appPath = appPath;
mNetId = netId;
m_signalLevel = 0;
m_signalLevelUpdateTime = time(NULL);
mBuildTime = 0;
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");
}
if (service != NULL)
{
m_javaService = env->NewGlobalRef(service);
jclass classService = env->GetObjectClass(m_javaService);
mRegisterHeartbeatMid = env->GetMethodID(classService, "registerHeartbeatTimer", "(IJ)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", "(IJ)V");
mEnableGpsMid = env->GetMethodID(classService, "enableGps", "(Z)V");
mRequestPositionMid = env->GetMethodID(classService, "requestPosition", "()Z");
env->DeleteLocalRef(classService);
}
if (didAttachThread)
{
vm->DetachCurrentThread();
}
m_timerUidFeed = time(NULL) * 1000;
m_wakelockIdFeed = (unsigned long)m_timerUidFeed;
#ifdef USING_NRSEC
TurnOnCameraPower(env);
GpioControl::setSpiPower(true);
#endif
}
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 (didAttachThread)
{
m_vm->DetachCurrentThread();
}
m_javaService = NULL;
if (m_pRecognizationCfg != NULL)
{
if (mAIInitialized)
{
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
std::string paramFile = m_appPath + (APP_PATH_RECOG_PARAM);
std::string binFile = m_appPath + (APP_PATH_RECOG_BIN);
std::error_code err;
if (!existsFile(paramFile) || !existsFile(binFile) || fs::is_directory(fs::path(paramFile), err) || fs::is_directory(fs::path(binFile), err))
{
XYLOG(XYLOG_SEVERITY_WARNING, "AI Config Files are invalid");
}
else
{
12 months ago
XYLOG(XYLOG_SEVERITY_INFO, "AI Enabled");
ncnn_init();
mAIInitialized = true;
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");
}
}
}
1 year ago
else
{
XYLOG(XYLOG_SEVERITY_WARNING, "AI Disabled");
}
m_pRecognizationCfg = pRecognizationCfg;
}
bool CPhoneDevice::BindNetwork(int sock)
{
return true;
}
1 year ago
bool CPhoneDevice::SelfTest(std::string& result)
{
result.clear();
unsigned int numberOfChannels = 0;
result += "设备自检 版本:" + GetVersion() + NEW_LINE_TAG;
1 year ago
Json::Value appConfig = Json::objectValue;
std::vector<unsigned char> content;
std::string filePath = m_appPath + (APP_DATA_DIR DIR_SEP_STR APP_FILE_NAME_APP_CONF);
if (!readFile(filePath, content))
{
result += ("读取系统配置文件App.json失败" NEW_LINE_TAG);
1 year ago
}
else
{
Json::CharReaderBuilder builder;
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
const char* doc = (const char*)&(content[0]);
if (reader->parse(doc, doc + content.size(), &appConfig, NULL))
{
unsigned int val = 0;
if (GetJSONUInt32Value(appConfig, "channels", val) && (val > 0 && val <= 255))
{
numberOfChannels = val;
result += "通道数:" + std::to_string(numberOfChannels) + NEW_LINE_TAG;
1 year ago
}
else
{
result += "通道数未定义或者无效" NEW_LINE_TAG;
1 year ago
}
}
else
{
result += "解析系统配置文件App.json失败" NEW_LINE_TAG;
1 year ago
}
}
for (unsigned int channel = 1; channel <= numberOfChannels; channel++)
{
std::string path = m_appPath + (APP_PATH_CHANNELS DIR_SEP_STR);
unsigned char cameraId = 0;
unsigned char usbCamera = 0;
1 year ago
Json::Value channelCfg = Json::objectValue;
content.clear();
filePath = m_appPath + (APP_DATA_DIR DIR_SEP_STR APP_FILE_NAME_APP_CONF);
if (!readFile(filePath, content))
{
result += "读取通道" + std::to_string(channel) + "配置文件失败" NEW_LINE_TAG;
1 year ago
}
else
{
Json::CharReaderBuilder builder;
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
const char* doc = (const char*)&(content[0]);
if (reader->parse(doc, doc + content.size(), &channelCfg, NULL))
{
GetJSONUInt8Value(channelCfg, "usbCamera", usbCamera);
1 year ago
if (GetJSONUInt8Value(channelCfg, "cameraId", cameraId))
{
result += "通道" + std::to_string(channel) + " Camera ID为 " + std::to_string(cameraId) + NEW_LINE_TAG;
1 year ago
}
else
{
cameraId = channel - 1;
result += "通道" + std::to_string(channel) + "未定义Camera ID, 使用默认值 " + std::to_string(cameraId) + NEW_LINE_TAG;
1 year ago
}
}
else
{
result += "解析通道" + std::to_string(channel) + "配置文件App.json失败" NEW_LINE_TAG;
1 year ago
}
}
int32_t width = 0;
int32_t height = 0;
NdkCamera::CAMERA_PARAMS params = { 0 };
if (usbCamera)
{
TurnOnOtg(NULL);
}
TurnOnCameraPower(NULL);
1 year ago
NdkCamera camera(width, height, params);
int res = camera.selfTest(std::to_string(cameraId), width, height);
TurnOffCameraPower(NULL);
if (usbCamera)
{
TurnOffOtg(NULL);
}
1 year ago
if (res == 0)
{
result += "通道" + std::to_string(channel) + "正常:最大分辨率:" + std::to_string(width) + "x" + std::to_string(height) + NEW_LINE_TAG;
1 year ago
}
else
{
result += "通道" + std::to_string(channel) + " 异常 err=" + std::to_string(res) + NEW_LINE_TAG;
1 year ago
}
}
int bv = QueryBatteryVoltage(DEFAULT_BATTERY_QUERY_RETRIES);
if (bv > 0)
{
bv -= bv % 100;
result += std::string("电池电压:") + std::to_string(bv / 1000) + std::string(".") + std::to_string((bv % 1000) / 100) + NEW_LINE_TAG;
1 year ago
}
fs::space_info si = fs::space("/data");
double fr = ((double)si.available * 100.0f) / ((double)si.capacity);
1 year ago
result += "可用存储:";
1 year ago
result += std::to_string((int)fr);
result += "%%" NEW_LINE_TAG;
1 year ago
long fm = android_os_Process_getFreeMemory();
long tm = android_os_Process_getTotalMemory();
double fmp = ((double)fm * 100.0f) / ((double)tm);
result += std::string("可用内存:") + std::to_string((int)fmp) + std::string("%%" NEW_LINE_TAG);
1 year ago
if (!m_tfCardPath.empty())
{
fs::space_info si2 = fs::space(m_tfCardPath.c_str());
double fr2 = ((double)si2.available * 100.0f) / ((double)si2.capacity);
result += "TF卡可用空间";
result += std::to_string((int)fr2);
result += "%%" NEW_LINE_TAG;
}
result += "4G信号强度";
result += std::to_string(m_signalLevel);
result += NEW_LINE_TAG;
result += "网络接口:";
std::vector<std::string> devices;
GetNetDevices(devices);
for (auto it = devices.cbegin(); it != devices.cend(); ++it)
{
result += (*it);
result += " ";
}
// result += NEW_LINE_TAG;
1 year ago
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);
}
1 year ago
int CPhoneDevice::QueryBatteryVoltage(int retries)
{
int val = -1; // // BatVol
for (int idx = 0; idx < retries; idx++)
{
val = GpioControl::getBatteryBusVoltage(); // // BatVol
if (val >= 0)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return val;
}
bool CPhoneDevice::QuerySystemProperties(std::map<std::string, std::string>& properties)
{
2 years ago
char value[PROP_VALUE_MAX] = { 0 };
std::map<std::string, std::string> powerInfo;
int res = 0;
int bv = -1;
for (std::map<std::string, std::string>::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)
{
// FOR Protocol
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_VERSION_ABBR))
{
// FOR OSD
1 year ago
string version = GetVersion();
#if 0
version += " " + FormatLocalTime(mBuildTime);
#endif
it->second = version;
}
else if (it->first == PROP_BUILD_TIME)
{
it->second = FormatLocalDateTime(mBuildTime);
}
else if (it->first == PROP_PROD_DATE)
{
__system_property_get("ro.build.date.utc", value);
it->second = value;
}
else if (it->first == PROP_SN || it->first == PROP_BS_ID)
{
__system_property_get("ro.serialno", value);
it->second = value;
}
else if (it->first == PROP_IMEI)
{
if (m_simcard.empty())
{
__system_property_get("phone.imei", value);
it->second = value;
}
else
{
it->second = m_simcard;
}
}
else if (it->first == PROP_OPERATION_TEMP)
{
it->second = QueryCpuTemperature();
}
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_FREE_ROM_PERCENT)
{
fs::space_info si = fs::space("/data");
double fr = ((double)si.available * 100.0f) / ((double)si.capacity);
char buf[12] = { 0 };
snprintf(buf, sizeof(buf), "%d%%", (int)fr);
it->second = buf;
}
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)
{
it->second = std::to_string(android_os_Process_getFreeMemory()); // Unit: M
}
else if (it->first == PROP_FREE_MEMORY_PERCENT)
{
long fm = android_os_Process_getFreeMemory();
long tm = android_os_Process_getTotalMemory();
double fmp = ((double)fm * 100.0f) / ((double)tm);
char buf[12] = { 0 };
snprintf(buf, sizeof(buf), "%d%%", (int)fmp);
it->second = buf; // Unit: M
}
else if (it->first == PROP_TOTAL_MEMORY)
{
it->second = std::to_string(android_os_Process_getTotalMemory()); // Unit: M
}
1 year ago
else if (it->first == (PROP_LIGHTDEPENDENT_RESISTOR))
{
int val = GpioControl::getLightAdc();
it->second = std::to_string(val);
}
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());
}
1 year ago
else if (it->first == (PROP_CHARGING_BUS_VOL) || it->first == (PROP_CHARGING_VOLTAGE))
{
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_POWER))
{
it->second = std::to_string(GpioControl::getBatteryPower());
}
else if (it->first == (PROP_BATTERY_BUS_VOL) || it->first == (PROP_BATTERY_VOLTAGE))
{
1 year ago
int val = QueryBatteryVoltage(DEFAULT_BATTERY_QUERY_RETRIES); // // BatVol
if (val > 0)
{
bv = val;
char str[32] = { 0 };
snprintf(str, sizeof(str), "%.1f", val / 1000.0);
it->second = str;
}
else
{
#ifdef _DEBUG
int aa = 0;
#endif
}
}
1 year ago
else if ((it->first == (PROP_SIGNAL_4G)) || (it->first == (PROP_SIGNAL_2G)) || (it->first == (PROP_SIGNAL_LEVEL)))
{
it->second = std::to_string(m_signalLevel);
}
1 year ago
/*
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;
}
}
1 year ago
*/
}
2 years ago
std::map<std::string, std::string>::iterator it = properties.find(PROP_BATTERY_CURRENT);
if (it != properties.end())
{
if (bv == -1)
{
1 year ago
bv = QueryBatteryVoltage(DEFAULT_BATTERY_QUERY_RETRIES);
}
if (bv > 0)
{
char str[32] = { 0 };
float batteryCurrent = STANDARD_CURRENT_64V / ((float)bv / 1000.0f / STANDARD_VOLTAGE_64V);
snprintf(str, sizeof(str), "%d", (int)batteryCurrent);
it->second = str;
}
}
// __system_property_get("ro.telephony.default_network", value);
2 years ago
return true;
}
2 years ago
std::string CPhoneDevice::QueryCpuTemperature()
{
// /sys/devices/virtual/thermal/thermal_zone0/temp
std::vector<unsigned char> data;
// /sys/class/thermal/thermal zone*/temp
if (readFile("/sys/class/thermal/thermal_zone3/temp", data) && !data.empty())
{
data.push_back(0);
int temp = atoi((const char*)(&data[0]));
return std::to_string((temp / 1000) + 20);
}
2 years ago
return "";
}
void CPhoneDevice::QueryPowerInfo(std::map<std::string, std::string>& 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<std::string, std::string> queries = parseQuery(str);
powerInfo.swap(queries);
}
}
bool CPhoneDevice::GetNextScheduleItem(uint32_t tsBasedZero, uint32_t scheduleTime, vector<uint32_t>& 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->CallBooleanMethod(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)
2 years ago
{
if (resetType == REBOOT_TYPE_DEVICE)
{
// reboot the device
std::thread t([]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
GpioControl::reboot();
});
t.detach();
}
else
{
long timeout = 1000;
RestartApp(resetType, timeout);
}
return true;
}
void CPhoneDevice::RestartApp(int resetType, long timeout)
{
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, timeout);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
}
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;
}
float CPhoneDevice::QueryBattaryVoltage(int timesForAvg, bool* isCharging)
{
if (timesForAvg <= 0)
{
return 0.0f;
}
int val = 0;
int totalVals = 0;
float chargingBusVoltage = 0.0f;
for (int idx = 0; idx < timesForAvg; idx++)
{
val = GpioControl::getChargingBusVoltage();
if (val > 1000)
{
chargingBusVoltage = (float)val / 1000.0;
break;
}
}
if (isCharging != NULL)
{
*isCharging = chargingBusVoltage > DEFAULT_WARNING_CHARGING_BUS_VOL;
}
int matched = 0;
for (int idx = 0; idx < timesForAvg; idx++)
{
val = GpioControl::getBatteryVoltage(); // // BatVol
if (val > 0)
{
totalVals += val > BATTARY_VOLTAGE_MAX ? BATTARY_VOLTAGE_MAX : val;
matched++;
}
}
return (matched > 0) ? ((float)totalVals / 1000.0 / matched) : 0;
}
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);
2 years ago
}
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);
}
}
}
void CPhoneDevice::handleRebootTimer(union sigval v)
{
CPhoneDevice* pDevice = (CPhoneDevice*)(v.sival_ptr);
// Reboot APP
XYLOG(XYLOG_SEVERITY_ERROR, "Camera Close Thread is DEAD, will RESTART app");
pDevice->RestartApp(0, 2000);
}
// void CPhoneDevice::handleRebootTimerImpl()
// {
// }
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;
context->times = 0;
context->uid = 0;
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*>((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<IDevice::timer_uid_t, TIMER_CONTEXT*>::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, time_t tsForNextPhoto)
{
mHeartbeatStartTime = time(NULL);
mHeartbeatDuration = timeout;
2 years ago
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;
}
#ifdef ALIGN_HB_TIMER_TO_PHOTO
env->CallVoidMethod(m_javaService, mRegisterHeartbeatMid, (jint)timeout, (jlong)tsForNextPhoto);
#else
env->CallVoidMethod(m_javaService, mRegisterHeartbeatMid, (jint)timeout, 0);
#endif
if (didAttachThread)
2 years ago
{
m_vm->DetachCurrentThread();
}
return uid;
}
bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector<OSD_INFO>& osds, const std::string& path)
{
if (photoInfo.width == 0 || photoInfo.height == 0)
{
XYLOG(XYLOG_SEVERITY_ERROR, "TP: Invalid Size: (%u-%u) PHOTOID=%u", (unsigned int)photoInfo.width, (unsigned int)photoInfo.height, photoInfo.photoId);
return false;
}
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);
struct sigevent evp = { 0 };
struct itimerspec ts = { 0 };
timer_t timer;
int ret;
evp.sigev_value.sival_ptr = this;
evp.sigev_notify = SIGEV_THREAD; //SIGEV_THREAD_ID;
evp.sigev_notify_function = CPhoneDevice::handleRebootTimer;
// evp.sigev_notify_thread_id = gettid();
// evp.sigev_notify = SIGEV_SIGNAL;
// evp.sigev_signo = SIGUSR2;
ret = timer_create(CLOCK_REALTIME, &evp, &timer);
if( ret == 0)
{
ts.it_value.tv_sec = 8; // 8 seconds
ts.it_value.tv_nsec = 0;
ret = timer_settime(timer, 0, &ts, NULL);
}
m_threadClose.join();
timer_delete(timer);
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;
memset(&params, 0, sizeof(params));
params.sceneMode = mPhotoInfo.sceneMode;
params.autoFocus = mPhotoInfo.autoFocus;
params.autoExposure = mPhotoInfo.autoExposure;
params.focusTimeout = mPhotoInfo.focusTimeout * 1000;
params.exposureTime = mPhotoInfo.exposureTime;
1 year ago
params.sensitivity = mPhotoInfo.sensitivity;
params.compensation = mPhotoInfo.compensation;
params.orientation = mPhotoInfo.orientation;
params.zoom = mPhotoInfo.zoom;
params.zoomRatio = mPhotoInfo.zoomRatio;
11 months ago
params.requestTemplate = mPhotoInfo.requestTemplate;
params.awbMode = mPhotoInfo.awbMode;
params.wait3ALocked = mPhotoInfo.wait3ALocked;
if (params.requestTemplate <= 0 || params.requestTemplate > 5)
{
params.requestTemplate = 2;
}
#if 0
1 year ago
if (photoInfo.ldrEnabled)
{
if (GpioControl::getLightAdc() > 1400)
{
params.autoExposure = 0;
params.exposureTime = 1200000000;
1 year ago
params.sensitivity = 1200;
}
}
#endif
// 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<OSD_INFO>::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);
1 year ago
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<cv::ft::FreeType2> ft2, cv::Mat& mat, const std::string& str, cv::Point startPoint, int fontSize, cv::Scalar clr, int thickness)
{
std::vector<std::string> lines = split(str, "\n");
int lineHeight = 0;
cv::Point pt = startPoint;
cv::Size textSize;
int baseline = 0;
for (std::vector<std::string>::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;
}
time_t takingTime = time(NULL);
if (mPhotoInfo.remedy != 0)
{
if ((takingTime - mPhotoInfo.scheduleTime) > 30)
{
takingTime = mPhotoInfo.scheduleTime + mPhotoInfo.channel * 2;
}
}
mPhotoInfo.photoTime = takingTime;
int baseline = 0;
cv::Size textSize;
double height = mat.size().height;
double width = mat.size().width;
2 years ago
// double ratio = std::min(height / 1024, width / 1920);
double ratio = height / 1024.0;
int thickness = round(1.4 * ratio);
if (thickness < 1) thickness = 1;
else if (thickness > 5) thickness = 5;
11 months ago
cv::Scalar scalarWhite(255, 255, 255); // white
int fontSize = (int)(28.0 * ratio);
cv::Point pt;
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<cv::ft::FreeType2> 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<IDevice::RECOG_OBJECT> objs;
if ((m_pRecognizationCfg != NULL) && (m_pRecognizationCfg->enabled != 0) && (mPhotoInfo.recognization != 0))
{
1 year ago
XYLOG(XYLOG_SEVERITY_INFO, "Channel AI Enabled");
// 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);
cv::Scalar textColor(m_pRecognizationCfg->textColor & 0xFF, (m_pRecognizationCfg->textColor & 0xFF00) >> 8, (m_pRecognizationCfg->textColor & 0xFF0000) >> 16);
for (std::vector<IDevice::RECOG_OBJECT>::const_iterator it = objs.cbegin(); it != objs.cend();)
{
if (it->label >= m_pRecognizationCfg->items.size())
{
it = objs.erase(it);
continue;
}
const IDevice::CFG_RECOGNIZATION::ITEM& item = m_pRecognizationCfg->items[it->label];
if (item.enabled == 0 || it->prob < item.prob)
{
it = objs.erase(it);
continue;
}
if ((mPhotoInfo.recognization & 0x2) != 0)
{
cv::Rect rc(it->x, it->y, it->w, it->h);
cv::rectangle(mat, rc, borderColor, m_pRecognizationCfg->thickness);
textSize = ft2->getTextSize(item.name, fontSize, thickness, &baseline);
textSize.height += baseline;
if (it->y > textSize.height)
{
pt.y = it->y - textSize.height - 4 - m_pRecognizationCfg->thickness;
}
else if (mat.rows - it->y - it->h > textSize.height)
{
pt.y = it->y + it->h + 4 + m_pRecognizationCfg->thickness;
}
else
{
// Inner
pt.y = it->y + 4 + m_pRecognizationCfg->thickness;
}
if (mat.cols - it->x > textSize.width)
{
pt.x = it->x;
}
else
{
pt.x = it->x + it->w - textSize.width;
}
1 year ago
#ifdef OUTPUT_CAMERA_DBG_INFO
char buf[128];
1 year ago
snprintf(buf, sizeof(buf), "AI: %d=%s (%f,%f)-(%f,%f) Text:(%d,%d)-(%d,%d)",
it->label, item.name.c_str(), it->x, it->y, it->w, it->h, pt.x, pt.y, textSize.width, textSize.height);
1 year ago
XYLOG(XYLOG_SEVERITY_DEBUG, buf);
#endif
1 year ago
ft2->putText(mat, item.name + std::to_string((int)(it->prob * 100.0)) + "%", pt, fontSize, textColor, thickness, cv::LINE_AA, false, true);
}
++it;
}
}
}
1 year ago
else
{
XYLOG(XYLOG_SEVERITY_WARNING, "Channel AI Disabled");
}
#ifdef OUTPUT_CAMERA_DBG_INFO
cv::Scalar scalarRed(0, 0, 255); // red
1 year ago
NdkCamera::CAPTURE_RESULT captureResult = mCamera->getCaptureResult();
#if 0
if (captureResult.avgY < 25 && mPhotoInfo.autoExposure != 0)
{
// Take another photo
CPhoneDevice* pThis = this;
std::string path = mPath;
IDevice::PHOTO_INFO photoInfo = mPhotoInfo;
std::vector<IDevice::OSD_INFO> osds = mOsds;
photoInfo.photoId += 1;
photoInfo.autoExposure = 0;
if (captureResult.avgY == 0)
{
photoInfo.exposureTime = 600000000;
photoInfo.sensitivity = 2500;
}
else if (captureResult.avgY <= 6)
{
photoInfo.exposureTime = captureResult.exposureTime * 150 / captureResult.avgY;
photoInfo.sensitivity = photoInfo.sensitivity * 80 / captureResult.avgY;
if (photoInfo.sensitivity < captureResult.sensitivity)
{
photoInfo.sensitivity = captureResult.sensitivity;
}
else if (photoInfo.sensitivity > 3000)
{
photoInfo.sensitivity = 3000;
}
}
else
{
photoInfo.exposureTime = captureResult.exposureTime * 120 / captureResult.avgY;
photoInfo.sensitivity = photoInfo.sensitivity * 60 / captureResult.avgY;
if (photoInfo.sensitivity < captureResult.sensitivity)
{
photoInfo.sensitivity = captureResult.sensitivity;
}
else if (photoInfo.sensitivity > 3000)
{
photoInfo.sensitivity = 3000;
}
}
std::thread t([=]
{
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
pThis->TakePhoto(photoInfo, osds, path);
});
t.detach();
}
#endif // 0
char extimeunit[4] = { 0 };
unsigned int extime = (captureResult.exposureTime >= 1000000) ? ((unsigned int)(captureResult.exposureTime / 1000000)) : ((unsigned int)(captureResult.exposureTime / 1000));
strcpy(extimeunit, (captureResult.exposureTime >= 1000000) ? "ms" : "μs");
char str[128] = { 0 };
11 months ago
snprintf(str, sizeof(str), "AE=%u AF=%u EXPS=%u%s(%d) ISO=%d AFS=%u AES=%u AWBS=%u SCENE=%d LDR=%d(%u) %0.1fx T=%u FD=%lld",
captureResult.autoExposure, captureResult.autoFocus,
extime, extimeunit, captureResult.compensation, captureResult.sensitivity,
// isnan(captureResult.FocusDistance) ? 0 : captureResult.FocusDistance,
11 months ago
(unsigned int)captureResult.afState, (unsigned int)captureResult.aeState, captureResult.awbState,
captureResult.sceneMode, GpioControl::getLightAdc(), (unsigned int)captureResult.avgY, captureResult.zoomRatio,
(uint32_t)captureResult.duration, captureResult.frameDuration);
11 months ago
// cv::putText(mat, str, cv::Point(0, mat.rows - 20), cv::FONT_HERSHEY_COMPLEX, fontScale, scalarWhite, thickness1, cv::LINE_AA);
int fs = fontSize * 2 / 3;
textSize = ft2->getTextSize(str, fs, -1, &baseline);
cv::Point lt(0, mat.rows - fs - 20 * ratio);
11 months ago
cv::Point lt2(0, lt.y - 2 * ratio);
cv::Point rb(0 + textSize.width, lt2.y + textSize.height + 8 * ratio);
11 months ago
if (rb.x > (int)width - 1)
{
rb.x = (int)width - 1;
}
if (rb.y > (int)height - 1)
{
rb.y = (int)height - 1;
}
cv::Mat roi = mat(cv::Rect(lt2, rb));
11 months ago
cv::Mat clrMat(roi.size(), CV_8UC3, scalarWhite);
11 months ago
double alpha = 0.2;
cv::addWeighted(clrMat, alpha, roi, 1.0 - alpha, 0.0, roi);
// cv::rectangle(mat, lt2, rb,cv::Scalar(255, 255, 255), -1);
ft2->putText(mat, str, lt, fs, scalarRed, -1, cv::LINE_AA, false);
11 months ago
// DrawOutlineText(ft2, mat, str, cv::Point(0, mat.rows - fs - 20 * ratio), fs, scalarWhite, 1);
#endif // OUTPUT_CAMERA_DBG_INFO
for (vector<OSD_INFO>::const_iterator it = mOsds.cbegin(); it != mOsds.cend(); ++it)
{
2 years ago
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);
11 months ago
DrawOutlineText(ft2, mat, it->text, pt, fontSize, scalarWhite, thickness);
}
vector <int> params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(mPhotoInfo.quality);
bool res = false;
std::string fullPath = endsWith(mPath, ".jpg") ? mPath : (mPath + CTerminal::BuildPhotoFileName(mPhotoInfo));
11 months ago
#ifdef OUTPUT_CAMERA_DBG_INFO
bool shouldRetry = false;
if (captureResult.avgY > 245 || captureResult.avgY < 10)
{
if (mPhotoInfo.retries < (DEFAULT_TAKE_PHOTO_RETRIES - 1))
{
shouldRetry = true;
replaceAll(fullPath, ".jpg", std::string("-") + std::to_string(mPhotoInfo.retries) + ".jpg");
replaceAll(fullPath, "/photos/", "/sentPhotos/");
XYLOG(XYLOG_SEVERITY_ERROR, "Photo is TOO dark or light(LDR=%u), will RETRY it", (uint32_t)captureResult.avgY);
}
}
#endif // OUTPUT_CAMERA_DBG_INFO
if (!std::filesystem::exists(std::filesystem::path(fullPath)))
{
bool res = cv::imwrite(fullPath.c_str(), mat, params);
if (!res)
{
1 year ago
XYLOG(XYLOG_SEVERITY_ERROR, "Failed to write photo: %s", fullPath.c_str());
}
else
{
1 year ago
XYLOG(XYLOG_SEVERITY_INFO, "Succeeded to write photo: %s", fullPath.c_str());
}
11 months ago
#ifdef OUTPUT_CAMERA_DBG_INFO
if (shouldRetry)
{
TakePhotoCb(false, mPhotoInfo, fullPath, takingTime, objs);
}
else
{
TakePhotoCb(res, mPhotoInfo, fullPath, takingTime, objs);
}
#else
TakePhotoCb(res, mPhotoInfo, fullPath, takingTime, objs);
11 months ago
#endif
}
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<IDevice::RECOG_OBJECT> 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);
}
void CPhoneDevice::onDisconnected(ACameraDevice* device)
{
if (mCamera == NULL)
{
return;
}
XYLOG(XYLOG_SEVERITY_ERROR, "Failed to Take Photo (IMGID=%u) as for Disconnection", mPhotoInfo.photoId);
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;
}
2 years ago
1 year ago
std::string CPhoneDevice::GetVersion() const
{
// FOR OSD
string version = std::to_string(mVersionCode / 100000);
version += ".";
version += std::to_string((mVersionCode % 100000) / 1000);
version += ".";
version += std::to_string(mVersionCode % 1000);
return version;
}
void CPhoneDevice::UpdatePosition(double lon, double lat, double radius, time_t ts)
{
if (m_listener != NULL)
{
return m_listener->OnPositionDataArrived(lon, lat, radius, ts);
}
1 year ago
}
void CPhoneDevice::TurnOnCameraPower(JNIEnv* env)
{
m_devLocker.lock();
1 year ago
if (mCameraPowerCount == 0)
{
GpioControl::setCam3V3Enable(true);
1 year ago
}
mCameraPowerCount++;
m_devLocker.unlock();
1 year ago
}
void CPhoneDevice::TurnOffCameraPower(JNIEnv* env)
{
m_devLocker.lock();
1 year ago
if (mCameraPowerCount > 0)
{
mCameraPowerCount--;
if (mCameraPowerCount == 0)
{
GpioControl::setCam3V3Enable(false);
1 year ago
}
}
m_devLocker.unlock();
1 year ago
}
void CPhoneDevice::TurnOnOtg(JNIEnv* env)
{
m_devLocker.lock();
1 year ago
if (mOtgCount == 0)
{
ALOGD("setOtgState 1");
GpioControl::setOtgState(true);
1 year ago
}
mOtgCount++;
m_devLocker.unlock();
1 year ago
}
void CPhoneDevice::TurnOffOtg(JNIEnv* env)
{
m_devLocker.lock();
1 year ago
if (mOtgCount > 0)
{
mOtgCount--;
if (mOtgCount == 0)
{
ALOGD("setOtgState 0");
GpioControl::setOtgState(false);
1 year ago
}
}
m_devLocker.unlock();
}
void CPhoneDevice::UpdateSignalLevel(int signalLevel)
{
m_signalLevel = signalLevel;
m_signalLevelUpdateTime = time(NULL);
}
void CPhoneDevice::UpdateSimcard(const std::string& simcard)
{
m_simcard = simcard;
}