|
|
|
#include "TerminalDevice.h"
|
|
|
|
/*
|
|
|
|
* Copyright 2018 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define LOG_TAG "CameraTestHelpers"
|
|
|
|
|
|
|
|
#include "PhoneDevice.h"
|
|
|
|
#include "TermClient.h"
|
|
|
|
|
|
|
|
#include <opencv2/opencv.hpp>
|
|
|
|
#include <opencv2/core.hpp>
|
|
|
|
#include <opencv2/imgproc.hpp>
|
|
|
|
// #include <opencv2/objdetect.hpp>
|
|
|
|
// #include <opencv2/features2d.hpp>
|
|
|
|
|
|
|
|
#include <opencv2/core/types.hpp>
|
|
|
|
#include <opencv2/core/core.hpp>
|
|
|
|
#include <opencv2/imgproc/imgproc.hpp>
|
|
|
|
|
|
|
|
#include <android/log.h>
|
|
|
|
|
|
|
|
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
|
|
|
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
|
|
|
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
|
|
|
#define ASSERT(cond, fmt, ...) \
|
|
|
|
if (!(cond)) { \
|
|
|
|
__android_log_assert(#cond, LOG_TAG, fmt, ##__VA_ARGS__); \
|
|
|
|
}
|
|
|
|
|
|
|
|
extern bool GetJniEnv(JavaVM *vm, JNIEnv **env);
|
|
|
|
|
|
|
|
|
|
|
|
// 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 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) : NdkCamera(width, height), m_dev(dev)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPhoneDevice::CPhoneCamera::on_image(const cv::Mat& rgb) const
|
|
|
|
{
|
|
|
|
if (m_dev != NULL)
|
|
|
|
{
|
|
|
|
m_dev->OnImageReady(rgb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service)
|
|
|
|
{
|
|
|
|
m_vm = vm;
|
|
|
|
JNIEnv* env = NULL;
|
|
|
|
bool attached = GetJniEnv(m_vm, &env);
|
|
|
|
m_javaService = env->NewGlobalRef(service);
|
|
|
|
|
|
|
|
mHeartbeatStartTime = 0;
|
|
|
|
mHeartbeatDuration = 0;
|
|
|
|
|
|
|
|
jclass classService = env->GetObjectClass(m_javaService);
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (attached)
|
|
|
|
{
|
|
|
|
vm->DetachCurrentThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_timerUidFeed = time(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
CPhoneDevice::~CPhoneDevice()
|
|
|
|
{
|
|
|
|
JNIEnv* env = NULL;
|
|
|
|
bool attached = GetJniEnv(m_vm, &env);
|
|
|
|
env->DeleteGlobalRef(m_javaService);
|
|
|
|
if (attached)
|
|
|
|
{
|
|
|
|
m_vm->DetachCurrentThread();
|
|
|
|
}
|
|
|
|
m_javaService = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 GetNextScheduleItem(uint32_t tsBasedZero, uint32_t scheduleTime, vector<uint32_t>& items)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPhoneDevice::Reboot()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
IDevice::timer_uid_t CPhoneDevice::RegisterTimer(unsigned int timerType, unsigned int timeout)
|
|
|
|
{
|
|
|
|
IDevice::timer_uid_t uid = m_timerUidFeed.fetch_add(1);
|
|
|
|
|
|
|
|
ALOGI("NDK RegTimer: uid=%lld Type=%u timeout=%u", uid, timerType, timeout);
|
|
|
|
|
|
|
|
JNIEnv* env = NULL;
|
|
|
|
jboolean ret = JNI_FALSE;
|
|
|
|
bool attached = GetJniEnv(m_vm, &env);
|
|
|
|
if (attached)
|
|
|
|
{
|
|
|
|
ret = env->CallBooleanMethod(m_javaService, mRegisterTimerMid, (jlong)uid, (jint)timeout);
|
|
|
|
m_vm->DetachCurrentThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == JNI_TRUE)
|
|
|
|
{
|
|
|
|
unsigned long val = timerType;
|
|
|
|
mTimers.insert(mTimers.end(), std::pair<IDevice::timer_uid_t, unsigned long>(uid, val));
|
|
|
|
return uid;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPhoneDevice::UnregisterTimer(IDevice::timer_uid_t uid)
|
|
|
|
{
|
|
|
|
JNIEnv* env = NULL;
|
|
|
|
jboolean ret = JNI_FALSE;
|
|
|
|
bool attached = GetJniEnv(m_vm, &env);
|
|
|
|
if (attached)
|
|
|
|
{
|
|
|
|
ret = env->CallBooleanMethod(m_javaService, mUnregisterTimerMid, (jlong)uid);
|
|
|
|
m_vm->DetachCurrentThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == JNI_TRUE)
|
|
|
|
{
|
|
|
|
mTimers.erase(uid);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPhoneDevice::FireTimer(timer_uid_t uid)
|
|
|
|
{
|
|
|
|
std::map<IDevice::timer_uid_t, unsigned long>::iterator it = mTimers.find(uid);
|
|
|
|
if (it == mTimers.end())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long timerType = it->second & 0xFFFFFFFF;
|
|
|
|
unsigned long times = (it->second & 0xFFFFFFFF00000000) >> 32;
|
|
|
|
times++;
|
|
|
|
|
|
|
|
if (timerType != 100)
|
|
|
|
{
|
|
|
|
int aa = 0;
|
|
|
|
}
|
|
|
|
it->second = timerType | (times << 32);
|
|
|
|
|
|
|
|
if (m_listener == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_listener->OnTimeout(uid, timerType, times);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
JNIEnv* env = NULL;
|
|
|
|
jboolean ret = JNI_FALSE;
|
|
|
|
bool attached = GetJniEnv(m_vm, &env);
|
|
|
|
if (attached)
|
|
|
|
{
|
|
|
|
env->CallVoidMethod(m_javaService, mRegisterHeartbeatMid, (jint)timeout);
|
|
|
|
m_vm->DetachCurrentThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
return uid;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector<OSD_INFO>& osds, const string& path)
|
|
|
|
{
|
|
|
|
LOGI("TAKE_PHOTO: CH=%u PR=%u\n", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset);
|
|
|
|
mPhotoInfo = photoInfo;
|
|
|
|
mPath = path;
|
|
|
|
mOsds = osds;
|
|
|
|
|
|
|
|
mCamera = new CPhoneCamera(this, photoInfo.width, photoInfo.height);
|
|
|
|
mCamera->open(to_string(photoInfo.channel - 1).c_str());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPhoneDevice::OnImageReady(const cv::Mat& mat) const
|
|
|
|
{
|
|
|
|
|
|
|
|
int baseline = 0;
|
|
|
|
cv::Size textSize, textSize2;
|
|
|
|
for (vector<OSD_INFO>::const_iterator it = mOsds.cbegin(); it != mOsds.cend(); ++it)
|
|
|
|
{
|
|
|
|
// getTextSize(value, font, scale, 1, &bottom);
|
|
|
|
textSize = cv::getTextSize(it->text, cv::FONT_HERSHEY_COMPLEX, 1, 4, &baseline);
|
|
|
|
cv::Point pt(it->x, it->y + textSize.height);
|
|
|
|
putText(mat, it->text, pt, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 0), 4,cv::LINE_AA);
|
|
|
|
textSize2 = cv::getTextSize(it->text, cv::FONT_HERSHEY_COMPLEX, 1, 2, &baseline);
|
|
|
|
pt.y -= (textSize.height - textSize2.height) / 2;
|
|
|
|
putText(mat, it->text, pt, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 255, 255), 2,cv::LINE_AA);
|
|
|
|
}
|
|
|
|
|
|
|
|
vector <int> compression_params;
|
|
|
|
compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
|
|
|
|
compression_params.push_back(80);
|
|
|
|
|
|
|
|
bool res = cv::imwrite(mPath.c_str(), mat, compression_params);
|
|
|
|
|
|
|
|
TakePhotoCb(res, mPhotoInfo, mPath, time(NULL));
|
|
|
|
|
|
|
|
delete mCamera;
|
|
|
|
mCamera = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CPhoneDevice::GetFileName() const
|
|
|
|
{
|
|
|
|
return mPath;
|
|
|
|
}
|
|
|
|
|