实现OSD

serial
Matthew 2 years ago
parent 96f206c213
commit 2d8cb9f1e9

@ -2,22 +2,28 @@ plugins {
id 'com.android.application'
}
def AppVersionName = "1.0.0"
def AppVersionCode = ((1 * 100 + 1) * 100 + 0) * 10 + 0
android {
namespace 'com.xinyingpower.microphoto'
compileSdk 33
defaultConfig {
applicationId "com.xinyingpower.microphoto"
minSdk 26
targetSdk 33
versionCode 1
versionName "1.0"
minSdk 28
//noinspection ExpiredTargetSdkVersion
targetSdk 28
versionCode AppVersionCode
versionName AppVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
externalNativeBuild {
cmake {
cppFlags '-std=c++17'
cppFlags '-std=c++17 -frtti -fexceptions -Wno-error=format-security'
abiFilters 'arm64-v8a'
// setAbiFilters(['arm64-v8a'])
arguments "-DOpenCV_DIR=" + opencvsdk + "/sdk/native/jni"
}
}
}
@ -48,19 +54,23 @@ android {
buildFeatures {
viewBinding true
}
ndkVersion '25.1.8937393'
}
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:2.0.4'
// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.googlecode.mp4parser:isoparser:1.1.21'
// implementation 'com.tencent.mars:mars-core:1.2.5'
// implementation 'com.tencent:mmkv-static:1.3.0'
implementation project(path: ':opencv')
}

@ -1,8 +1,8 @@
package com.xinyingpower.microphoto;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;

@ -2,93 +2,34 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.SET_TIME"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.CHANGE_CONFIGURATION"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.SET_TIME_ZONE"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.SET_TIME" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.SET_TIME_ZONE" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.REBOOT"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" /> <!-- 关机权限 -->
<uses-permission
android:name="android.permission.RECOVERY"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.SHUTDOWN"
tools:ignore="ProtectedPermissions" /> <!-- 添加访问手机状态的权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 添加手机关机的权限 -->
<uses-permission
android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" /> <!-- 接收短信权限 -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.SET_TIME"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.CHANGE_CONFIGURATION"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.SET_TIME_ZONE"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.REBOOT" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" tools:ignore="ProtectedPermissions" /> <!-- 关机权限 -->
<uses-permission android:name="android.permission.RECOVERY" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.SHUTDOWN" tools:ignore="ProtectedPermissions" /> <!-- 添加访问手机状态的权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 添加手机关机的权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" /> <!-- 接收短信权限 -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission
android:name="android.permission.DEVICE_POWER"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.DEVICE_POWER" tools:ignore="ProtectedPermissions" />
<uses-feature android:name="android.hardware.camera" />
<application
android:allowBackup="true"
@ -98,7 +39,7 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MicroPhoto"
tools:targetApi="31">
tools:targetApi="28">
<service
@ -120,9 +61,7 @@
<receiver android:name=".MicroPhotoService$AlarmReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.xinyingpower.mp.ScheduleDetailActivity.AlarmReceiver" />
</intent-filter>
</receiver>
<receiver android:name=".ScreenActionReceiver"

@ -5,15 +5,39 @@
cmake_minimum_required(VERSION 3.22.1)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
# SET_TARGET_PROPERTIES(microphoto PROPERTIES LINK_FLAGS "-Wl,-s,--gc-sections")
add_definitions(-DTERMINAL_CLIENT)
add_definitions(-DKEEP_FRAME_TYPE_ON_REVERSE)
add_definitions(-DBOOST_ALL_NO_LIB)
# set(OpenCV_DIR D:/Workspace/deps/OpenCV-android-sdk/sdk/native/jni/)
set(OPENCV_EXTRA_MODULES_PATH D:/Workspace/Github/opencv_contrib/modules)
# include_directories(${OpenCV_DIR}/include)
add_library( lib_opencv SHARED IMPORTED )
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${OpenCV_DIR}/../libs/${ANDROID_ABI}/libopencv_java4.so)
# Declares and names the project.
project("microphoto")
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
include_directories(${OpenCV_INCLUDE_DIRS})
message(WARNING "OpenCV library status:")
message(WARNING " version: ${OpenCV_VERSION}")
message(WARNING " libraries: ${OpenCV_LIBS}")
message(WARNING " include path: ${OpenCV_INCLUDE_DIRS}")
else(OpenCV_FOUND)
message(FATAL_ERROR "OpenCV library not found")
endif(OpenCV_FOUND)
# include(mars/src/CMakeUtils.txt)
SET(TERM_CORE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../xymp/Core)
@ -206,5 +230,7 @@ target_link_libraries( # Specifies the target library.
camera2ndk
mediandk
lib_opencv
)

@ -238,11 +238,13 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_getPhotoTimeData(
// dataArray.push_back(val);
for (vector<unsigned int>::const_iterator it2 = it->second.cbegin(); it2 != it->second.cend(); ++it2)
{
val = ((unsigned long)channel << 40);
// preset
val |= ((unsigned long)((*it2) & 0xFF)) << 32;
// time
val |= (unsigned long)(((*it2) & 0xFFFFFF00) >> 8);
val = ((unsigned long)((*it2) & 0xFFFFFF00)) << 24;
// channel
val |= ((unsigned long)channel) << 16;
// preset
val |= ((unsigned long)((*it2) & 0xFF)) << 8;
dataArray.push_back((jlong)val);
}
}

@ -21,14 +21,61 @@
#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::CPhoneDevice(JavaVM* vm, jobject service)
{
m_vm = vm;
@ -38,6 +85,7 @@ CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service)
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");
env->DeleteLocalRef(classService);
@ -48,6 +96,7 @@ CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service)
}
m_timerUidFeed = time(NULL);
presentRotation_ = 0;
}
CPhoneDevice::~CPhoneDevice()
@ -67,6 +116,11 @@ void CPhoneDevice::SetListener(IListener* listener)
m_listener = listener;
}
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);
@ -140,7 +194,18 @@ bool CPhoneDevice::FireTimer(timer_uid_t uid)
IDevice::timer_uid_t CPhoneDevice::RegisterHeartbeat(unsigned int timerType, unsigned int timeout)
{
return registerTimer(timerType, 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 string& path)
@ -149,6 +214,10 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const string&
mPhotoInfo = photoInfo;
mPath = path;
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_");
int cameraId = (int)photoInfo.channel - 1;
ACameraIdList *cameraIdList = NULL;
@ -199,7 +268,7 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const string&
LOGI("Failed to open camera device (id: %s)\n", selectedCameraId);
}
camera_status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_PREVIEW,
camera_status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_STILL_CAPTURE/*TEMPLATE_PREVIEW*/,
&captureRequest);
if (camera_status != ACAMERA_OK) {
@ -218,7 +287,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(1920, 1080, AIMAGE_FORMAT_JPEG, 5, &mAImageReader);
status = AImageReader_new(photoInfo.width, photoInfo.height, AIMAGE_FORMAT_YUV_420_888/*AIMAGE_FORMAT_JPEG*/, 5, &mAImageReader);
if (status != AMEDIA_OK)
{
LOGI("AImageReader_new error\n");
@ -273,11 +342,65 @@ ACameraCaptureSession_stateCallbacks* CPhoneDevice::GetSessionListener()
void CPhoneDevice::ImageCallback(AImageReader *reader)
{
bool res = false;
AImage *image = nullptr;
media_status_t status = AImageReader_acquireNextImage(reader, &image);
if (status == AMEDIA_OK && image)
{
bool res = WriteFile(image);
AImageCropRect srcRect;
AImage_getCropRect(image, &srcRect);
int32_t width = srcRect.right - srcRect.left;
int32_t height = srcRect.bottom - srcRect.top;
uint8_t *yPixel = nullptr;
uint8_t *uPixel = nullptr;
uint8_t *vPixel = nullptr;
int32_t yLen = 0;
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);
uint8_t * data = new uint8_t[yLen + vLen + uLen];
memcpy(data, yPixel, yLen);
memcpy(data+yLen, vPixel, vLen);
memcpy(data+yLen+vLen, uPixel, uLen);
cv::Mat mYUV = cv::Mat(height * 1.5, width, CV_8UC1, data);
cv::cvtColor(mYUV, _yuv_rgb_img, cv::COLOR_YUV2RGB_NV21, 3);
// cv::Mat mYUV = cv::Mat(height, yStride, CV_8UC4, data);
cv::cvtColor(mYUV, _yuv_rgb_img, cv::COLOR_YUV2RGB_NV21, 3);
cv::rotate(_yuv_rgb_img, _yuv_rgb_img, cv::ROTATE_90_CLOCKWISE);
// cv::Mat mat = cv::Mat(buffer.height, buffer.stride, CV_8UC4, buffer.bits);
const char *str = "OSD";
putText(_yuv_rgb_img, str, cv::Point(50, 50), cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 0), 4,cv::LINE_AA);
putText(_yuv_rgb_img, str, cv::Point(50, 50), 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(75);
res = cv::imwrite(mPath.c_str(), _yuv_rgb_img, compression_params);
// ANativeWindow_unlockAndPost(theNativeWindow);
if (res)
{
int aa = 0;
}
// bool res = WriteFile(image);
AImage_delete(image);
// delete pThis;
@ -319,7 +442,7 @@ bool CPhoneDevice::WriteFile(AImage *image)
fwrite(data, 1, len, file);
fclose(file);
LOGE("Capture: %s", path.c_str());
LOGI("Capture: %s", path.c_str());
res = true;
@ -342,6 +465,220 @@ std::string CPhoneDevice::GetFileName() const
{
return mPath;
}
/**
* Convert yuv image inside AImage into ANativeWindow_Buffer
* ANativeWindow_Buffer format is guaranteed to be
* WINDOW_FORMAT_RGBX_8888
* WINDOW_FORMAT_RGBA_8888
* @param buf a {@link ANativeWindow_Buffer } instance, destination of
* image conversion
* @param image a {@link AImage} instance, source of image conversion.
* it will be deleted via {@link AImage_delete}
*/
bool CPhoneDevice::DisplayImage(ANativeWindow_Buffer *buf, AImage *image) {
ASSERT(buf->format == WINDOW_FORMAT_RGBX_8888 ||
buf->format == WINDOW_FORMAT_RGBA_8888,
"Not supported buffer format");
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");
switch (presentRotation_) {
case 0:
PresentImage(buf, image);
break;
case 90:
PresentImage90(buf, image);
break;
case 180:
PresentImage180(buf, image);
break;
case 270:
PresentImage270(buf, image);
break;
default:
ASSERT(0, "NOT recognized display rotation: %d", presentRotation_);
}
AImage_delete(image);
image = nullptr;
return true;
}
/*
* PresentImage()
* Converting yuv to RGB
* No rotation: (x,y) --> (x, y)
* Refer to:
* https://mathbits.com/MathBits/TISection/Geometry/Transformations2.htm
*/
void CPhoneDevice::PresentImage(ANativeWindow_Buffer *buf, AImage *image) {
AImageCropRect srcRect;
AImage_getCropRect(image, &srcRect);
AImage_getPlaneRowStride(image, 0, &yStride);
AImage_getPlaneRowStride(image, 1, &uvStride);
yPixel = imageBuffer_;
AImage_getPlaneData(image, 0, &yPixel, &yLen);
vPixel = imageBuffer_ + yLen;
AImage_getPlaneData(image, 1, &vPixel, &vLen);
uPixel = imageBuffer_ + yLen + vLen;
AImage_getPlaneData(image, 2, &uPixel, &uLen);
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
int32_t height = std::min(buf->height, (srcRect.bottom - srcRect.top));
int32_t width = std::min(buf->width, (srcRect.right - srcRect.left));
uint32_t *out = static_cast<uint32_t *>(buf->bits);
for (int32_t y = 0; y < height; y++) {
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
for (int32_t x = 0; x < width; x++) {
const int32_t uv_offset = (x >> 1) * uvPixelStride;
out[x] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
}
out += buf->stride;
}
}
/*
* PresentImage90()
* Converting YUV to RGB
* Rotation image anti-clockwise 90 degree -- (x, y) --> (-y, x)
*/
void CPhoneDevice::PresentImage90(ANativeWindow_Buffer *buf, AImage *image) {
AImageCropRect srcRect;
AImage_getCropRect(image, &srcRect);
AImage_getPlaneRowStride(image, 0, &yStride);
AImage_getPlaneRowStride(image, 1, &uvStride);
yPixel = imageBuffer_;
AImage_getPlaneData(image, 0, &yPixel, &yLen);
vPixel = imageBuffer_ + yLen;
AImage_getPlaneData(image, 1, &vPixel, &vLen);
uPixel = imageBuffer_ + yLen + vLen;
AImage_getPlaneData(image, 2, &uPixel, &uLen);
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
int32_t height = std::min(buf->width, (srcRect.bottom - srcRect.top));
int32_t width = std::min(buf->height, (srcRect.right - srcRect.left));
uint32_t *out = static_cast<uint32_t *>(buf->bits);
out += height - 1;
for (int32_t y = 0; y < height; y++) {
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
for (int32_t x = 0; x < width; x++) {
const int32_t uv_offset = (x >> 1) * uvPixelStride;
// [x, y]--> [-y, x]
int testb = pU[uv_offset];
int testc = pV[uv_offset];
int testA = pY[x];
out[x * buf->stride] = YUV2RGB(testA, testb, testc);
}
out -= 1; // move to the next column
}
}
/*
* PresentImage180()
* Converting yuv to RGB
* Rotate image 180 degree: (x, y) --> (-x, -y)
*/
void CPhoneDevice::PresentImage180(ANativeWindow_Buffer *buf, AImage *image) {
AImageCropRect srcRect;
AImage_getCropRect(image, &srcRect);
AImage_getPlaneRowStride(image, 0, &yStride);
AImage_getPlaneRowStride(image, 1, &uvStride);
yPixel = imageBuffer_;
AImage_getPlaneData(image, 0, &yPixel, &yLen);
vPixel = imageBuffer_ + yLen;
AImage_getPlaneData(image, 1, &vPixel, &vLen);
uPixel = imageBuffer_ + yLen + vLen;
AImage_getPlaneData(image, 2, &uPixel, &uLen);
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
int32_t height = std::min(buf->height, (srcRect.bottom - srcRect.top));
int32_t width = std::min(buf->width, (srcRect.right - srcRect.left));
uint32_t *out = static_cast<uint32_t *>(buf->bits);
out += (height - 1) * buf->stride;
for (int32_t y = 0; y < height; y++) {
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
for (int32_t x = 0; x < width; x++) {
const int32_t uv_offset = (x >> 1) * uvPixelStride;
// mirror image since we are using front camera
out[width - 1 - x] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
// out[x] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
}
out -= buf->stride;
}
}
/*
* PresentImage270()
* Converting image from YUV to RGB
* Rotate Image counter-clockwise 270 degree: (x, y) --> (y, x)
*/
void CPhoneDevice::PresentImage270(ANativeWindow_Buffer *buf, AImage *image) {
AImageCropRect srcRect;
AImage_getCropRect(image, &srcRect);
AImage_getPlaneRowStride(image, 0, &yStride);
AImage_getPlaneRowStride(image, 1, &uvStride);
yPixel = imageBuffer_;
AImage_getPlaneData(image, 0, &yPixel, &yLen);
vPixel = imageBuffer_ + yLen;
AImage_getPlaneData(image, 1, &vPixel, &vLen);
uPixel = imageBuffer_ + yLen + vLen;
AImage_getPlaneData(image, 2, &uPixel, &uLen);
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
int32_t height = std::min(buf->width, (srcRect.bottom - srcRect.top));
int32_t width = std::min(buf->height, (srcRect.right - srcRect.left));
uint32_t *out = static_cast<uint32_t *>(buf->bits);
for (int32_t y = 0; y < height; y++) {
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
for (int32_t x = 0; x < width; x++) {
const int32_t uv_offset = (x >> 1) * uvPixelStride;
int testb = pU[uv_offset];
int testc = pV[uv_offset];
int testA = pY[x];
out[(width - 1 - x) * buf->stride] =
YUV2RGB(testA, testb, testc);
}
out += 1; // move to the next column
}
}
/*
bool CPhoneDevice::SendBroadcastMessage(String16 action, int value)
{

@ -32,6 +32,7 @@ public:
virtual ~CPhoneDevice();
virtual void SetListener(IListener* listener);
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);
virtual timer_uid_t registerTimer(unsigned int timerType, unsigned int timeout);
@ -45,6 +46,13 @@ protected:
bool SendBroadcastMessage(std::string action, int value);
bool DisplayImage(ANativeWindow_Buffer* buf, AImage* image);
void PresentImage(ANativeWindow_Buffer* buf, AImage* image);
void PresentImage90(ANativeWindow_Buffer* buf, AImage* image);
void PresentImage180(ANativeWindow_Buffer* buf, AImage* image);
void PresentImage270(ANativeWindow_Buffer* buf, AImage* image);
static void camera_device_on_disconnected(void *context, ACameraDevice *device);
static void camera_device_on_error(void *context, ACameraDevice *device, int error);
static void capture_session_on_ready(void *context, ACameraCaptureSession *session);
@ -74,6 +82,7 @@ protected:
jobject m_javaService;
jmethodID mRegisterTimerMid;
jmethodID mRegisterHeartbeatMid;
jmethodID mUnregisterTimerMid;
std::string mPath;
@ -95,6 +104,18 @@ protected:
ACameraDevice_StateCallbacks deviceStateCallbacks;
ACameraCaptureSession_stateCallbacks captureSessionStateCallbacks;
int32_t presentRotation_;
int32_t imageHeight_;
int32_t imageWidth_;
uint8_t* imageBuffer_;
int32_t yStride, uvStride;
uint8_t *yPixel, *uPixel, *vPixel;
int32_t yLen, uLen, vLen;
int32_t uvPixelStride;
};

@ -9,8 +9,8 @@ import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
@ -111,7 +111,7 @@ public class MainActivity extends AppCompatActivity {
binding.start.performClick();
}
};
handler.postDelayed(runnable, 2000);
handler.postDelayed(runnable, 1000);
}

@ -10,32 +10,20 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.provider.SyncStateContract;
import android.support.v4.app.NotificationCompat;
import androidx.core.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;
import com.dowse.base.param.pic.ChannelPicParam;
import com.dowse.camera.client.DSCameraManager;
import java.io.File;
import java.io.FileOutputStream;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
@ -89,7 +77,7 @@ public class MicroPhotoService extends Service {
private AlarmManager mAlarmManager;
private NotificationManager mNotificationManager;
private int mHeartbeatDuration = 300000; // 5m: 5 * 60 * 1000
private int mHeartbeatDuration = 0; // 5m: 5 * 60 * 1000
private long mNextHeartbeatTime = 0;
private Map<Long, PendingIntent> mTimers = new HashMap<>();
@ -192,8 +180,8 @@ public class MicroPhotoService extends Service {
for (int idx = 0; idx < cnt; idx++) {
long val = intent.getLongExtra(EXTRA_PARAM_SCHEDULE + idx, 0);
int channel = (int)((val & 0xFF0000000000L) >> 40);
int preset = (int)((val & 0xFF00000000L) >> 32);
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);
}
@ -226,8 +214,13 @@ public class MicroPhotoService extends Service {
}
}
private void updateHeartbeatDuration(int duration) {
private void registerHeartbeatTimer(int duration) {
int orgHeartbeatDuration = mHeartbeatDuration;
mHeartbeatDuration = duration;
if (orgHeartbeatDuration == 0) {
registerHeartbeatTimer();
}
}
private void registerHeartbeatTimer() {
@ -249,8 +242,10 @@ public class MicroPhotoService extends Service {
alarmIntent.setAction(ACTION_TAKE_PHOTO);
int cnt = schedules.size();
alarmIntent.putExtra(EXTRA_PARAM_SCHEDULES, cnt);
String channelStr = "";
for (int idx = 0; idx < cnt; idx++) {
alarmIntent.putExtra(EXTRA_PARAM_SCHEDULE + idx, schedules.get(idx).longValue());
channelStr += schedules.get(idx).toString() + " ";
}
alarmIntent.putExtra(EXTRA_PARAM_TIME, ts);
@ -259,6 +254,10 @@ public class MicroPhotoService extends Service {
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_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);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent);
}
@ -311,7 +310,6 @@ public class MicroPhotoService extends Service {
}
int cnt = photoTimeData.length;
int idx = 0;
short channel = 0;
short preset = 0;
int ts = 0;
@ -320,9 +318,9 @@ public class MicroPhotoService extends Service {
int maxDuration = 35 * 60 * 1000 + 1000;
int currentTs = 0;
List<Long> schedules = new ArrayList<>();
while (true) {
val = photoTimeData[idx++];
ts = (int)(val & 0xFFFFFFFFL);
for (int idx = 0; idx < cnt; idx++) {
val = photoTimeData[idx];
ts = (int)((val & 0x00FFFFFF00000000L) >> 32);
if (ts < baseTime) {
continue;
@ -334,8 +332,8 @@ public class MicroPhotoService extends Service {
if (currentTs == 0) {
currentTs = ts;
channel = (short)((val & 0xFF0000000000L) >> 40);
preset = (short)((val & 0xFF00000000L) >> 32);
channel = (short)((val & 0xFF0000L) >> 16);
preset = (short)((val & 0xFF00L) >> 8);
schedules.add(Long.valueOf(val));
} else if (ts > currentTs) {
@ -346,8 +344,9 @@ public class MicroPhotoService extends Service {
}
if (!schedules.isEmpty()) {
Date date = new Date((startTime + ts) * 1000);
Log.d(TAG, "Register Photo Time: " + date.toString() + " BaseTime=" + baseTime + " ts=" + ts + " startTime=" + startTime);
long expectedTs = startTime + ts;
Date date = new Date(expectedTs * 1000);
// Log.d(TAG, "Register Photo Time: " + date.toString() + " BaseTime=" + baseTime + " ts=" + ts + " startTime=" + startTime + " expectedTs=" + expectedTs);
registerPhotoTimer(channel, preset, currentTs, (currentTs - baseTime) * 1000, schedules);
}
@ -390,14 +389,14 @@ public class MicroPhotoService extends Service {
private void connect() {
// after 10 seconds its connected
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
Log.d(TAG, "Bluetooth Low Energy device is connected!!");
Toast.makeText(getApplicationContext(),"Connected!",Toast.LENGTH_SHORT).show();
stateService = STATE_SERVICE.CONNECTED;
startForeground(NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
}
}, 10000);
new Runnable() {
public void run() {
Log.d(TAG, "Bluetooth Low Energy device is connected!!");
Toast.makeText(getApplicationContext(),"Connected!",Toast.LENGTH_SHORT).show();
stateService = STATE_SERVICE.CONNECTED;
startForeground(NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
}
}, 10000);
}

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@ -83,4 +83,4 @@
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="288dp" />
</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -1,5 +1,18 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.6.10'
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
plugins {
id 'com.android.application' version '7.4.2' apply false
id 'com.android.library' version '7.4.2' apply false
}

@ -15,3 +15,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.useAndroidX=true
android.enableJetifier=true
opencvsdk=D:/Workspace/deps/OpenCV-android-sdk

@ -14,3 +14,6 @@ dependencyResolutionManagement {
}
rootProject.name = "MicroPhoto"
include ':app'
include ':opencv'
project(':opencv').projectDir = new File(opencvsdk + '/sdk')
Loading…
Cancel
Save