Merge branch 'dev_robotaxi-p-app_254_220228_2.5.4' into 'test_robobus-d-app-module_252_220215_2.5.2.1'

# Conflicts:
#   test/crashreport-apmbyte/src/main/java/com/mogo/test/crashreport/apm/ApmCrashReportProvider.java
This commit is contained in:
pangfan
2022-03-01 08:28:25 +00:00
322 changed files with 38227 additions and 639 deletions

View File

@@ -24,6 +24,7 @@ import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotPlanningListenerManager;
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr;
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast;
@@ -134,7 +135,7 @@ public class MogoOCHTaxiModelNew {
if (NetworkUtils.isConnected(mContext)) {
// startOrStopOrderLoop(mOCHCarStatus == 1);
if (FunctionBuildConfig.appIdentityMode == 0x00) {
if (AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)) {
queryCarStatus();
}
}
@@ -821,7 +822,7 @@ public class MogoOCHTaxiModelNew {
Logger.d( TAG, "onIntentReceived = %s", intentStr );
if ( ConnectivityManager.CONNECTIVITY_ACTION.equals( intentStr ) ) {
if ( NetworkUtils.isConnected( mContext ) ) {
if (FunctionBuildConfig.appIdentityMode == 0x00) {
if (AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)) {
startOrStopOrderLoop(mOCHCarStatus == 1);
queryCarStatus();
}
@@ -941,7 +942,7 @@ public class MogoOCHTaxiModelNew {
mPrevAPStatus = state;
if (FunctionBuildConfig.isDemoMode
&& FunctionBuildConfig.appIdentityMode == 0x01) {
&& AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)) {
// 当美化模式演示模式开启时且是乘客app、且未到终点时维持自动驾驶icon开启状态
if (!arriveAtEnd) {
return;
@@ -955,7 +956,7 @@ public class MogoOCHTaxiModelNew {
mPrevAPStatus = state;
if (FunctionBuildConfig.isDemoMode
&& FunctionBuildConfig.appIdentityMode == 0x01) {
&& AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)) {
// 当美化模式演示模式开启时且是乘客app、且未到终点时维持自动驾驶icon开启状态
if (!arriveAtEnd) {
return;
@@ -981,7 +982,7 @@ public class MogoOCHTaxiModelNew {
@Override
public void onAutopilotArriveAtStation(@Nullable AutopilotStationInfo data) {
if (FunctionBuildConfig.isDemoMode
&& FunctionBuildConfig.appIdentityMode == 0x01) {
&& AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)) {
arriveAtEnd = true;
}

View File

@@ -30,6 +30,7 @@ import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager;
import com.mogo.eagle.core.function.call.map.CallerSmpManager;
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.map.listener.IMogoMapListener;
import com.mogo.map.uicontroller.VisualAngleMode;
@@ -133,7 +134,7 @@ public abstract class BaseOchTaxiTabFragment<V extends IView, P extends Presente
@Override
public void onClickImpl(View v) {
// 如果能自动驾驶,就自动驾驶,不能就提示
if (FunctionBuildConfig.appIdentityMode == 0x00) {
if (AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)) {
restartAutopilot();
}
// TODO: 2021/11/27 通过开启结果更新ui
@@ -220,7 +221,7 @@ public abstract class BaseOchTaxiTabFragment<V extends IView, P extends Presente
showNaviToStartStationFragment(false);
});
if (FunctionBuildConfig.appIdentityMode == 0x01) {
if (AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)) {
flStationPanelContainer.setVisibility(View.GONE);
tvOperationStatus.setVisibility(View.GONE);
mSettingBtn.setVisibility(View.GONE);

View File

@@ -12,6 +12,7 @@ import androidx.fragment.app.FragmentTransaction;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.eagle.core.data.autopilot.AutopilotRouteInfo;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.och.taxi.passenger.R;
@@ -85,7 +86,7 @@ public class OCHTaxiFragment extends BaseOchTaxiTabFragment<OCHTaxiFragment, OCH
switchVRFlatMode(false);
}
if (FunctionBuildConfig.appIdentityMode == 0x00) {
if (AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)) {
tvOperationStatus.setVisibility(View.VISIBLE);
} else {
tvOperationStatus.setVisibility(View.GONE);

21
ZD_README/README_Utils.md Normal file
View File

@@ -0,0 +1,21 @@
### 项目说明
* ### 应用身份 相关 -> [AppIdentityModeUtils.java][AppIdentityModeUtils.java] -> [Demo][AppIdentityModeUtils.java]
```
// 判断是否是 司机身份
AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)
// 判断是否是 乘客身份
AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)
// 判断是否是 小巴车
AppIdentityModeUtils.isBus(FunctionBuildConfig.appIdentityMode)
// 判断是否是 出租车
AppIdentityModeUtils.isTaxi(FunctionBuildConfig.appIdentityMode)
```

View File

@@ -5,9 +5,6 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'android-aspectjx'
apply plugin: 'bugly'
if (!isAndroidTestBuild()) {
apply plugin: 'apm-plugin'
}
//apply ByteX宿主
if (!isAndroidTestBuild()) {
@@ -18,73 +15,78 @@ if (!isAndroidTestBuild()) {
logLevel "DEBUG"
}
}
if (!isAndroidTestBuild()) {
apply plugin: 'bytex.threadOpt'
thread_opt {
enable true
enableInDebug true
rxJavaIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
rxJavaComputationReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
coroutineIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
coroutineDefaultReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
}
/**
* 方便使用systrace工具在工程侧打点便于分析工程侧性能问题
*/
apply plugin: 'bytex.systrace'
systrace {
/**
* 交付时要关闭,会有性能损耗
*/
enable false
enableInDebug false
/**
* - 是否使用[Trace.beginAsyncSection(String, int)/Trace.endAsyncSection(String, int)]进行打点
* - 默认使用[Trace.beginSection(String)/Trace.endSection()]进行打点
*/
isTraceAsync false
/**
* - 是否在运行时只针对主线程打点,其它线程不打
*/
isOnlyMainThread false
/**
* - 是否忽略对类的静态构造方法打点
* - 默认不忽略
*/
isIgnoreClinitMethod false
/**
* - 是否忽略对类中的简单方法打点
* 简单方法定义:
* - 空方法
* - get/set 方法
* - 单独的方法,方法体内没有调用其它方法
* - 默认不忽略
*/
isIgnoreSampleMethod false
/**
* - 针对特定类集合,配置打点白名单,在此集合中的类中的所有方法不打点
* - 支持正则表达式
*/
whiteListForClass = []
/**
* - 针对特定包名集合,配置打点白名单,所有类以此包名为前缀的类不打点
* - 支持正则表达式
*/
whiteListForPackage = []
apply plugin: 'chain.log.hook'
hooklog{
enableLoggerToServer true
}
}
//if (!isAndroidTestBuild()) {
// apply plugin: 'apm-plugin'
//}
//if (!isAndroidTestBuild()) {
// apply plugin: 'bytex.threadOpt'
// thread_opt {
// enable true
// enableInDebug true
// rxJavaIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
// rxJavaComputationReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
// coroutineIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
// coroutineDefaultReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
// }
//
// /**
// * 方便使用systrace工具在工程侧打点便于分析工程侧性能问题
// */
// apply plugin: 'bytex.systrace'
// systrace {
// /**
// * 交付时要关闭,会有性能损耗
// */
// enable false
// enableInDebug false
// /**
// * - 是否使用[Trace.beginAsyncSection(String, int)/Trace.endAsyncSection(String, int)]进行打点
// * - 默认使用[Trace.beginSection(String)/Trace.endSection()]进行打点
// */
// isTraceAsync false
// /**
// * - 是否在运行时只针对主线程打点,其它线程不打
// */
// isOnlyMainThread false
//
// /**
// * - 是否忽略对类的静态构造方法打点
// * - 默认不忽略
// */
// isIgnoreClinitMethod false
//
// /**
// * - 是否忽略对类中的简单方法打点
// * 简单方法定义:
// * - 空方法
// * - get/set 方法
// * - 单独的方法,方法体内没有调用其它方法
// * - 默认不忽略
// */
// isIgnoreSampleMethod false
//
// /**
// * - 针对特定类集合,配置打点白名单,在此集合中的类中的所有方法不打点
// * - 支持正则表达式
// */
// whiteListForClass = []
//
// /**
// * - 针对特定包名集合,配置打点白名单,所有类以此包名为前缀的类不打点
// * - 支持正则表达式
// */
// whiteListForPackage = []
// }
//}
/*apply plugin: 'chain.log.hook'
hooklog{
enableLoggerToServer true
}*/
bugly {
appId = 'ac71228f85' // 注册时分配的App ID
appKey = '3c736249-d6be-4066-b577-b7a6dc975cf7' // 注册时分配的App Key
@@ -254,31 +256,31 @@ dependencies {
androidTestImplementation rootProject.ext.dependencies.androidx_espresso_core
}
if (!isAndroidTestBuild()) {
ApmPlugin {
// 是否进行插桩
enable true
// 是否在Debug包插桩默认不插桩
enableInDebug true
// DEBUG("DEBUG"), INFO("INFO"), WARN("WARN"), ERROR("ERROR");
// INFO 级别Log会汇总所有被插桩处理的类供查看路径 app/build/ByteX/ApmPlugin
logLevel "DEBUG"
// 启动分析开关监控App启动耗时需要同时开启pageLoadSwitch
startSwitch = true
// 页面响应开关监控Activity的生命周期耗时
pageLoadSwitch = true
// 网络监控开关监控okhttp3的网络请求
okHttp3Switch = true
// 白名单下的包进行插桩,需要填写要插装类所在的包名,支持前缀配置
whiteList = [
"com.mogo"
]
// 黑名单包下类不进行插桩,可以配置包名和类名,没有可以填空
blackList = [
]
}
}
//if (!isAndroidTestBuild()) {
// ApmPlugin {
// // 是否进行插桩
// enable true
// // 是否在Debug包插桩默认不插桩
// enableInDebug true
// // DEBUG("DEBUG"), INFO("INFO"), WARN("WARN"), ERROR("ERROR");
// // INFO 级别Log会汇总所有被插桩处理的类供查看路径 app/build/ByteX/ApmPlugin
// logLevel "DEBUG"
// // 启动分析开关监控App启动耗时需要同时开启pageLoadSwitch
// startSwitch = true
// // 页面响应开关监控Activity的生命周期耗时
// pageLoadSwitch = true
// // 网络监控开关监控okhttp3的网络请求
// okHttp3Switch = true
// // 白名单下的包进行插桩,需要填写要插装类所在的包名,支持前缀配置
// whiteList = [
// "com.mogo"
// ]
// // 黑名单包下类不进行插桩,可以配置包名和类名,没有可以填空
// blackList = [
//
// ]
// }
//}
android.applicationVariants.all { variant ->
def buildTime = new Date().format("yyyyMMdd", TimeZone.getTimeZone("GMT+08:00"))

View File

@@ -29,7 +29,7 @@ project.android.productFlavors {
// 构建的应用身份类型,司机|乘客
buildConfigField 'int', 'APP_IDENTITY_MODE', "0x02"
// 连接的工控机IP地址
buildConfigField 'String', 'ADAS_CONNECT_IP', "\"192.168.1.102\""
buildConfigField 'String', 'ADAS_CONNECT_IP', "\"192.168.1.104\""
// 构建的是否是演示(美化)模式
buildConfigField 'boolean', 'IS_DEMO_MODE', 'true'
}

View File

@@ -28,8 +28,8 @@ project.android.productFlavors {
// GPS数据提供源 0-Android系统1-工控机2-OBU
buildConfigField 'int', 'GPS_PROVIDER', "1"
// 构建的应用身份类型,司机|乘客
buildConfigField 'int', 'APP_IDENTITY_MODE', "0x00"
// 构建的应用身份类型,出租车0|小巴A-司机|乘客
buildConfigField 'int', 'APP_IDENTITY_MODE', "0xA0"
// 连接的工控机IP地址
buildConfigField 'String', 'ADAS_CONNECT_IP', "\"192.168.8.102\""
// 构建的是否是演示(美化)模式

View File

@@ -26,8 +26,8 @@ project.android.productFlavors {
// GPS数据提供源 0-Android系统1-工控机2-OBU
buildConfigField 'int', 'GPS_PROVIDER', "1"
// 构建的应用身份类型司机0乘客1
buildConfigField 'int', 'APP_IDENTITY_MODE', "0x01"
// 构建的应用身份类型,出租车0|小巴A-司机0乘客1
buildConfigField 'int', 'APP_IDENTITY_MODE', "0xA1"
// 连接的工控机IP地址
buildConfigField 'String', 'ADAS_CONNECT_IP', "\"192.168.8.103\""
// 构建的是否是演示(美化)模式

View File

@@ -29,7 +29,7 @@ project.android.productFlavors {
// GPS数据提供源 0-Android系统1-工控机2-OBU
buildConfigField 'int', 'GPS_PROVIDER', "1"
// 构建的应用身份类型,司机|乘客
// 构建的应用身份类型,出租车0|小巴A-司机|乘客
buildConfigField 'int', 'APP_IDENTITY_MODE', "0x00"
// 连接的工控机IP地址
buildConfigField 'String', 'ADAS_CONNECT_IP', "\"192.168.1.102\""

View File

@@ -28,7 +28,7 @@ project.android.productFlavors {
// GPS数据提供源 0-Android系统1-工控机2-OBU
buildConfigField 'int', 'GPS_PROVIDER', "1"
// 构建的应用身份类型司机0乘客1
// 构建的应用身份类型,出租车0|小巴A-司机0乘客1
buildConfigField 'int', 'APP_IDENTITY_MODE', "0x01"
// 连接的工控机IP地址
buildConfigField 'String', 'ADAS_CONNECT_IP', "\"192.168.1.103\""

View File

@@ -0,0 +1,71 @@
package com.mogo.functions.test
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.mogo.eagle.core.function.hmi.ui.MoGoHmiFragment
import com.mogo.eagle.core.function.main.MainLauncherActivity
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.suspendCancellableCoroutine
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit.MILLISECONDS
@RunWith(AndroidJUnit4::class)
@LargeTest
class TipToastLeakTest {
lateinit var launch: ActivityScenario<MainLauncherActivity>
@Before
fun before() {
launch = ActivityScenario.launch(MainLauncherActivity::class.java)
}
@Test
fun test() = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow()
var index = 0
while (index < 50) {
delay(TimeUnit.SECONDS.toMillis(4))
TipToast.shortTip("toast-> $index" )
index ++
}
delay(TimeUnit.SECONDS.toMillis(1))
f.activity?.finish()
delay(TimeUnit.SECONDS.toMillis(2))
}
private suspend fun ensureMoGoHmiFragmentShow(): MoGoHmiFragment = suspendCancellableCoroutine {
launch.onActivity { itx ->
val executor = Executors.newSingleThreadScheduledExecutor()
executor.scheduleAtFixedRate({
var find =
itx.supportFragmentManager.fragments.find { it is MoGoHmiFragment } as? MoGoHmiFragment
while (find == null) {
find =
itx.supportFragmentManager.fragments.find { it is MoGoHmiFragment } as? MoGoHmiFragment
}
while (!find.isResumed) {
Thread.sleep(500)
}
it.resumeWith(Result.success(find))
try {
Thread.sleep(500)
executor.shutdownNow()
} catch (e: Throwable) {
e.printStackTrace()
}
}, 50, 500, MILLISECONDS)
}
}
}

View File

@@ -89,6 +89,7 @@ public class MogoApplication extends MainMoGoApplication {
HmiBuildConfig.isShowPerspectiveSwitchView = false;
HmiBuildConfig.isShowToolsView = false;
HmiBuildConfig.isShowBadCaseView = false;
HmiBuildConfig.isShowUpgradeTipsView = false;
//业务端可以根据需要控制是否展示刹车和转向灯的ui
// HmiBuildConfig.isShowBrakeLightView = false;
// HmiBuildConfig.isShowTurnLightView = false;

View File

@@ -35,9 +35,7 @@ buildscript {
classpath 'com.volcengine:apm_insight_plugin:1.4.1'
classpath 'com.mogo.cloud:thread_opt:1.0.1'
classpath 'com.mogo.cloud:systrace:1.0.1'
// classpath "com.bytedance.android.byteX:base-plugin:0.3.0"
// classpath "com.mogo.cloud:hook:${HOOK_LOG_VERSION}"
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
// classpath ("com.tencent.matrix:matrix-gradle-plugin:0.6.6") { changing = true }
}

View File

@@ -222,6 +222,7 @@ ext {
mogoaicloudtanlu : "com.mogo.cloud:tanlu:${MOGO_TANLU_VERSION}",
mogoaicloudtrafficlive : "com.mogo.cloud:trafficlive:${MOGO_TRAFFICLIVE_VERSION}",
mogoaicloudlocation : "com.mogo.cloud:location:${MOGO_LOCATION_VERSION}",
mogoaicloudtelematic : "com.mogo.cloud:telematic:${MOGO_TELEMATIC_VERSION}",
//========================= 新架构的 Maven 版本管理 =========================
mogo_core_function_autopilot : "com.mogo.eagle.core.function.impl:autopilot:${MOGO_CORE_FUNCTION_AUTOPILOT_VERSION}",
@@ -237,13 +238,14 @@ ext {
mogo_core_function_v2x : "com.mogo.eagle.core.function.impl:v2x:${MOGO_CORE_FUNCTION_V2X_VERSION}",
mogo_core_function_api : "com.mogo.eagle.core.function:api:${MOGO_CORE_FUNCTION_API_VERSION}",
mogo_core_function_call : "com.mogo.eagle.core.function:call:${MOGO_CORE_FUNCTION_CALL_VERSION}",
mogo_core_function_carcorder : "com.mogo.eagle.core.function:carcorder:${MOGO_CORE_FUNCTION_CARCORDER_VERSION}",
mogo_core_data : "com.mogo.eagle.core:data:${MOGO_CORE_DATA_VERSION}",
mogo_core_res : "com.mogo.eagle.core:res:${MOGO_CORE_RES_VERSION}",
mogo_core_utils : "com.mogo.eagle.core:utils:${MOGO_CORE_UTILS_VERSION}",
mogo_core_network : "com.mogo.eagle.core:network:${MOGO_CORE_NETWORK_VERSION}",
//========================= V2X SDK =========================
mogo_v2x : "com.mogo.v2x:v2x:${MOGO_V2X_SDK_VERSION}",
mogo_v2x : "com.mogo.v2x:v2x:${MOGO_V2X_SDK_VERSION}",
life_cycle_scope : "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0",
view_model_scope : "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0",

View File

@@ -2,25 +2,25 @@
本模块用来编写鹰眼核型功能
- function-impl 目录下编写的都是对mogo-core-function-api定的功能实现,
- mogo-core-function-check 程序及车辆检测模块
- mogo-core-function-devatools 开发工具模块
- mogo-core-function-hmi UI呈现及交互模块
- mogo-core-function-main 主入口
- mogo-core-function-map 地图相关的模块
- mogo-core-function-monitoring 远距离监控查看,路测摄像头、前车直播
- mogo-core-function-notice 云端公告、调度相关模块
- mogo-core-function-obu-mogo 自研OBU预警模块
- mogo-core-function-smp 小地图模块
- mogo-core-function-v2x 自车+云端预警模块
- check 程序及车辆检测模块
- devatools 开发工具模块日志采集、BadCase、
- hmi UI呈现及交互模块
- main 主入口(模块加载、后台服务启动、多进程启动等)
- map 地图相关的模块
- monitoring 行车超视距模块,路测摄像头、前车直播
- notice 云端公告、调度相关模块
- obu-mogo 自研OBU预警模块
- smp 小地图模块
- v2x 自车+云端预警模块
- mogo-core-data定义基础业务所需要的数据结构
- mogo-core-function-res这里只存放公共资源图片布局动画等
- mogo-core-network公共网络请求
- mogo-core-function-api定义基础业务功能的接口
- mogo-core-function-call定义基础业务暴露给外部调用的接口,对function-impl的二次封装,只将能对外调用的功能进行封装
- mogo-core-res程序中涉及到的图片及布局资源同一管理并通过设置不同的目录指定是那个模块的资源
- mogo-core-res程序中涉及到的图片及布局资源同一管理并通过设置不同的目录指定是那个模块的资源,这里只存放公共资源,图片,布局,动画等
- mogo-core-utils基于成熟的工具类开源框架下沉的这里可以增添针对我们业务上的一些工具类

View File

@@ -51,18 +51,20 @@ dependencies {
kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.adasHigh
implementation rootProject.ext.dependencies.mogochainbase
implementation rootProject.ext.dependencies.mogoami
implementation rootProject.ext.dependencies.mogoaicloudtelematic
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogo_core_data
implementation rootProject.ext.dependencies.modulecommon
implementation rootProject.ext.dependencies.moduleservice
implementation rootProject.ext.dependencies.mogo_core_data
implementation rootProject.ext.dependencies.mogo_core_utils
implementation rootProject.ext.dependencies.mogo_core_network
implementation rootProject.ext.dependencies.mogo_core_function_api
implementation rootProject.ext.dependencies.mogo_core_function_call
implementation rootProject.ext.dependencies.adasHigh
} else {
implementation project(':modules:mogo-module-common')
implementation project(':modules:mogo-module-service')
@@ -72,6 +74,7 @@ dependencies {
implementation project(':core:mogo-core-network')
implementation project(':core:mogo-core-function-api')
implementation project(':core:mogo-core-function-call')
implementation project(':libraries:mogo-adas')
}
}

View File

@@ -4,6 +4,7 @@ import android.Manifest.permission
import android.content.Context
import androidx.annotation.RequiresPermission
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.data.autopilot.AutopilotControlCmdParameter
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters
import com.mogo.eagle.core.data.config.FunctionBuildConfig
@@ -23,9 +24,11 @@ import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.zhidao.support.adas.high.AdasManager
import com.zhidao.support.adas.high.AdasOptions
import com.zhidao.support.adas.high.bean.IPCUpgradeInfo
import com.zhidao.support.adas.high.common.Constants
import com.zhidao.support.adas.high.common.CupidLogUtils
import java.util.concurrent.TimeUnit
/**
* @author xiaoyuzhou
* @date 2021/9/22 8:43 下午
@@ -33,7 +36,7 @@ import java.util.concurrent.TimeUnit
*/
@Route(path = MogoServicePaths.PATH_AUTO_PILOT)
class MoGoAutopilotProvider :
IMoGoAutopilotProvider, IMoGoMapDataCollectProvider.OnMapCollectCmdListener {
IMoGoAutopilotProvider, IMoGoMapDataCollectProvider.OnMapCollectCmdListener {
private val TAG = "MoGoAutoPilotProvider"
private var mContext: Context? = null
@@ -51,40 +54,123 @@ class MoGoAutopilotProvider :
when (FunctionBuildConfig.appIdentityMode) {
0x00 -> // 司机
{
// 注册地图采集功能
CallerMapDataCollectorManager.registerOnMapCollectTaskListener(this)
// "192.168.1.102"
val options = AdasOptions.Builder()
.setIPCIp(FunctionBuildConfig.adasConnectIP)
.setClient(false)
.build()
.setIpcConnectionMode(AdasOptions.IPC_CONNECTION_MODE.FIXATION)
.setIpcFixationIP(AdasManager.getInstance().getIPCFixationIPList(mContext))
.setClient(false)
.build()
AdasManager.getInstance().create(context, options)
// NSDNettyManager.getInstance().startNSDNettyServer(context, object : NettyServerListener<MogoProtocolMsg> {
// override fun onMessageResponseServer(msg: MogoProtocolMsg?, channel: Channel?) {
// Logger.d(TAG, "Receive client data is:${msg?.toString()}")
// }
//
// override fun onStartServer() {
// ToastUtils.showShort("司机端服务启动成功!")
// Logger.d(TAG, "onStartServer")
// }
//
// override fun onStopServer() {
// ToastUtils.showLong("司机端服务停止!")
// Logger.d(TAG, "onStopServer")
// }
//
// override fun onChannelConnect(channel: Channel?) {
// NSDNettyManager.getInstance().selectChannel(channel)
// val socketAddress = channel?.remoteAddress().toString()
// Logger.d(TAG, "Client ip is:${socketAddress}")
// }
//
// override fun onChannelDisConnect(channel: Channel?) {
// Logger.d(TAG, "onChannelDisConnect")
// }
// })
}
0x01 -> // 乘客
{
// 乘客端默认接收绘制全局路径+引导线
//FunctionBuildConfig.isDemoMode = true
//FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData = true
// "192.168.1.103"
val options = AdasOptions.Builder()
.setIPCIp(FunctionBuildConfig.adasConnectIP)
.setClient(false)
.build()
// "192.168.1.102"
val options = AdasOptions
.Builder()
.setClient(true)
.build()
AdasManager.getInstance().create(context, options)
// NSDNettyManager.getInstance().searchAndConnectServer(context, MoGoAiCloudClientConfig.getInstance().sn, object : NettyClientListener<MogoProtocolMsg> {
// override fun onMessageResponseClient(msg: MogoProtocolMsg?, sign: String?) {
// Logger.d(TAG, "收到司机端的数据!")
// // 乘客端收到adas数据直接解析后续分发解析后的数据流程同司机端
// msg?.let {
// AdasManager.getInstance().parseIPCData(it.body)
// Logger.d(TAG, "解析司机端数据完毕!")
// }
// }
//
// override fun onClientStatusConnectChanged(statusCode: Int, sign: String?) {
// when (statusCode) {
// ConnectState.STATUS_CONNECT_SUCCESS -> Logger.d(TAG, "乘客端连接司机端服务成功! sign is:${sign}")
// else -> {
// ToastUtils.showLong("和司机端连接异常!")
// Logger.d(TAG, "client statusCode is:${statusCode}")
// }
// }
// }
// })
}
//
else -> // 默认采用UDP寻址方式
{
val options = AdasOptions.Builder()
.setIPCIp(FunctionBuildConfig.adasConnectIP)
.setClient(false)
.build()
.setIpcConnectionMode(AdasOptions.IPC_CONNECTION_MODE.FIXATION)
.setIpcFixationIP(AdasManager.getInstance().getIPCFixationIPList(mContext))
.setClient(false)
.build()
AdasManager.getInstance().create(context, options)
}
}
//////////////////////////////////注意先后顺序AdasManager.getInstance().create后才可以设置监听/////////////////////////////////////////////
// 监听 adas 连接状态
AdasManager.getInstance().setOnAdasConnectStatusListener(MoGoAdasMsgConnectStatusListenerImpl())
// 监听ADAS-SDK获取到的工控机数据
AdasManager.getInstance()
.setOnAdasConnectStatusListener(MoGoAdasMsgConnectStatusListenerImpl())
// 监听ADAS-SDK获取到的工控机数据(乘客也需注册)
AdasManager.getInstance().setOnAdasListener(MoGoAdasListenerImpl())
// // 司机端监听
// if (FunctionBuildConfig.appIdentityMode == 0) {
// AdasManager.getInstance().setOnMultiDeviceListener { bytes ->
// Logger.d(
// TAG,
// "司机端接收到工控机吐出来的数据为:${Arrays.toString(bytes)}"
// )
// // 发送数据给乘客端
// if (NSDNettyManager.getInstance().isServerStart) {
// Logger.d(
// TAG,
// "司机端透传数据给乘客端!"
// )
// NSDNettyManager.getInstance().sendMsgToAllClients(MogoProtocolMsg(NORMAL_DATA, bytes.size, bytes))
//// NSDNettyManager.getInstance().sendMogoProtocolMsgToClient(MogoProtocolMsg(NORMAL_DATA, bytes.size, bytes)) { channelFuture: ChannelFuture ->
//// if (channelFuture.isSuccess) {
//// Logger.d(
//// TAG,
//// "Send data to client is success."
//// )
//// } else {
//// Logger.d(
//// TAG,
//// "Send data to client is failure."
//// )
//// }
//// }
// } else {
// Logger.d(TAG, "司机端Server未启动")
// }
// }
// }
// 同步数据给工控机的服务
AsyncDataToAutopilotServer.INSTANCE.initServer()
// 同步是否开启美化模式
@@ -98,17 +184,20 @@ class MoGoAutopilotProvider :
*/
override fun resetIpAddress(autoPilotIp: String) {
// 关闭通信
AdasManager.getInstance().closeAllMsg()
AdasManager.getInstance().disconnect()
// 延时执行连接指定IP地址
ThreadUtils.executeBySingleWithDelay(object : ThreadUtils.SimpleTask<String>() {
@RequiresPermission(permission.INTERNET)
override fun doInBackground(): String {
// 保存本地 AutoPilot IP地址
mContext?.let { SharedPrefsMgr.getInstance(it).putString(MoGoConfig.AUTOPILOT_IP, autoPilotIp) }
mContext?.let {
SharedPrefsMgr.getInstance(it).putString(MoGoConfig.AUTOPILOT_IP, autoPilotIp)
}
// 设置IP地址
AdasManager.getInstance().setIPCIp(autoPilotIp)
AdasManager.getInstance().adasOptions.setIpcConnectionMode(AdasOptions.IPC_CONNECTION_MODE.ASSIGN)
AdasManager.getInstance().adasOptions.setIpcAssignIP(autoPilotIp)
// 打开通讯连接
AdasManager.getInstance().startAllMsg()
AdasManager.getInstance().connect()
return ""
}
@@ -129,7 +218,7 @@ class MoGoAutopilotProvider :
}
override fun startAutoPilot(result: AutopilotControlParameters) {
if (AdasManager.getInstance().isSocketConnect) {
if (AdasManager.getInstance().ipcConnectionStatus == Constants.IPC_CONNECTION_STATUS.CONNECTED) {
val parameter = AutopilotControlCmdParameter("aiCloudToStartAutopilot", result)
AdasManager.getInstance().aiCloudToAdasData(GsonUtils.toJson(parameter))
} else {
@@ -142,7 +231,7 @@ class MoGoAutopilotProvider :
}
override fun cancelAutoPilot() {
if (AdasManager.getInstance().isSocketConnect) {
if (AdasManager.getInstance().ipcConnectionStatus == Constants.IPC_CONNECTION_STATUS.CONNECTED) {
AdasManager.getInstance().controlAutopilotCarHead()
} else {
Logger.e(TAG, "车机与工控机链接失败,无法断开自动驾驶")
@@ -151,7 +240,7 @@ class MoGoAutopilotProvider :
override fun recordPackage(): Boolean {
return AdasManager.getInstance()
.recordPackage(1, (System.currentTimeMillis() / 1000).toInt())
.recordPackage(1, (System.currentTimeMillis() / 1000).toInt())
}
override fun recordPackage(type: Int, id: Int): Boolean {

View File

@@ -1,5 +1,17 @@
package com.mogo.eagle.core.function.autopilot.adapter;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_ARRIVE;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_GUARDIAN;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_RECORD;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_ROUTE;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_STATUS;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_TRAJECTORY;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_WARN;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_ALIAS_CODE_ADAS_MESSAGE_CAR_STATE;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_ALIAS_CODE_ADAS_MESSAGE_RECT_DATA;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_LINK_ADAS;
import static com.mogo.eagle.core.data.chain.ChainConstant.CHAIN_LINK_LOG_WEB_SOCKET_DATA;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -42,6 +54,8 @@ import com.zhidao.support.adas.high.bean.WarnMessageInfo;
import com.zhidao.support.adas.high.bean.guardian.AutopilotGuardianInfo;
import com.zhidao.support.adas.high.bean.record.AutopilotRecordResult;
import com.zhidao.support.obu.ami.AmiClientManager;
import com.zhjt.service.chain.ChainLog;
import com.zhjt.service.chain.TracingConstants;
import org.json.JSONObject;
@@ -57,7 +71,12 @@ import java.util.List;
public class MoGoAdasListenerImpl implements OnAdasListener {
private final String TAG = "OnAdasListenerAdapter";
// @ChainLog(linkCode = CHAIN_LINK_ADAS,
// linkChainLog = CHAIN_LINK_LOG_WEB_SOCKET_DATA,
// endpoint = TracingConstants.Endpoint.PAD,
// nodeAliasCode = CHAIN_ALIAS_CODE_ADAS_MESSAGE_RECT_DATA,
// paramIndexes = {0},
// clientPkFileName = "sn")
@Override
public void onRectData(RectInfo rectInfo) {
if (HdMapBuildConfig.isMapLoaded) {
@@ -66,6 +85,12 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
}
}
// @ChainLog(linkCode = CHAIN_LINK_ADAS,
// linkChainLog = CHAIN_LINK_LOG_WEB_SOCKET_DATA,
// endpoint = TracingConstants.Endpoint.PAD,
// nodeAliasCode = CHAIN_ALIAS_CODE_ADAS_MESSAGE_CAR_STATE,
// paramIndexes = {0},
// clientPkFileName = "sn")
@Override
public void onCarStateData(CarStateInfo carStateInfo) {
if (HdMapBuildConfig.isMapLoaded) {
@@ -145,6 +170,12 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
}
}
// @ChainLog(linkCode = CHAIN_LINK_ADAS,
// linkChainLog = CHAIN_LINK_LOG_WEB_SOCKET_DATA,
// endpoint = TracingConstants.Endpoint.PAD,
// nodeAliasCode = CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_STATUS,
// paramIndexes = {0},
// clientPkFileName = "sn")
@Override
public void autopilotStatus(AutopilotStatus autopilotStatus) {
if (HdMapBuildConfig.isMapLoaded) {
@@ -177,6 +208,12 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
}
}
// @ChainLog(linkCode = CHAIN_LINK_ADAS,
// linkChainLog = CHAIN_LINK_LOG_WEB_SOCKET_DATA,
// endpoint = TracingConstants.Endpoint.PAD,
// nodeAliasCode = CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_ARRIVE,
// paramIndexes = {0},
// clientPkFileName = "sn")
@Override
public void autopilotArrive(AutopilotWayArrive autopilotWayArrive) {
if (HdMapBuildConfig.isMapLoaded) {
@@ -195,6 +232,12 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
}
}
// @ChainLog(linkCode = CHAIN_LINK_ADAS,
// linkChainLog = CHAIN_LINK_LOG_WEB_SOCKET_DATA,
// endpoint = TracingConstants.Endpoint.PAD,
// nodeAliasCode = CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_ROUTE,
// paramIndexes = {0},
// clientPkFileName = "sn")
@Override
public void onAutopilotRoute(AutopilotRoute route) {
if (HdMapBuildConfig.isMapLoaded) {
@@ -204,6 +247,11 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
}
}
// @ChainLog(linkCode = CHAIN_LINK_ADAS,
// endpoint = TracingConstants.Endpoint.PAD,
// nodeAliasCode = CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_TRAJECTORY,
// paramIndexes = {0},
// clientPkFileName = "sn")
@Override
public void onAutopilotTrajectory(List<TrajectoryInfo> trajectoryList) {
if (HdMapBuildConfig.isMapLoaded) {
@@ -234,6 +282,12 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
CallerAutoPilotStatusListenerManager.INSTANCE.invokeAutopilotSNRequest();
}
// @ChainLog(linkCode = CHAIN_LINK_ADAS,
// linkChainLog = CHAIN_LINK_LOG_WEB_SOCKET_DATA,
// endpoint = TracingConstants.Endpoint.PAD,
// nodeAliasCode = CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_GUARDIAN,
// paramIndexes = {0},
// clientPkFileName = "sn")
@Override
public void onAutopilotGuardian(AutopilotGuardianInfo guardianInfo) {
if (HdMapBuildConfig.isMapLoaded) {
@@ -242,6 +296,12 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
}
}
// @ChainLog(linkCode = CHAIN_LINK_ADAS,
// linkChainLog = CHAIN_LINK_LOG_WEB_SOCKET_DATA,
// endpoint = TracingConstants.Endpoint.PAD,
// nodeAliasCode = CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_RECORD,
// paramIndexes = {0},
// clientPkFileName = "sn")
@Override
public void onAutopilotRecord(AutopilotRecordResult result) {
if (result != null) {
@@ -276,7 +336,12 @@ public class MoGoAdasListenerImpl implements OnAdasListener {
}
// @ChainLog(linkCode = CHAIN_LINK_ADAS,
// linkChainLog = CHAIN_LINK_LOG_WEB_SOCKET_DATA,
// endpoint = TracingConstants.Endpoint.PAD,
// nodeAliasCode = CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_WARN,
// paramIndexes = {0},
// clientPkFileName = "sn")
@Override
public void onWarnMessage(WarnMessageInfo warnMessageInfo) {
final AutopilotWarnMessage warnMessage = AdasObjectConvertUtils.INSTANCE.fromAdasObject(warnMessageInfo);

View File

@@ -2,17 +2,16 @@ package com.mogo.eagle.core.function.autopilot.adapter
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.data.app.AppConfigInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.data.constants.MoGoConfig
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.autopilot.network.AdasServiceModel
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.zhidao.support.adas.high.AdasManager
import com.zhidao.support.adas.high.OnAdasMsgConnectStatusListener
import com.zhidao.support.adas.high.OnAdasConnectStatusListener
import com.zhidao.support.adas.high.bean.BasicInfo
import com.zhidao.support.adas.high.common.Constants
import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
@@ -23,7 +22,8 @@ import java.util.concurrent.TimeUnit
*
* @author donghongyu
*/
class MoGoAdasMsgConnectStatusListenerImpl : OnAdasMsgConnectStatusListener, IMoGoAutopilotStatusListener {
class MoGoAdasMsgConnectStatusListenerImpl : OnAdasConnectStatusListener,
IMoGoAutopilotStatusListener {
private val TAG = "MoGoAdasMsgConnectStatusListenerImpl"
//自动驾驶状态
@@ -32,26 +32,36 @@ class MoGoAdasMsgConnectStatusListenerImpl : OnAdasMsgConnectStatusListener, IMo
//自动驾驶车速度
private var mCurrentAutopilotSpeed = 0f
override fun onWebSocketConnectSuccess() {
Logger.d(TAG, "webSocket 连接成功")
// 初始化自动驾驶状态信息
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectIP = AdasManager.getInstance().adasConfig.address
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectStatus = true
CallerAutoPilotStatusListenerManager.invokeAutoPilotStatus()
// 同步SN给工控机
syncBasicInfoToAutopilot()
override fun onConnectionIPCStatus(ipcConnectionStatus: Int, reason: String?) {
if (ipcConnectionStatus == Constants.IPC_CONNECTION_STATUS.CONNECTED) {
Logger.d(TAG, "webSocket 连接成功")
// 初始化自动驾驶状态信息
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectIP =
AdasManager.getInstance().adasConfig.address
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectStatus = true
CallerAutoPilotStatusListenerManager.invokeAutoPilotStatus()
// 开启轮训上传自动驾驶状态
updateDriveStatusTask()
// 同步SN给工控机
syncBasicInfoToAutopilot()
// 开启轮训上传自动驾驶状态
updateDriveStatusTask()
} else if (ipcConnectionStatus == Constants.IPC_CONNECTION_STATUS.DISCONNECTED) {
Logger.d(TAG, "webSocket 连接失败 reason:$reason")
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectIP =
AdasManager.getInstance().adasConfig.address
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectStatus = false
CallerAutoPilotStatusListenerManager.invokeAutoPilotStatus()
} else if (ipcConnectionStatus == Constants.IPC_CONNECTION_STATUS.CONNECTING) {
Logger.d(TAG, "webSocket 正在连接")
} else if (ipcConnectionStatus == Constants.IPC_CONNECTION_STATUS.SEARCH_ADDRESS) {
Logger.d(TAG, "webSocket 正在搜索工控机IP")
} else if (ipcConnectionStatus == Constants.IPC_CONNECTION_STATUS.NOT_FOUND_ADDRESS) {
Logger.d(TAG, "webSocket 找不到可用IP 传入的IP不可用或固定IP列表中所有IP不可用")
}
}
override fun onWebSocketConnectFailed(reason: String) {
Logger.d(TAG, "webSocket 连接失败 reason:$reason")
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectIP = AdasManager.getInstance().adasConfig.address
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectStatus = false
CallerAutoPilotStatusListenerManager.invokeAutoPilotStatus()
}
/**
* 工控机获取SN
@@ -94,12 +104,13 @@ class MoGoAdasMsgConnectStatusListenerImpl : OnAdasMsgConnectStatusListener, IMo
private fun updateDriveStatusTask() {
Logger.d(TAG, "updateDriveStatusTask")
Flowable.interval(0, 5, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { aLong: Long? ->
AdasServiceModel.getInstance().updateDriveStatus(mCurrentAutopilotStatus, mCurrentAutopilotSpeed)
}
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { aLong: Long? ->
AdasServiceModel.getInstance()
.updateDriveStatus(mCurrentAutopilotStatus, mCurrentAutopilotSpeed)
}
}

View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,75 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-android-extensions'
id 'kotlin-kapt'
id 'com.alibaba.arouter'
}
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
// buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode Integer.valueOf(VERSION_CODE)
versionName getValueFromRootProperties("${project.name.replace("-", "_").toUpperCase()}_VERSION")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
//ARouter apt 参数
kapt {
useBuildCache = false
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation rootProject.ext.dependencies.kotlinstdlibjdk7
implementation rootProject.ext.dependencies.coroutinescore
implementation rootProject.ext.dependencies.arouter
kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.mogologlib
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogoserviceapi
implementation rootProject.ext.dependencies.modulecommon
implementation project(':libraries:map-usbcamera')
implementation rootProject.ext.dependencies.mogo_core_utils
implementation rootProject.ext.dependencies.mogo_core_function_api
implementation rootProject.ext.dependencies.mogo_core_function_call
implementation rootProject.ext.dependencies.mogo_core_data
}else {
implementation project(':services:mogo-service-api')
implementation project(':modules:mogo-module-common')
implementation project(':libraries:map-usbcamera')
implementation project(':core:mogo-core-utils')
implementation project(':core:mogo-core-function-api')
implementation project(':core:mogo-core-function-call')
implementation project(':core:mogo-core-data')
}
}
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()

View File

@@ -0,0 +1,3 @@
GROUP=com.mogo.eagle.core.function.impl
POM_ARTIFACT_ID=carcorder
VERSION_CODE=1

View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.eagle.core.function.carcorder">
<application>
<service
android:name=".service.CarcorderService"
android:enabled="true"
android:exported="true"
android:process=":uvcservice">
<intent-filter>
<action android:name="com.mogo.launcher.action.CARCORDER_SERVICE" />
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -0,0 +1,162 @@
package com.mogo.eagle.core.function.carcorder.service
import android.content.Intent
import android.hardware.usb.UsbDevice
import android.os.IBinder
import android.util.Log
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.usbcamera.UVCCameraHelper
import com.serenegiant.usb.IFrameCallback
import com.serenegiant.usb.USBMonitor
import com.serenegiant.usb.USBMonitor.OnDeviceConnectListener
import com.serenegiant.usb.USBMonitor.UsbControlBlock
import com.serenegiant.usb.UVCCamera
import com.serenegiant.usb.common.BaseService
import com.serenegiant.usb.encoder.MediaVideoBufferEncoder
/**
* 行车记录仪服务
* @author donghongyu
*/
class CarcorderService : BaseService() {
private val DEBUG = true
val TAG = CarcorderService::class.java.name
// 挂载的USB设备集合
private var mDeviceList: List<UsbDevice>? = null
// USB 设备连接工具
private var mUSBMonitor: USBMonitor? = null
// 用于接入UVC摄像机
private var mUVCCamera: UVCCamera? = null
// 相机控制
private var mCtrlBlock: UsbControlBlock? = null
/**
* 配置相机基本按书
*/
private val previewWidth = 640
private val previewHeight = 480
// Default using MJPEG
// if your device is connected,but have no images
// please try to change it to FRAME_FORMAT_YUYV
val FRAME_FORMAT_MJPEG: Int = UVCCamera.FRAME_FORMAT_MJPEG
val MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS
val MODE_CONTRAST = UVCCamera.PU_CONTRAST
private val mFrameFormat = UVCCameraHelper.FRAME_FORMAT_MJPEG
override fun onCreate() {
super.onCreate()
if (DEBUG) {
Logger.d(TAG, "onCreate……")
}
if (mUSBMonitor == null) {
mUSBMonitor = USBMonitor(applicationContext, mOnDeviceConnectListener)
mUSBMonitor!!.register()
mDeviceList = mUSBMonitor!!.deviceList
}
}
override fun onDestroy() {
super.onDestroy()
if (DEBUG) Log.d(TAG, "onDestroy:")
if (mUSBMonitor != null) {
mUSBMonitor!!.unregister()
mUSBMonitor = null
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onRebind(intent: Intent) {
if (DEBUG) Log.d(TAG, "onRebind:$intent")
}
override fun onUnbind(intent: Intent): Boolean {
if (DEBUG) Log.d(TAG, "onUnbind:$intent")
if (DEBUG) Log.d(TAG, "onUnbind:finished")
return true
}
//**********************************************************************************************************************************
private val mSync: Any = Any()
private val mVideoEncoder: MediaVideoBufferEncoder? = null
/**
* USB 设备连接监听
*/
private val mOnDeviceConnectListener: OnDeviceConnectListener = object : OnDeviceConnectListener {
override fun onAttach(device: UsbDevice) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onAttach:${device.deviceName}---mDeviceList:${mDeviceList?.size}")
mUSBMonitor!!.requestPermission(device)
}
override fun onConnect(device: UsbDevice, ctrlBlock: UsbControlBlock, createNew: Boolean) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onConnect:${device.deviceName}")
openCamera(device, ctrlBlock, createNew)
}
override fun onDisconnect(device: UsbDevice, ctrlBlock: UsbControlBlock) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onDisconnect:${device.deviceName}")
}
override fun onDettach(device: UsbDevice) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onDettach:${device.deviceName}")
}
override fun onCancel(device: UsbDevice) {
if (DEBUG) Log.d(TAG, "OnDeviceConnectListener#onCancel:${device.deviceName}")
}
}
/**
* 连接相机
*/
private fun openCamera(device: UsbDevice, ctrlBlock: UsbControlBlock, createNew: Boolean) {
if (mUVCCamera == null) {
mUVCCamera = UVCCamera()
mUVCCamera!!.open(ctrlBlock)
mUVCCamera!!.setStatusCallback { statusClass, event, selector, statusAttribute, data ->
if (DEBUG) Log.d(TAG, "IStatusCallback#onStatus(statusClass=${statusClass},event=${event},selector=${selector},statusAttribute=${statusAttribute},data=${data})")
}
try {
mUVCCamera!!.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.FRAME_FORMAT_MJPEG)
} catch (e: Exception) {
e.printStackTrace()
try {
mUVCCamera!!.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.DEFAULT_PREVIEW_MODE)
} catch (e: Exception) {
e.printStackTrace()
mUVCCamera!!.destroy()
}
}
mUVCCamera!!.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP)
mUVCCamera!!.startPreview()
}
}
/**
* 视频帧回掉
*/
private val mIFrameCallback = IFrameCallback { frame ->
if (DEBUG) Log.d(TAG, "IFrameCallback#onFrame:${frame}")
}
}

View File

@@ -0,0 +1,53 @@
package com.mogo.eagle.core.function.carcorder.service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.serenegiant.usb.common.BaseService
/**
* 行车记录仪服务
* @author donghongyu
*/
class LivePushService : BaseService() {
private val DEBUG = true
val TAG = LivePushService::class.java.name
override fun onCreate() {
super.onCreate()
if (DEBUG) {
Logger.d(TAG, "onCreate……")
}
}
override fun onDestroy() {
super.onDestroy()
if (DEBUG) Log.d(TAG, "onDestroy:")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onRebind(intent: Intent) {
if (DEBUG) Log.d(TAG, "onRebind:$intent")
}
override fun onUnbind(intent: Intent): Boolean {
if (DEBUG) Log.d(TAG, "onUnbind:$intent")
if (DEBUG) Log.d(TAG, "onUnbind:finished")
return true
}
}

View File

@@ -48,6 +48,7 @@ dependencies {
implementation rootProject.ext.dependencies.arouter
kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.mogologlib
implementation rootProject.ext.dependencies.mogochainbase
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogoserviceapi

View File

@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zhjt.mogo_core_function_devatools">
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
</manifest>

View File

@@ -1,29 +1,105 @@
package com.zhjt.mogo_core_function_devatools
import android.annotation.SuppressLint
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_ADAS_INIT
import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_ADAS_MSG
import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_CONNECT_STATUS
import com.mogo.eagle.core.data.chain.ChainConstant.Companion.CHAIN_LINK_LOG_WEB_SOCKET_DATA
import com.mogo.eagle.core.data.chain.ChainLogParam
import com.mogo.eagle.core.data.constants.MoGoConfig
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.api.devatools.IDevaToolsProvider
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr
import com.mogo.eagle.core.utilcode.util.DeviceUtils
import com.mogo.eagle.core.utilcode.util.Utils
import com.zhidao.loglib.fw.FileWriteManager
import com.zhidao.loglib.fw.FwBuild
import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchManager
import com.zhjt.service.chain.core.ChainTraceStarter
@Route(path = MogoServicePaths.PATH_DEVA_TOOLS)
class DevaToolsProvider : IDevaToolsProvider {
private val traceInfoCache = hashMapOf<Int, ChainLogParam>()
private val fwBuildMap: MutableMap<Int, FwBuild> = HashMap()
override val functionName: String
get() = "DevaToolsProvider"
override fun init(context: Context) {
MogoLogCatchManager.init(context)
logCheck(context)
initTrace(context)
}
private fun logCheck(context: Context) {
val logger = SharedPrefsMgr.getInstance(context).getBoolean(MoGoConfig.CATCH_LOG, false)
val loggerTime = SharedPrefsMgr.getInstance(context).getLong(MoGoConfig.CATCH_LOG_TIME, 0)
val logCatchDuration = (System.currentTimeMillis() - loggerTime) / 1000 / 60
if (logger && loggerTime > 0) {
val logTime: Int = if (10 - logCatchDuration < 1) {
1
} else {
10 - logCatchDuration.toInt()
}
MogoLogCatchManager.startCatchLog(logTime)
} else {
Logger.d(
functionName,
"logCheck logger : $logger , logCatchDuration : $logCatchDuration"
)
}
}
override fun startLogCatch() {
MogoLogCatchManager.startCatchLog()
}
override fun startLogCatch(duration: Int) {
MogoLogCatchManager.startCatchLog(duration)
}
override fun stopLogCatch() {
MogoLogCatchManager.stopCatchLog()
}
private fun initTrace(context: Context) {
// 初始化Trace抓取服务
val pkgName = Utils.getApp().packageName
ChainTraceStarter.start(pkgName, DeviceUtils.getMacAddress(), BuildConfig.DEBUG)
// Trace过程中进行日志抓取对日志进行配置
fwBuildMap[CHAIN_LINK_LOG_CONNECT_STATUS] =
FwBuild(true, pkgName + CHAIN_LINK_LOG_ADAS_INIT, 5_000)
fwBuildMap[CHAIN_LINK_LOG_WEB_SOCKET_DATA] =
FwBuild(false, pkgName + CHAIN_LINK_LOG_ADAS_MSG, 500)
traceInfoCache[CHAIN_LINK_LOG_CONNECT_STATUS] = ChainLogParam(true, "ADAS连接状态")
traceInfoCache[CHAIN_LINK_LOG_WEB_SOCKET_DATA] = ChainLogParam(false, "ADAS长链数据")
FileWriteManager.getInstance()
.init(context, MoGoAiCloudClientConfig.getInstance().sn, pkgName, fwBuildMap)
}
override fun getTraceInfo(): HashMap<Int, ChainLogParam> {
return traceInfoCache
}
@SuppressLint("NewApi")
override fun refreshTraceInfo(map: HashMap<Int, ChainLogParam>) {
map.forEach { (type, param) ->
val fwBuild = this.fwBuildMap[type]
fwBuild?.let {
Logger.d(functionName, "param : ${param.des} , record : ${param.record}")
it.isRecord = param.record
}
}
FileWriteManager.getInstance().operateChainMap(fwBuildMap)
}
override fun onDestroy() {
MogoLogCatchManager.onDestroy()
}

View File

@@ -7,13 +7,14 @@ import android.os.Message
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.data.constants.MoGoConfig
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsListenerManager
import com.mogo.eagle.core.network.NetConfig
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.TimeUtils
import com.mogo.module.common.MogoApisHandler
import com.mogo.service.cloud.socket.IMogoOnMessageListener
import com.zhidao.loglib.bean.RemoteLogPushContent
@@ -25,7 +26,6 @@ import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchConst.Companio
import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchConst.Companion.LOG_PUSH_TYPE
import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchConst.Companion.START_CATCH_LOG
import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchConst.Companion.STOP_CATCH_LOG
import java.io.File
@SuppressLint("StaticFieldLeak")
object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handler.Callback,
@@ -47,7 +47,6 @@ object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handl
MogoApisHandler.getInstance().apis
.getSocketManagerApi(AbsMogoApplication.getApp().applicationContext)
.registerOnMessageListener(LOG_PUSH_TYPE, this)
manualContent.duration = 10
manualContent.pkgName = context.packageName
}
@@ -80,12 +79,13 @@ object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handl
return false
}
fun startCatchLog() {
fun startCatchLog(duration:Int = 10) {
if (catchingList.contains(MANUAL_CATCH_PKG_NAME)) {
TipToast.shortTip("已经在抓取日志了,请稍后再试")
} else {
Logger.d(TAG, "开始抓取日志====")
Logger.d(TAG, "开始抓取日志==== duration : $duration")
manualContent.type = START_CATCH_LOG
manualContent.duration = duration
startCatchLog(manualContent)
}
}
@@ -101,20 +101,28 @@ object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handl
var delay = (content.duration).toLong()
handler.removeMessages(MSG_TRY_CLOSE_LOG)
if (delay <= 0) {
// 如果push 下来的delay小于等于0那就给个默认最大值一小时
// 如果push 下来的delay小于等于0那就给个默认
delay = 10
}
handler.sendEmptyMessageDelayed(MSG_TRY_CLOSE_LOG, delay * 1000L * 60)
openLoggerLevel()
logInfoManager = LogInfoManagerFactory.createPushLogInfoManager(
mContext,
MoGoAiCloudClientConfig.getInstance().sn + File.separator + TimeUtils.formatYMD(System.currentTimeMillis()),
content, this
MoGoAiCloudClientConfig.getInstance().sn,
content,
this
)
logInfoManager?.start()
logInfoManager?.registerLogOutListener { lineLog ->
CallerDevaToolsListenerManager.invokeDevaToolsLogCatchLines(lineLog)
}
SharedPrefsMgr.getInstance(mContext!!).putBoolean(MoGoConfig.CATCH_LOG, true)
SharedPrefsMgr.getInstance(mContext!!)
.putLong(MoGoConfig.CATCH_LOG_TIME, System.currentTimeMillis())
}
private fun stopCatchLog(content: RemoteLogPushContent) {
@@ -125,6 +133,9 @@ object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handl
logInfoManager?.stop()
logInfoManager = null
closeLoggerLevel()
SharedPrefsMgr.getInstance(mContext!!).putBoolean(MoGoConfig.CATCH_LOG, false)
SharedPrefsMgr.getInstance(mContext!!).putLong(MoGoConfig.CATCH_LOG_TIME, 0)
}
/**
@@ -152,6 +163,10 @@ object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handl
override fun onClose(pkgName: String?) {
ThreadUtils.runOnUiThread {
SharedPrefsMgr.getInstance(mContext!!).putBoolean(MoGoConfig.CATCH_LOG, false)
SharedPrefsMgr.getInstance(mContext!!).putLong(MoGoConfig.CATCH_LOG_TIME, 0)
CallerDevaToolsListenerManager.invokeDevaToolsLogCatchClose()
TipToast.shortTip("日志抓取默认计时结束")
}

View File

@@ -59,26 +59,26 @@ dependencies {
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.androidxrecyclerview
implementation rootProject.ext.dependencies.modulecommon
compileOnly rootProject.ext.dependencies.mogoserviceapi
implementation project(':libraries:map-usbcamera')
implementation rootProject.ext.dependencies.mogo_core_res
implementation rootProject.ext.dependencies.mogo_core_data
implementation rootProject.ext.dependencies.mogo_core_utils
implementation rootProject.ext.dependencies.mogo_core_network
implementation rootProject.ext.dependencies.mogo_core_function_call
// implementation rootProject.ext.dependencies.mogo_core_res
} else {
implementation project(':modules:mogo-module-common')
implementation project(':services:mogo-service-api')
implementation project(':libraries:map-usbcamera')
implementation project(':core:mogo-core-res')
implementation project(':core:mogo-core-data')
implementation project(':core:mogo-core-utils')
implementation project(':core:mogo-core-res')
implementation project(':core:mogo-core-network')
implementation project(':core:mogo-core-function-api')
implementation project(':core:mogo-core-function-call')
// implementation project(':core:mogo-core-res')
}
}

View File

@@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.eagle.core.function.hmi">
<application>
<receiver android:name="com.mogo.eagle.core.function.hmi.receiver.V2XWarningBroadcastReceiver">

View File

@@ -5,12 +5,16 @@ import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.util.Log
import android.view.*
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.animation.OvershootInterpolator
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.lifecycleScope
import androidx.transition.*
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.mvp.MvpFragment
@@ -98,6 +102,8 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
private var onBadCaseShow: (() -> View)? = null
private var onBadCaseHide: (() -> Unit)? = null
private var upgradeTipsView: (() -> View)? = null
companion object {
private const val MSG_WHAT_DISMISS_BAD_CASE_ENTRY = 0x1010
private val CASE_EXPIRE_DURATION = TimeUnit.HOURS.toMillis(4)
@@ -116,7 +122,8 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
Log.d("QQQ", "-- step -- 1 --")
var oldT = try {
old?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time ?: 0L
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time
?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
@@ -126,30 +133,31 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
var newT = try {
it.receive()?.also { record = it }?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time
?: 0L
?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
if (oldT == 0L || (newT > 0L && (newT - oldT > 0L) && (newT - oldT) < CASE_EXPIRE_DURATION)) {
if (oldT == 0L || (newT > 0L && (newT - oldT > 0L) && (newT - oldT) < CASE_EXPIRE_DURATION)) {
Log.d("QQQ", "-- step -- 2 --")
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Log.d("QQQ", "record: [$record] is displaying and consuming ~~~" )
Log.d("QQQ", "record: [$record] is displaying and consuming ~~~")
showBadCaseEntrance(it)
}
continue
}
while (oldT != 0L && newT != 0L && (newT - oldT) >= CASE_EXPIRE_DURATION) {
Log.d("QQQ", "record: [$record] has been discarded, because it has been timeout." )
Log.d("QQQ", "record: [$record] has been discarded, because it has been timeout.")
oldT = newT
newT = try {
it.receive()?.also {
record = it
}?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time ?: 0L
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time
?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
@@ -157,7 +165,7 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Log.d("QQQ", "record: [$record] is displaying for rest ..." )
Log.d("QQQ", "record: [$record] is displaying for rest ...")
showBadCaseEntrance(it)
}
} else {
@@ -169,7 +177,7 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
it.visibility = View.VISIBLE
}
}
Log.d("QQQ", "record: [$old] hasn't been consumed~~~~" )
Log.d("QQQ", "record: [$old] hasn't been consumed~~~~")
}
} finally {
delay(1000)
@@ -231,6 +239,11 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
/*ivCameraIcon?.setOnLongClickListener {
activity?.let { it1 -> CarcorderPreviewView.show(it1) }
true
}*/
ivToolsIcon?.setOnClickListener {
if (toolsViewFloat == null) {
showToolsFloat()
@@ -259,6 +272,11 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
}
/*// TODO 这里后面需要改成独立进程通讯后台获取YUV
view.postDelayed({
activity?.let { CarcorderPreviewView.show(it) }
}, 1000)*/
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -346,21 +364,29 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
onBadCaseHide = onHide
}
/**
*注册工控机升级提示圆点View的回调
* @param 提示圆点View
*/
override fun registerUpgradeTipsCallback(tipsView: () -> View) {
upgradeTipsView = tipsView
}
/**
* 工控机重启返回结果
* @param code
* @param msg
*/
override fun showDockerRebootResult(code: Int, msg: String) {
ThreadUtils.runOnUiThread{
if(code>=-1){
ThreadUtils.runOnUiThread {
if (code >= -1) {
//重启成功
ToastUtils.showShort("重启成功")
}else{
} else {
//重启失败
msg?.let {
ToastUtils.showShort(it)
}
}
}
}
}
@@ -372,7 +398,7 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
if (autoPilotBadCaseView == null) {
autoPilotBadCaseView = AutoPilotBadCaseView(it).also { itx ->
val record =
autoPilotBadCaseEntrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
autoPilotBadCaseEntrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
itx.tag = record
itx.onDismiss {
dismissBadCaseFloatView()
@@ -383,12 +409,13 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
val params = mutableMapOf<String, String>()
autoPilotBadCaseEntrance?.apply {
params["carLicense"] =
MoGoAiCloudClientConfig.getInstance().sn
MoGoAiCloudClientConfig.getInstance().sn
params["filename"] = record?.fileName ?: ""
params["filesize"] = record?.total.toString()
params["key"] = record?.key ?: ""
params["reason"] = it.reason ?: ""
params["duration"] = record?.duration?.toInt()?.toString() ?: ""
params["duration"] = record?.duration?.toInt()?.toString()
?: ""
params["timestamp"] = record?.timestamp ?: ""
}
val response = post(params)
@@ -403,9 +430,9 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
dismissBadCaseFloatView()
dismiss?.invoke()
CallerAutoPilotManager.recordCause(
record?.key,
record?.fileName,
it.id, it.reason)
record?.key,
record?.fileName,
it.id, it.reason)
ToastUtils.showShort("接管反馈成功~")
record?.also {
it.consumed = true
@@ -431,39 +458,39 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
autoPilotToolsFloat = WarningFloat.with(it)
.setTag("BadCaseCollectFloat")
.setLayout(autoPilotBadCaseView!!)
.setSidePattern(SidePattern.LEFT)
.setGravity(Gravity.LEFT, offsetY = 72)
.setImmersionStatusBar(true)
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)
?.apply {
interpolator = OvershootInterpolator()
}
.setTag("BadCaseCollectFloat")
.setLayout(autoPilotBadCaseView!!)
.setSidePattern(SidePattern.LEFT)
.setGravity(Gravity.LEFT, offsetY = 72)
.setImmersionStatusBar(true)
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)
?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)
?.setDuration(200)
})
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onDismiss() {
autoPilotToolsFloat = null
autoPilotBadCaseView = null
}
})
.show()
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)
?.setDuration(200)
})
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onDismiss() {
autoPilotToolsFloat = null
autoPilotBadCaseView = null
}
})
.show()
} else {
autoPilotToolsFloat?.show()
}
@@ -663,7 +690,8 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
if (floatWindow == null || TextUtils.isEmpty(showTag) || !floatWindow.isShow() || floatWindow.config.floatTag != tag) {
val notificationView = V2XNotificationView(it)
notificationView.setWarningIcon(EventTypeEnum.getWarningIcon(v2xType.toString()))
val warningContent = alertContent ?: EventTypeEnum.getWarningContent(v2xType.toString())
val warningContent = alertContent
?: EventTypeEnum.getWarningContent(v2xType.toString())
if (warningContent.isEmpty()) {
Logger.e(TAG, "Show warningContent is null or empty!")
return@launchWhenResumed
@@ -674,48 +702,48 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
WarningFloat.dismiss(floatWindow.config.floatTag, true)
}
mWarningFloat = WarningFloat.with(it)
.setTag(tag)
.setLayout(notificationView)
.setSidePattern(SidePattern.RESULT_TOP)
.setCountDownTime(expireTime)
.setGravity(Gravity.CENTER_HORIZONTAL, offsetY = 110)
.setImmersionStatusBar(true)
.isEnqueue(true)
.addWarningStatusListener(listenerIMoGo)
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onShow() {
// 创建弹窗成功才进行TTS播报
Logger.d(
"MoGoWarningFragment",
"mWarningFloat = $mWarningFloat---ttsContent = $ttsContent"
)
if (mWarningFloat != null && !TextUtils.isEmpty(ttsContent) && playTts) {
Logger.d("MoGoWarningFragment", "---> ttsContent = $ttsContent")
AIAssist.getInstance(activity)
.speakTTSVoice(ttsContent)
}
}
})
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)?.apply {
interpolator = OvershootInterpolator()
.setTag(tag)
.setLayout(notificationView)
.setSidePattern(SidePattern.RESULT_TOP)
.setCountDownTime(expireTime)
.setGravity(Gravity.CENTER_HORIZONTAL, offsetY = 110)
.setImmersionStatusBar(true)
.isEnqueue(true)
.addWarningStatusListener(listenerIMoGo)
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onShow() {
// 创建弹窗成功才进行TTS播报
Logger.d(
"MoGoWarningFragment",
"mWarningFloat = $mWarningFloat---ttsContent = $ttsContent"
)
if (mWarningFloat != null && !TextUtils.isEmpty(ttsContent) && playTts) {
Logger.d("MoGoWarningFragment", "---> ttsContent = $ttsContent")
AIAssist.getInstance(activity)
.speakTTSVoice(ttsContent)
}
}
})
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)?.setDuration(200)
})
.show()
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)?.setDuration(200)
})
.show()
} else {
val notification = floatWindow.config.layoutView as? V2XNotificationView
if (alertContent?.isNotEmpty() == true) {
@@ -1067,22 +1095,32 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
* @param upgradeStatus 升级状态
*/
override fun showAdUpgradeStatus(
upgradeMode: Int,
downloadStatus: Int,
currentProgress: Int,
totalProgress: Int,
downloadVersion: String,
upgradeStatus: Int
upgradeMode: Int,
downloadStatus: Int,
currentProgress: Int,
totalProgress: Int,
downloadVersion: String,
upgradeStatus: Int
) {
ThreadUtils.runOnUiThread{
ThreadUtils.runOnUiThread {
val tipsView = upgradeTipsView?.invoke()
//如果工控机处于“下载中”、“可升级(下载完成)”、“升级中”、“升级失败”状态时,工具箱入口显示红色角标
if(AdUpgradeStateHelper.showUpgradeTips(downloadStatus, upgradeStatus)){
viewUpgradeTips.visibility = View.VISIBLE
}else{
viewUpgradeTips.visibility = View.GONE
if (AdUpgradeStateHelper.showUpgradeTips(downloadStatus, upgradeStatus)) {
if (HmiBuildConfig.isShowUpgradeTipsView){
viewUpgradeTips?.visibility = View.VISIBLE
}else{
tipsView?.let {
it.visibility = View.VISIBLE
}
}
} else {
viewUpgradeTips?.visibility = View.GONE
tipsView?.let {
it.visibility = View.GONE
}
}
//将状态同步到工具箱
toolsView?.showAdUpgradeStatus(upgradeMode,downloadStatus, currentProgress, totalProgress, downloadVersion, upgradeStatus)
toolsView?.showAdUpgradeStatus(upgradeMode, downloadStatus, currentProgress, totalProgress, downloadVersion, upgradeStatus)
}
}

View File

@@ -0,0 +1,252 @@
package com.mogo.eagle.core.function.hmi.ui.carcorder
import android.animation.Animator
import android.app.Activity
import android.content.Context
import android.hardware.usb.UsbDevice
import android.os.Looper
import android.util.Log
import android.view.*
import android.view.animation.OvershootInterpolator
import com.mogo.cloud.live.manager.ILiveStreamManager
import com.mogo.cloud.live.manager.LiveStreamManagerImpl
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.notification.WarningFloat
import com.mogo.eagle.core.function.hmi.notification.anim.DefaultAnimator
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.usbcamera.UVCCameraHelper
import com.serenegiant.usb.ParentPreviewConstraintLayout
import com.serenegiant.usb.widget.CameraViewInterface
import kotlinx.android.synthetic.main.view_carcorder_preview.view.*
/**
* @author donghongyu
* @date 2021/9/30 8:46 下午
* USB-Camera 摄像头预览
*
* TODO 临时方案,后面考虑封装将摄像头数据流通过 Tensorflow-lite 处理后展示
*/
class CarcorderPreviewView private constructor(
context: Context
) : ParentPreviewConstraintLayout(context),
CameraViewInterface.Callback {
private val TAG = "CarcorderPreviewView"
private var mCameraHelper: UVCCameraHelper? = null
private var isRequest = false
private var isPreview = false
private var liveStreamManager: ILiveStreamManager? = null
init {
LayoutInflater.from(context).inflate(R.layout.view_carcorder_preview, this, true)
}
companion object {
private var mCarcorderPreviewViewFloat: WarningFloat.Builder? = null
@Volatile
var instance: CarcorderPreviewView? = null
fun getInstance(context: Activity): CarcorderPreviewView {
if (instance == null) {
synchronized(CarcorderPreviewView::class) {
if (instance == null) {
instance = CarcorderPreviewView(context)
}
}
}
return instance!!
}
/**
* 展示窗口
*/
fun show(context: Activity) {
if (mCarcorderPreviewViewFloat == null) {
val carcorderPreviewVie = getInstance(context)
mCarcorderPreviewViewFloat = WarningFloat.with(context)
.setTag("CarcorderPreviewView")
.setLayout(carcorderPreviewVie)
.setSidePattern(SidePattern.RIGHT)
.setGravity(Gravity.RIGHT, offsetY = 250)
.setImmersionStatusBar(true)
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)
?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)
?.setDuration(200)
})
.show()
} else {
dismiss()
}
}
/**
* 隐藏窗口
*/
fun dismiss() {
if (mCarcorderPreviewViewFloat != null) {
WarningFloat.dismiss(mCarcorderPreviewViewFloat!!.config.floatTag, false)
mCarcorderPreviewViewFloat = null
}
}
}
private val listener: UVCCameraHelper.OnMyDevConnectListener = object : UVCCameraHelper.OnMyDevConnectListener {
override fun onAttachDev(device: UsbDevice?) {
Log.d(TAG, "onAttachDev")
// request open permission
if (!isRequest) {
isRequest = true
mCameraHelper?.requestPermission(0)
}
}
override fun onDettachDev(device: UsbDevice) {
Log.d(TAG, "onDettachDev")
// close camera
if (isRequest) {
isRequest = false
mCameraHelper?.closeCamera()
showShortMsg(device.deviceName + " is out")
}
}
override fun onConnectDev(device: UsbDevice?, isConnected: Boolean) {
Log.d(TAG, "onConnectDev:isConnected=$isConnected")
if (!isConnected) {
showShortMsg("fail to connect,please check resolution params")
isPreview = false
} else {
isPreview = true
showShortMsg("相机连接中")
// initialize seekbar
// need to wait UVCCamera initialize over
Thread {
try {
Thread.sleep(2500)
} catch (e: InterruptedException) {
e.printStackTrace()
}
Looper.prepare()
if (mCameraHelper != null && mCameraHelper!!.isCameraOpened) {
Logger.d(TAG, "亮度(brightness):${mCameraHelper!!.getModelValue(UVCCameraHelper.MODE_BRIGHTNESS)}")
Logger.d(TAG, "对比度(contrast):${mCameraHelper!!.getModelValue(UVCCameraHelper.MODE_CONTRAST)}")
}
Looper.loop()
}.start()
}
}
override fun onDisConnectDev(device: UsbDevice?) {
Log.d(TAG, "onDisConnectDev")
showShortMsg("相机断开连接")
}
override fun onCancelDev(device: UsbDevice?) {
Log.d(TAG, "onCancelDev" + device?.deviceName)
}
}
private fun showShortMsg(msg: String) {
//Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
private fun initView() {
// step.1 initialize UVCCameraHelper
carcorderPreview.setCallback(this)
mCameraHelper = UVCCameraHelper.getInstance()
mCameraHelper?.setDefaultFrameFormat(UVCCameraHelper.FRAME_FORMAT_MJPEG)
mCameraHelper?.initUSBMonitor(context as Activity, carcorderPreview, listener)
mCameraHelper?.setOnPreviewFrameListener { nv21Yuv ->
Log.d(TAG, "onPreviewResult: " + nv21Yuv.size)
//Log.i(TAG, "onVideoFrame byte length: " + bytesLength);
if (liveStreamManager != null) {
// 将摄像头采集的YUV数据推送到ZEGO
liveStreamManager!!.notifyYUVData(nv21Yuv, 640, 480, 2)
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
initView()
// step.2 register USB event broadcast
if (mCameraHelper != null) {
mCameraHelper!!.registerUSB()
}
// 初始化直播流管理
// 初始化直播流管理
liveStreamManager = LiveStreamManagerImpl.getInstance((context as Activity).application,
MoGoAiCloudClientConfig.getInstance().sn, true)
// 设置状态回调
liveStreamManager!!.setLiveStatusChangeCallback { status ->
if (status == 0) {
Logger.d(TAG, "直播中……")
} else {
Logger.d(TAG, "直播结束……")
}
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
// step.3 unregister USB event broadcast
if (mCameraHelper != null) {
mCameraHelper!!.unregisterUSB()
}
if (liveStreamManager != null) {
// 停止
liveStreamManager!!.stopLiveStream()
// 释放资源
liveStreamManager!!.release()
}
}
override fun onSurfaceCreated(view: CameraViewInterface?, surface: Surface?) {
if (!isPreview && mCameraHelper!!.isCameraOpened) {
mCameraHelper!!.startPreview(carcorderPreview)
isPreview = true
}
}
override fun onSurfaceChanged(view: CameraViewInterface?, surface: Surface?, width: Int, height: Int) {
}
override fun onSurfaceDestroy(view: CameraViewInterface?, surface: Surface?) {
if (isPreview && mCameraHelper!!.isCameraOpened) {
mCameraHelper!!.stopPreview()
isPreview = false
}
}
}

View File

@@ -68,7 +68,6 @@ abstract class AbsLogView : ILogView, TouchProxy.OnTouchEventListener {
fun show(context: Context) {
if (isShow) {
Log.d("EmArrow", "isShow : $isShow")
return
}
performCreate(context)

View File

@@ -244,7 +244,6 @@ class LogInfoView : AbsLogView() {
mLogHint!!.visibility = View.VISIBLE
mLogRvWrap!!.visibility = View.GONE
val layoutParams = systemLayoutParams ?: return
Log.d("EmArrow", "minimize , layoutParams is not null")
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT

View File

@@ -11,6 +11,7 @@ import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.data.app.AppConfigInfo
import com.mogo.eagle.core.data.autopilot.*
import com.mogo.eagle.core.data.chain.ChainConstant
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.data.constants.MoGoConfig
import com.mogo.eagle.core.data.map.MogoLocation
@@ -35,11 +36,20 @@ import com.mogo.eagle.core.function.call.obu.CallerObuListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.logcatch.ILogViewListener
import com.mogo.eagle.core.function.hmi.ui.logcatch.LogInfoView
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr
import com.mogo.eagle.core.utilcode.util.*
import com.mogo.map.MogoMap
import com.mogo.map.uicontroller.VisualAngleMode
import com.mogo.map.uicontroller.VisualAngleMode.MAP_STYLE_VR_ANGLE_300
import com.mogo.map.uicontroller.VisualAngleMode.MAP_STYLE_VR_ANGLE_CROSS
import com.mogo.map.uicontroller.VisualAngleMode.MAP_STYLE_VR_ANGLE_TOP
import com.mogo.map.uicontroller.VisualAngleMode.MODE_CLOSE_SIGHT
import com.mogo.map.uicontroller.VisualAngleMode.MODE_LONG_SIGHT
import com.mogo.map.uicontroller.VisualAngleMode.MODE_MEDIUM_SIGHT
import com.mogo.module.common.MogoApisHandler
import kotlinx.android.synthetic.main.view_debug_setting.view.*
import java.util.*
@@ -50,13 +60,13 @@ import java.util.*
* 展示 本机、网络、工控机、OBU等状态信息支持设置IP等参数进行调试
*/
class DebugSettingView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoObuStatusListener,
IMoGoAutopilotStatusListener, IMoGoAutopilotCarStateListener,
IMoGoMapLocationListener, IMoGoAutopilotIdentifyListener,
IMoGoAutopilotPlanningListener {
IMoGoAutopilotStatusListener, IMoGoAutopilotCarStateListener,
IMoGoMapLocationListener, IMoGoAutopilotIdentifyListener,
IMoGoAutopilotPlanningListener {
private val TAG = "DebugSettingView"
@@ -75,6 +85,12 @@ class DebugSettingView @JvmOverloads constructor(
// 全局路径规划点个数
private var mRouteInfoSize = 0
private val mapUiController by lazy {
MogoApisHandler.getInstance().apis?.mapServiceApi?.mapUIController
}
private var lastVisualAngleMode: VisualAngleMode? = null
init {
LayoutInflater.from(context).inflate(R.layout.view_debug_setting, this, true)
initView()
@@ -204,21 +220,40 @@ class DebugSettingView @JvmOverloads constructor(
}
}
changesight_top_btn.setOnClickListener {
CallerHDMapManager.setMapDAngle(0);
lastVisualAngleMode = mapUiController?.currentMapVisualAngle
changesight_top_btn.onClick {
mapUiController?.changeMapVisualAngle(MAP_STYLE_VR_ANGLE_TOP, null)
}
changesight_back_btn?.onClick {
mapUiController?.changeMapVisualAngle(MAP_STYLE_VR_ANGLE_300, null)
}
changesight_cross_btn?.onClick {
mapUiController?.changeMapVisualAngle(MAP_STYLE_VR_ANGLE_CROSS, null)
}
changesight_far_btn?.onClick {
mapUiController?.changeMapVisualAngle(MODE_LONG_SIGHT, null)
}
reset_changesight?.onClick {
lastVisualAngleMode?.let {
mapUiController?.changeMapVisualAngle(it, null)
}
}
tvObuInfo.text = CallerObuListenerManager.getObuStatusInfoJsonString()
tvAutopilotInfo.text =
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfoJsonString()
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfoJsonString()
// 绘制应用基本信息
drawAppInfo()
// 初始化OBU IP信息
val ipAddress =
SharedPrefsMgr.getInstance(context).getString(MoGoConfig.OBU_IP, "192.168.1.199")
SharedPrefsMgr.getInstance(context).getString(MoGoConfig.OBU_IP, "192.168.1.199")
etObuIP.setText(ipAddress)
etObuIP.text?.let { etObuIP.setSelection(it.length) }
@@ -255,18 +290,18 @@ class DebugSettingView @JvmOverloads constructor(
// 初始化 GSP数据源 数据
rgGpsProvider.check(
when (FunctionBuildConfig.gpsProvider) {
0 -> {
R.id.rbGpsProviderAndroid
}
1 -> {
R.id.rbGpsProviderRTK
}
2 -> {
R.id.rbGpsProviderOBU
}
else -> R.id.rbGpsProviderAndroid
when (FunctionBuildConfig.gpsProvider) {
0 -> {
R.id.rbGpsProviderAndroid
}
1 -> {
R.id.rbGpsProviderRTK
}
2 -> {
R.id.rbGpsProviderOBU
}
else -> R.id.rbGpsProviderAndroid
}
)
rgGpsProvider.setOnCheckedChangeListener { group, checkedId ->
when (checkedId) {
@@ -350,31 +385,29 @@ class DebugSettingView @JvmOverloads constructor(
}
tbLogCatch.isChecked =
SharedPrefsMgr.getInstance(context).getBoolean(MoGoConfig.CATCH_LOG, false)
SharedPrefsMgr.getInstance(context).getBoolean(MoGoConfig.CATCH_LOG, false)
tbLogCatch.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
CallerDevaToolsManager.startCatchLog()
SharedPrefsMgr.getInstance(context).putBoolean(MoGoConfig.CATCH_LOG, true)
} else {
CallerDevaToolsManager.stopCatchLog()
SharedPrefsMgr.getInstance(context).putBoolean(MoGoConfig.CATCH_LOG, false)
}
}
CallerDevaToolsListenerManager.registerDevaToolsLogCatchListener(TAG,
object : IMoGoDevaToolsListener {
override fun onLogCatchClose() {
super.onLogCatchClose()
tbLogCatch.isChecked = false
}
object : IMoGoDevaToolsListener {
override fun onLogCatchClose() {
super.onLogCatchClose()
tbLogCatch.isChecked = false
}
override fun onLogCatch(lineLog: String) {
logInfoView?.let {
if (logViewAttach) {
it.onLogCatch(lineLog)
}
override fun onLogCatch(lineLog: String) {
logInfoView?.let {
if (logViewAttach) {
it.onLogCatch(lineLog)
}
}
})
}
})
tbLogDebugView.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
logInfoView = LogInfoView()
@@ -394,6 +427,15 @@ class DebugSettingView @JvmOverloads constructor(
logViewDestroy()
}
}
tbLogTraceView.setOnCheckedChangeListener { _, isChecked ->
val traceInfoMap = CallerDevaToolsManager.getTraceInfo()
val chainLogParam = traceInfoMap[ChainConstant.CHAIN_LINK_LOG_WEB_SOCKET_DATA]
chainLogParam?.let {
it.record = isChecked
traceInfoMap[ChainConstant.CHAIN_LINK_LOG_WEB_SOCKET_DATA] = chainLogParam
CallerDevaToolsManager.refreshTraceInfo(traceInfoMap)
}
}
}
private fun logViewDestroy() {
@@ -428,15 +470,15 @@ class DebugSettingView @JvmOverloads constructor(
tvAutopilotInfo.text = GsonUtils.toJson(mAutoPilotStatusInfo)
tvCarInfo.text =
"GPS时间${mAutoPilotCarStateInfo?.values?.satelliteTime}\n" +
"自车经纬度:\n${mAutoPilotCarStateInfo?.values?.lon}\n${mAutoPilotCarStateInfo?.values?.lat}\n"
"GPS时间${mAutoPilotCarStateInfo?.values?.satelliteTime}\n" +
"自车经纬度:\n${mAutoPilotCarStateInfo?.values?.lon}\n${mAutoPilotCarStateInfo?.values?.lat}\n"
tvIdentifyInfo.text =
"感知数据个数:${mIdentifyDataSize}"
"感知数据个数:${mIdentifyDataSize}"
tvTrajectoryInfoSize.text =
"引导线点个数:${mTrajectoryInfoSize}"
"引导线点个数:${mTrajectoryInfoSize}"
tvRouteInfoSize.text =
"全局路径规划点个数:${mRouteInfoSize}"
"全局路径规划点个数:${mRouteInfoSize}"
// 用完之后重制为0防止节点回掉突然没数据导致页面显示还是之前的数据情况
mIdentifyDataSize = 0

View File

@@ -8,11 +8,13 @@ import android.view.View
import android.widget.FrameLayout
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.utils.KeyBoardUtil
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import kotlinx.android.synthetic.main.view_auto_pilot_check.view.*
import kotlinx.android.synthetic.main.view_check_system.view.*
@@ -102,6 +104,7 @@ class AutoPilotAndCheckView @JvmOverloads constructor(
return@setOnTouchListener false
}
}
updateSpeedSettingViews()
// // 比如需要设置默认速度
// val speed = "30"
// etInputSpeed.setText(speed)
@@ -112,6 +115,22 @@ class AutoPilotAndCheckView @JvmOverloads constructor(
this.clickListener = clickListener
}
/**
* Bus不可设置自动驾驶速度而Taxi可以
*/
private fun updateSpeedSettingViews() {
when {
AppIdentityModeUtils.isBus(FunctionBuildConfig.appIdentityMode) -> {
tvSpeedTitle.visibility = View.GONE
llSpeedPosition.visibility = View.GONE
}
else -> {
tvSpeedTitle.visibility = View.VISIBLE
llSpeedPosition.visibility = View.VISIBLE
}
}
}
/**
* 展示工控机下载、升级状态信息
* @param upgradeMode 升级模式(提示升级、静默升级)

View File

@@ -71,6 +71,12 @@ class SystemVersionView @JvmOverloads constructor(
if(AdUpgradeStateHelper.isDownloading(downloadStatus)){
//点击Toast提示下载剩余时间
ToastUtils.showShort("预计"+AdUpgradeStateHelper.getRemainingTime(totalProgress,previousProgress,currentProgress)+"下载完成")
}else if(AdUpgradeStateHelper.getUpgradeStatus()){
//工控机状态为“升级中”
ToastUtils.showShort("新版本升级中预计5分钟升级完成")
}else if(AdUpgradeStateHelper.isUpgradeFailed(upgradeStatus)){
//如果升级失败则Toast提示升级失败请联系运维人员
ToastUtils.showShort("升级失败,请联系运维人员")
}else if(AdUpgradeStateHelper.isHintUpgradeMode(upgradeMode) && AdUpgradeStateHelper.isDownloadFinish(downloadStatus,upgradeStatus)){
//如果升级模式为“提示升级”,并且下载状态为已经下载完成,点击弹出升级确认弹窗
if(adUpgradeDialog == null){
@@ -86,6 +92,10 @@ class SystemVersionView @JvmOverloads constructor(
//设置当前状态为“升级中”
AdUpgradeStateHelper.setUpgradeStatus(true)
CallerAutoPilotManager.setIPCUpgradeAffirm()
//将角标设为升级中
ivAdStatus?.setImageResource(R.drawable.icon_upgrading)
adCircularProgressView?.visibility = View.GONE
ivAdVersion?.setBackgroundResource(R.drawable.version_latest_background)
}
}
@@ -99,12 +109,6 @@ class SystemVersionView @JvmOverloads constructor(
})
}
adUpgradeDialog?.showUpgradeDialog()
}else if(AdUpgradeStateHelper.getUpgradeStatus()){
//工控机状态为“升级中”
ToastUtils.showShort("新版本升级中预计5分钟升级完成")
}else if(AdUpgradeStateHelper.isUpgradeFailed(upgradeStatus)){
//如果升级失败则Toast提示升级失败请联系运维人员
ToastUtils.showShort("升级失败,请联系运维人员")
}
}

View File

@@ -112,8 +112,8 @@
<View
android:id="@+id/viewUpgradeTips"
android:layout_width="20px"
android:layout_height="20px"
android:layout_width="22px"
android:layout_height="22px"
app:layout_constraintCircle="@id/ivToolsIcon"
app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="60px"

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/dp_768"
android:layout_height="@dimen/dp_432">
<com.serenegiant.usb.widget.UVCCameraTextureView
android:id="@+id/carcorderPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -413,6 +413,16 @@
android:textOn="关闭日志过滤面板"
android:textSize="@dimen/dp_24" />
<ToggleButton
android:id="@+id/tbLogTraceView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:gravity="center"
android:textOff="ADAS长链数据未写入"
android:textOn="ADAS长链数据写入中..."
android:textSize="@dimen/dp_24" />
</com.google.android.flexbox.FlexboxLayout>
</LinearLayout>
<!--域控制器(工控机)配置信息-->

View File

@@ -68,7 +68,6 @@ dependencies {
api rootProject.ext.dependencies.mogocommons
api rootProject.ext.dependencies.modulecommon
api rootProject.ext.dependencies.mogoservice
api rootProject.ext.dependencies.moduleshare
api rootProject.ext.dependencies.moduleextensions
api rootProject.ext.dependencies.callchat
api rootProject.ext.dependencies.callchatprovider

View File

@@ -61,7 +61,6 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
return;
}
start = System.currentTimeMillis();
ChainTraceStarter.start("com.mogo.launcher.f", DeviceUtils.getMacAddress());
// Crash 日志收集
initCrashConfig();
initLogConfig();
@@ -153,7 +152,7 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
// 设置是否输出日志
clientConfig.setShowDebugLog(true);
// 设置是否是直播推流的主播
clientConfig.setAnchor(false);
clientConfig.setAnchor(true);
// 设置从蘑菇AI开放平台获取的APPKey
switch (DebugConfig.getCarMachineType()) {
// 比亚迪
@@ -178,8 +177,8 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
// 设置AI云平台分配给三方应用的签名密钥需要从AI云平台申请
// 设置车机设备的唯一标识(这些表识必须是通过后台录入的设备)
clientConfig.setThirdPartyDeviceId(Utils.getDevicesId());
// 设置循环检测间隔时间
clientConfig.setLoopCheckDelay(60 * 60 * 24 * 1000);
// 设置循环检测间隔时间每隔2小时loop一次httpDnsConfig
clientConfig.setLoopCheckDelay(60 * 60 * 2 * 1000);
//连接ami
connectAmiIp();
@@ -302,7 +301,8 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
private void initModules() {
Logger.d(TAG, "initModules");
//mogo deva tools
MogoModulePaths.addModuleFunctionServer(new MogoModule(MogoServicePaths.PATH_DEVA_TOOLS, "IMoGoDevaToolsProvider"));
// 初始化 bugly 升级
MogoModulePaths.addBaseModule(new MogoModule(UpgradeReportConstants.PATH, UpgradeReportConstants.NAME));
// 初始化 apm 日志采集
@@ -312,9 +312,8 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
MogoModulePaths.addBaseModule(new MogoModule(MapApiPath.PATH, "CustomMapApiBuilder"));
MogoModulePaths.addBaseModule(new MogoModule(ServiceConst.PATH_REFRESH_STRATEGY, ServiceConst.PATH_REFRESH_STRATEGY));
// MogoModulePaths.addBaseModule(new MogoModule(V2XConst.PATH_V2X_UI, V2XConst.MODULE_NAME));
//mogo deva tools
MogoModulePaths.addModuleFunctionServer(new MogoModule(MogoServicePaths.PATH_DEVA_TOOLS, "IMoGoDevaToolsProvider"));
// MogoModulePaths.addBaseModule(new MogoModule(V2XConst.PATH_V2X_UI, V2XConst.MODULE_NAME));
// 域控制器模块(新)
MogoModulePaths.addModuleFunctionServer(new MogoModule(MogoServicePaths.PATH_AUTO_PILOT, "IMoGoAutoPilotProvider"));
// OBU 模块

View File

@@ -50,7 +50,7 @@ dependencies {
implementation rootProject.ext.dependencies.rxandroid
kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.adasHigh
//implementation rootProject.ext.dependencies.adasHigh
implementation rootProject.ext.dependencies.mogocustommapoperational
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.mogoserviceapi
@@ -74,6 +74,8 @@ dependencies {
implementation project(":libraries:mogo-map")
implementation project(":libraries:mogo-map-api")
implementation project(':libraries:mogo-adas')
}
}

View File

@@ -53,7 +53,7 @@ dependencies {
implementation rootProject.ext.dependencies.flexbox
kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.adasHigh
// implementation rootProject.ext.dependencies.adasHigh
implementation rootProject.ext.dependencies.mogo_v2x
implementation rootProject.ext.dependencies.mogoaicloudtrafficlive
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
@@ -75,6 +75,8 @@ dependencies {
implementation project(':modules:mogo-module-carchattingprovider')
implementation project(':core:mogo-core-res')
implementation project(':libraries:mogo-adas')
}
}

View File

@@ -1,16 +0,0 @@
package com.mogo.eagle.core.function.v2x.events.widgets;
import com.mogo.commons.mvp.IView;
import com.mogo.module.common.entity.MarkerExploreWay;
import java.util.List;
/**
* @author lixiaopeng
* @description
* @since 2020/7/29
*/
public interface SurroundingEventView extends IView {
void showSurroudingData(List<MarkerExploreWay> exploreWayList);
}

View File

@@ -1,36 +0,0 @@
package com.mogo.eagle.core.function.v2x.events.widgets;
import android.graphics.Rect;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* @author lixiaopeng
* @description
* @since 2020/8/11
*/
public class SurroundingMarginDecoration extends RecyclerView.ItemDecoration {
private int margin ;
private int marginLeft ;
public SurroundingMarginDecoration(int space, int left) {
margin = space;
marginLeft = left;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.bottom = margin;
//由于每行都只有2个所以第一个都是2的倍数
if (parent.getChildLayoutPosition(view) % 2 == 0) {
outRect.left = marginLeft;
outRect.right = margin / 2;
} else {
outRect.left = margin / 2;
outRect.right = marginLeft;
}
}
}

View File

@@ -1,25 +1,10 @@
package com.mogo.eagle.core.function.v2x.trafficlight
import com.mogo.commons.debug.DebugConfig
class TrafficLightConst {
companion object {
const val MODULE_NAME = "MODULE_V2X_TRAFFIC_LIGHT"
private const val HOST_DEV = "http://dzt-test.zhidaozhixing.com"
private const val HOST_TEST = "http://dzt-test.zhidaozhixing.com"
private const val HOST_DEMO = "http://dzt-show.zhidaozhixing.com"
private const val HOST_PRODUCT = "http://dzt.zhidaozhixing.com"
fun getNetHost(): String {
return when (DebugConfig.getNetMode()) {
DebugConfig.NET_MODE_DEV -> HOST_DEV
DebugConfig.NET_MODE_QA -> HOST_TEST
DebugConfig.NET_MODE_DEMO -> HOST_DEMO
else -> HOST_PRODUCT
}
}
}
}

View File

@@ -1,8 +1,5 @@
package com.mogo.eagle.core.data.autopilot
import android.util.Log
import java.util.logging.Logger
/**
* @author XuXinChao
* @description 工控机升级状态实体类
@@ -32,6 +29,7 @@ class AdUpgradeStateHelper {
* @param downloadStatus 下载状态
* @param upgradeStatus 升级状态
*/
@JvmStatic
fun showUpgradeTips(downloadStatus: Int,upgradeStatus: Int) : Boolean{
return isDownloading(downloadStatus) || isDownloadFinish(downloadStatus,upgradeStatus) || getUpgradeStatus() || isUpgradeFailed(upgradeStatus)
}
@@ -41,6 +39,7 @@ class AdUpgradeStateHelper {
* @param downloadStatus 下载状态
* @param upgradeStatus 升级状态
*/
@JvmStatic
fun showCannotReboot(downloadStatus: Int,upgradeStatus: Int): Boolean{
return isDownloading(downloadStatus)|| isDownloadFinish(downloadStatus,upgradeStatus) || getUpgradeStatus()
}
@@ -49,6 +48,7 @@ class AdUpgradeStateHelper {
* 工控机是否处于“下载中”状态
* @param downloadStatus 下载状态
*/
@JvmStatic
fun isDownloading(downloadStatus: Int) : Boolean{
return downloadStatus == DOWNLOAD_START
}
@@ -58,6 +58,7 @@ class AdUpgradeStateHelper {
* @param downloadStatus 下载状态
* @param upgradeStatus 升级状态
*/
@JvmStatic
fun isDownloadFinish(downloadStatus: Int,upgradeStatus: Int) : Boolean{
return downloadStatus == DOWNLOAD_FINISH && upgradeStatus == USER_AFFIRM
}
@@ -66,6 +67,7 @@ class AdUpgradeStateHelper {
* 工控机是否处于“下载失败”状态
* @param downloadStatus 下载状态
*/
@JvmStatic
fun isDownloadFailed(downloadStatus: Int) : Boolean{
return downloadStatus == DOWNLOAD_FAILED
}
@@ -74,6 +76,7 @@ class AdUpgradeStateHelper {
* 工控机是否处于“升级成功”状态
* @param upgradeStatus 升级状态
*/
@JvmStatic
fun isUpgradeSuccess(upgradeStatus: Int) : Boolean{
return upgradeStatus == UPGRADE_SUCCEED
}
@@ -82,6 +85,7 @@ class AdUpgradeStateHelper {
* 工控机是否处于“升级失败”状态
* @param upgradeStatus 升级状态
*/
@JvmStatic
fun isUpgradeFailed(upgradeStatus: Int) : Boolean{
return upgradeStatus == UPGRADE_FAILED
}
@@ -91,6 +95,7 @@ class AdUpgradeStateHelper {
* @param currentProgress 当前已下载包体大小
* @param totalProgress 包体总大小
*/
@JvmStatic
fun downloadProgress(currentProgress: Int,totalProgress: Int) : Int{
return (currentProgress.toDouble()/totalProgress.toDouble()*100).toInt()
}
@@ -99,6 +104,7 @@ class AdUpgradeStateHelper {
* 工控机升级模式是否是静默升级
* @param upgradeMode 升级模式
*/
@JvmStatic
fun isQuietUpgradeMode(upgradeMode: Int) : Boolean{
return upgradeMode == UPGRADE_QUIET
}
@@ -107,6 +113,7 @@ class AdUpgradeStateHelper {
* 工控机升级模式是否是提示升级
* @param upgradeMode 升级模式
*/
@JvmStatic
fun isHintUpgradeMode(upgradeMode: Int) : Boolean{
return upgradeMode == UPGRADE_HINT
}
@@ -114,6 +121,7 @@ class AdUpgradeStateHelper {
/**
* 获取是否处于“升级中”状态
*/
@JvmStatic
fun getUpgradeStatus() : Boolean{
return UPGRADING
}
@@ -122,6 +130,7 @@ class AdUpgradeStateHelper {
* 设置是否处于“升级中”状态
* @param upgrading 是否是升级中
*/
@JvmStatic
fun setUpgradeStatus(upgrading: Boolean){
UPGRADING = upgrading
}
@@ -129,6 +138,7 @@ class AdUpgradeStateHelper {
/**
* 获取工控机包体下载剩余时间
*/
@JvmStatic
fun getRemainingTime(totalProgress: Int,previousProgress: Int,currentProgress: Int) : String{
//剩余包体大小
val remainingSize = totalProgress - currentProgress
@@ -139,14 +149,14 @@ class AdUpgradeStateHelper {
//转换为分秒格式返回
val minute = time/60
val second = time%60
if(minute>0 && second>0){
return minute.toString()+"分钟"+second+""
return if(minute>0 && second>0){
minute.toString()+"分钟"+second+""
}else if(minute>0){
return minute.toString()+"分钟"
minute.toString()+"分钟"
}else if(second>0){
return second.toString()+""
second.toString()+""
}else{
return ""
""
}
}

View File

@@ -0,0 +1,31 @@
package com.mogo.eagle.core.data.chain
class ChainConstant {
companion object{
const val CHAIN_LINK_CLOUD_SHOW = 0
const val CHAIN_LINK_ADAS = 1
const val CHAIN_LINK_LOG_CONNECT_STATUS = 0
const val CHAIN_LINK_LOG_WEB_SOCKET_DATA = 1
const val CHAIN_LINK_LOG_ADAS_INIT = "-adasInitStatus"
const val CHAIN_LINK_LOG_ADAS_MSG = "-adasWsMsg"
const val CHAIN_ALIAS_CODE_UDP_INIT = "PAD_ADAS_UDP_INIT"
const val CHAIN_ALIAS_CODE_UDP_CONNECT_ADDRESS = "PAD_ADAS_UDP_CONNECT_ADDRESS"
const val CHAIN_ALIAS_CODE_WEB_SOCKET_OPEN = "PAD_ADAS_WEB_SOCKET_OPEN"
const val CHAIN_ALIAS_CODE_WEB_SOCKET_MESSAGE_JSON = "PAD_ADAS_WEB_SOCKET_MESSAGE_JSON"
const val CHAIN_ALIAS_CODE_WEB_SOCKET_MESSAGE_BYTE = "PAD_ADAS_WEB_SOCKET_MESSAGE_BYTE"
const val CHAIN_ALIAS_CODE_ADAS_MESSAGE_RECT_DATA = "PAD_ADAS_MESSAGE_AUTOPILOT_RECT_DATA"
const val CHAIN_ALIAS_CODE_ADAS_MESSAGE_CAR_STATE = "PAD_ADAS_MESSAGE_AUTOPILOT_CAR_STATE"
const val CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_STATUS = "PAD_ADAS_MESSAGE_AUTOPILOT_STATUS"
const val CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_ARRIVE = "PAD_ADAS_MESSAGE_AUTOPILOT_ARRIVE"
const val CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_ROUTE = "PAD_ADAS_MESSAGE_AUTOPILOT_ROUTE"
const val CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_TRAJECTORY = "PAD_ADAS_MESSAGE_AUTOPILOT_TRAJECTORY"
const val CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_GUARDIAN = "PAD_ADAS_MESSAGE_AUTOPILOT_GUARDIAN"
const val CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_RECORD = "PAD_ADAS_MESSAGE_AUTOPILOT_RECORD"
const val CHAIN_ALIAS_CODE_ADAS_MESSAGE_AUTOPILOT_WARN = "PAD_ADAS_MESSAGE_AUTOPILOT_WARN"
}
}

View File

@@ -0,0 +1,13 @@
package com.mogo.eagle.core.data.chain
class ChainLogParam {
var record: Boolean = false
var des: String? = null
constructor(record: Boolean, des: String) {
this.record = record
this.des = des
}
}

View File

@@ -36,6 +36,12 @@ object HmiBuildConfig {
@JvmField
var isShowBadCaseView = true
/**
* 是否展示工控机升级提示UI
*/
@JvmField
var isShowUpgradeTipsView = true
/**
* 是否展示转向灯ui
*/

View File

@@ -14,6 +14,8 @@ object MoGoConfig {
// CMD全量日志抓取
const val CATCH_LOG = "CATCH_LOG"
// CMD全量日志抓取当时时间
const val CATCH_LOG_TIME = "CATCH_LOG_TIME"
// 是否是演示美化模式会存在SP中方便做现场恢复
const val IS_DEMO_MODE = "IS_DEMO_MODE"

View File

@@ -1,5 +1,6 @@
package com.mogo.eagle.core.function.api.devatools
import com.mogo.eagle.core.data.chain.ChainLogParam
import com.mogo.eagle.core.function.api.base.IMoGoFunctionServerProvider
/**
@@ -9,5 +10,12 @@ interface IDevaToolsProvider : IMoGoFunctionServerProvider {
fun startLogCatch()
fun startLogCatch(duration: Int)
fun stopLogCatch()
fun getTraceInfo():HashMap<Int, ChainLogParam>
fun refreshTraceInfo(map: HashMap<Int, ChainLogParam>)
}

View File

@@ -193,6 +193,12 @@ interface IMoGoWaringProvider {
*/
fun registerBadCaseCallback(onShow:() -> View, onHide: (() -> Unit)?)
/**
*注册工控机升级提示圆点View的回调
* @param 提示圆点View
*/
fun registerUpgradeTipsCallback(tipsView:() -> View)
/**
* 工控机重启返回结果
* @param code

View File

@@ -1,8 +1,10 @@
package com.mogo.eagle.core.function.call.devatools
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.chain.ChainLogParam
import com.mogo.eagle.core.data.constants.MogoServicePaths.PATH_DEVA_TOOLS
import com.mogo.eagle.core.function.api.devatools.IDevaToolsProvider
import com.mogo.eagle.core.utilcode.util.SnackbarUtils
object CallerDevaToolsManager {
@@ -17,10 +19,32 @@ object CallerDevaToolsManager {
devaToolsProviderApi.startLogCatch()
}
/**
* 开始抓取全量日志
* duration 分钟数
*/
fun startCatchLog(duration: Int){
devaToolsProviderApi.startLogCatch(duration)
}
/**
* 停止抓取全量日志
*/
fun stopCatchLog() {
devaToolsProviderApi.stopLogCatch()
}
/**
* 更新链路节点信息,是否写入
*/
fun refreshTraceInfo(map: HashMap<Int, ChainLogParam>) {
devaToolsProviderApi.refreshTraceInfo(map)
}
/**
* 获取链路节点信息
*/
fun getTraceInfo():HashMap<Int, ChainLogParam>{
return devaToolsProviderApi.getTraceInfo()
}
}

View File

@@ -273,6 +273,14 @@ object CallerHmiManager : CallerBase() {
waringProviderApi?.registerBadCaseCallback(onShow, onHide)
}
/**
*注册工控机升级提示圆点View的回调
* @param 提示圆点View
*/
fun registerUpgradeTipsCallback(tipsView:() -> View){
waringProviderApi?.registerUpgradeTipsCallback(tipsView)
}
/**
* 工控机重启返回结果
* @param code

View File

@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.eagle.core.utilcode.util">
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<application>
<activity

View File

@@ -17,6 +17,13 @@ import android.view.Gravity;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;
import com.mogo.eagle.core.utilcode.kotlin.ExtensionsKt;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
@@ -34,7 +41,7 @@ public final class TipToast {
private static ToastViewGenerator sGenerator;
public static void init( Context context, ToastViewGenerator generator ) {
TipToast.sContext = context;
TipToast.sContext = context.getApplicationContext();
sHandler = new Handler( context.getMainLooper() );
sGenerator = generator;
}
@@ -165,20 +172,21 @@ public final class TipToast {
sHandler.post(() -> {
synchronized ( sSyncObject ) {
if ( context == null ) {
return;
}
if ( sToast != null ) {
sToast.cancel();
if ( sToast != null) {
View view = sToast.getView();
if (view != null && ViewCompat.isAttachedToWindow(view)) {
sToast.cancel();
}
}
if ( sGenerator == null ) {
sToast = Toast.makeText( context, msg, duration );
} else {
sToast = new Toast( context );
final View view = sGenerator.make( context, msg, tipDrawable );
if ( view != null ) {
sToast.setView( view );
sToast.setGravity( sGenerator.gravity(), sGenerator.xOffset(), sGenerator.yOffset() );
@@ -187,6 +195,15 @@ public final class TipToast {
sToast = Toast.makeText( context, msg, duration );
}
}
View view = sToast.getView();
if (view != null) {
LifecycleOwner lifecycleOwner = ExtensionsKt.getLifecycleOwner(view);
lifecycleOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> {
if (event == Lifecycle.Event.ON_DESTROY) {
sToast = null;
}
});
}
if ( sToast != null ) {
sToast.show();
}

View File

@@ -3,38 +3,40 @@ package com.mogo.eagle.core.utilcode.util;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.MediaDrm;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.elegant.utils.storage.SharedPrefsMgr;
import androidx.core.content.ContextCompat;
import com.elegant.utils.storage.SharedPrefsMgr;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.UUID;
public final class DeviceIdUtils {
public static final String KEY_DEVICE_ID = "deviceId";
private DeviceIdUtils() {}
private DeviceIdUtils() {
}
private static void saveDeviceId( Context context, String deviceId){
private static void saveDeviceId(Context context, String deviceId) {
SharedPrefsMgr.getInstance(context).putString(KEY_DEVICE_ID, deviceId);
}
public static String getDeviceId( Context context) {
if(context == null){
public static String getDeviceId(Context context) {
if (context == null) {
throw new NullPointerException("context must not be null.");
}
final Context appContext = context.getApplicationContext();
String deviceId = SharedPrefsMgr.getInstance( context ).getString( KEY_DEVICE_ID );
String deviceId = SharedPrefsMgr.getInstance(context).getString(KEY_DEVICE_ID);
if ( TextUtils.isEmpty( deviceId )) {
if (TextUtils.isEmpty(deviceId)) {
deviceId = getDeviceIdInternal(appContext);
if (TextUtils.isEmpty(deviceId)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@@ -54,35 +56,37 @@ public final class DeviceIdUtils {
}
}
}
saveDeviceId(appContext,deviceId);
saveDeviceId(appContext, deviceId);
}
return deviceId;
}
private static String getDeviceIdInternal( Context context) {
private static String getDeviceIdInternal(Context context) {
String id = "";
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
if ( ContextCompat.checkSelfPermission( context, Manifest.permission.READ_PHONE_STATE ) != PackageManager.PERMISSION_GRANTED ) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
return id;
}
}
TelephonyManager telephonymanager = ( TelephonyManager ) context.getSystemService( Context.TELEPHONY_SERVICE);
TelephonyManager telephonymanager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonymanager != null) {
id = telephonymanager.getDeviceId();
if ( TextUtils.isEmpty(id))
if (TextUtils.isEmpty(id)) {
id = "";
}
}
return id;
}
private static String getAndroidId( Context context) {
private static String getAndroidId(Context context) {
String s = "";
s = Settings.Secure.getString(context.getContentResolver(), "android_id");
if ( TextUtils.isEmpty(s))
if (TextUtils.isEmpty(s)) {
s = "";
}
return s;
}
@@ -95,16 +99,63 @@ public final class DeviceIdUtils {
if (!method.isAccessible()) {
method.setAccessible(true);
}
serial = ( String ) method.invoke(new Build(), "ro.serialno");
} catch ( ClassNotFoundException e) {
serial = (String) method.invoke(new Build(), "ro.serialno");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch ( NoSuchMethodException e) {
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch ( InvocationTargetException e) {
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch ( IllegalAccessException e) {
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return serial;
}
/**
* 获取数字版权管理设备ID
*
* @return WidevineID可能为空
*/
public static String getWidevineID(Context context) {
try {
//See https://stackoverflow.com/questions/16369818/how-to-get-crypto-scheme-uuid
//You can find some UUIDs in the https://github.com/google/ExoPlayer source code
final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID);
byte[] widevineId = mediaDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID);
if (widevineId == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (byte aByte : widevineId) {
sb.append(String.format("%02x", aByte));
}
return sb.toString();
} catch (Exception | Error e) {
e.printStackTrace();
}
return "";
}
/**
* 获取数字版权管理设备ID,进行MD5加密获取32位的唯一标记给后台生成SN
*
* @return WidevineID可能为空
*/
public static String getWidevineIDWithMd5(Context context) {
try {
String widevineId = getWidevineID(context);
if (!TextUtils.isEmpty(widevineId)) {
widevineId = EncryptUtils.encryptHmacMD5ToString(widevineId, "MoGoAuto");
return widevineId;
} else {
return getDeviceId(context);
}
} catch (Exception | Error e) {
e.printStackTrace();
}
return getDeviceId(context);
}
}

View File

@@ -55,14 +55,6 @@ public final class TimeUtils {
throw new UnsupportedOperationException("u can't instantiate me...");
}
@SuppressLint("SimpleDateFormat")
public static String formatYMD(long time){
Date date = new Date(time);
String strDateFormat = "yyyy-MM-dd";
SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
return sdf.format(date);
}
/**
* Milliseconds to the formatted time string.
* <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>
@@ -1532,8 +1524,8 @@ public final class TimeUtils {
return CHINESE_ZODIAC[year % 12];
}
private static final int[] ZODIAC_FLAGS = {20, 19, 21, 21, 21, 22, 23, 23, 23, 24, 23, 22};
private static final String[] ZODIAC = {
private static final int[] ZODIAC_FLAGS = {20, 19, 21, 21, 21, 22, 23, 23, 23, 24, 23, 22};
private static final String[] ZODIAC = {
"水瓶座", "双鱼座", "白羊座", "金牛座", "双子座", "巨蟹座",
"狮子座", "处女座", "天秤座", "天蝎座", "射手座", "摩羯座"
};

View File

@@ -179,13 +179,12 @@ public abstract class AbsMogoApplication extends Application {
*/
protected void registerSocketHttpDnsTTL(String host) {
sApis.addressChangedListener(map -> {
Logger.d("TEST-SOCKET", "ttl callBack ,ready to getCache Dns IP");
String dnsCacheIp = sApis.getCachedHttpDnsIps(host, HTTP_DNS_ADDRESS_TYPE_HTTP);
if (dnsCacheIp == null) {
return;
}
Logger.d("TEST-SOCKET", "获取缓存Dns IP : " + dnsCacheIp + " , 原缓存 IP " + cacheIp);
if (!dnsCacheIp.equals(cacheIp)) {
Logger.d("TEST-SOCKET", "获取缓存Dns IP : " + dnsCacheIp + " , 原缓存 IP " + cacheIp);
socketTTL();
this.cacheIp = dnsCacheIp;
}

View File

@@ -32,12 +32,12 @@ kapt.include.compile.classpath=false
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# Android operating system, and which are packaged with your app'protoc_platforms APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
android.jetifier.blacklist=module-service-2.1.16.8.aar
android.jetifier.blacklist=module-service-2.1.16.10.aar
## maven 配置
RELEASE_REPOSITORY_URL=http://nexus.zhidaoauto.com/repository/maven-releases/
SNAPSHOT_REPOSITORY_URL=http://nexus.zhidaoauto.com/repository/maven-snapshots/
@@ -56,28 +56,30 @@ bytex.forbidUseLenientMutationDuringGetArtifact=true
bytex.verifyProguardConfigurationChanged=false
bytex.ASM_API=ASM7
HOOK_LOG_VERSION=1.4.109
SERVICE_CHAIN_VERSION=1.0.43
HOOK_LOG_VERSION=1.5.17
SERVICE_CHAIN_VERSION=1.0.53
################ 外部依赖引用 ################
# loglib
LOGLIB_VERSION=1.1.18
LOGLIB_VERSION=1.2.8
######## MogoAiCloudSDK Version ########
# 网络请求
MOGO_NETWORK_VERSION=1.3.18
MOGO_NETWORK_VERSION=1.3.30
# 鉴权
MOGO_PASSPORT_VERSION=1.3.18
MOGO_PASSPORT_VERSION=1.3.30
# 常链接
MOGO_SOCKET_VERSION=1.3.18
MOGO_SOCKET_VERSION=1.3.30
# 数据采集
MOGO_REALTIME_VERSION=1.3.18
MOGO_REALTIME_VERSION=1.3.30
# 探路,道路事件发布,获取
MOGO_TANLU_VERSION=1.3.18
MOGO_TANLU_VERSION=1.3.30
# 直播推流
MOGO_LIVE_VERSION=1.3.18
MOGO_LIVE_VERSION=1.3.30
# 直播拉流
MOGO_TRAFFICLIVE_VERSION=1.3.18
MOGO_TRAFFICLIVE_VERSION=1.3.30
# 定位服务
MOGO_LOCATION_VERSION=1.3.18
MOGO_LOCATION_VERSION=1.3.30
# 远程通讯模块
MOGO_TELEMATIC_VERSION=1.3.30
######## MogoAiCloudSDK Version ########
# 自研地图
MAP_SDK_VERSION=2.0.5.1
@@ -92,105 +94,107 @@ versionCode=80008
versionName=2.5.2
################# 新架构模块Maven版本管理 #################
MOGO_CORE_FUNCTION_AUTOPILOT_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_CHECK_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_HMI_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_MAIN_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_MAP_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_MONITORING_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_NOTICE_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_OBU_MOGO_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_SMP_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_V2X_VERSION=0.0.58.8
MOGO_CORE_DATA_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_API_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_CALL_VERSION=0.0.58.8
MOGO_CORE_RES_VERSION=0.0.58.8
MOGO_CORE_UTILS_VERSION=0.0.58.8
MOGO_CORE_NETWORK_VERSION=0.0.58.8
MOGO_CORE_FUNCTION_AUTOPILOT_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_CHECK_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_HMI_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_MAIN_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_MAP_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_MONITORING_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_NOTICE_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_OBU_MOGO_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_SMP_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_V2X_VERSION=0.0.58.10
MOGO_CORE_DATA_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_API_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_CALL_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_CARCORDER_VERSION=0.0.58.10
MOGO_CORE_FUNCTION_DEVATOOLS_VERSION=0.0.58.10
MOGO_CORE_RES_VERSION=0.0.58.10
MOGO_CORE_UTILS_VERSION=0.0.58.10
MOGO_CORE_NETWORK_VERSION=0.0.58.10
################# 旧版本架构模块版本 #################
## 工程内模块
MOGO_COMMONS_VERSION=2.1.16.8
MOGO_UTILS_VERSION=2.1.16.8
MAP_AMAP_VERSION=2.1.16.8
MAP_AUTONAVI_VERSION=2.1.16.8
MOGO_MAP_VERSION=2.1.16.8
MOGO_MAP_API_VERSION=2.1.16.8
MOGO_SERVICE_VERSION=2.1.16.8
MOGO_SERVICE_API_VERSION=2.1.16.8
MOGO_CONNECTION_VERSION=2.1.16.8
MOGO_MODULE_APPS_VERSION=2.1.16.8
MOGO_MODULE_NAVI_VERSION=2.1.16.8
MOGO_MODULE_SHARE_VERSION=2.1.16.8
MOGO_MODULE_COMMON_VERSION=2.1.16.8
MOGO_MODULE_MAIN_VERSION=2.1.16.8
MOGO_MODULE_MAP_VERSION=2.1.16.8
MOGO_MODULE_SERVICE_VERSION=2.1.16.8
MOGO_MODULE_EXTENSIONS_VERSION=2.1.16.8
MOGO_MODULE_SEARCH_VERSION=2.1.16.8
MOGO_MODULE_BACK_VERSION=2.1.16.8
MOGO_MODULE_V2X_VERSION=2.1.16.8
MOGO_COMMONS_VERSION=2.1.16.10
MOGO_UTILS_VERSION=2.1.16.10
MAP_AMAP_VERSION=2.1.16.10
MAP_AUTONAVI_VERSION=2.1.16.10
MOGO_MAP_VERSION=2.1.16.10
MOGO_MAP_API_VERSION=2.1.16.10
MOGO_SERVICE_VERSION=2.1.16.10
MOGO_SERVICE_API_VERSION=2.1.16.10
MOGO_CONNECTION_VERSION=2.1.16.10
MOGO_MODULE_APPS_VERSION=2.1.16.10
MOGO_MODULE_NAVI_VERSION=2.1.16.10
MOGO_MODULE_SHARE_VERSION=2.1.16.10
MOGO_MODULE_COMMON_VERSION=2.1.16.10
MOGO_MODULE_MAIN_VERSION=2.1.16.10
MOGO_MODULE_MAP_VERSION=2.1.16.10
MOGO_MODULE_SERVICE_VERSION=2.1.16.10
MOGO_MODULE_EXTENSIONS_VERSION=2.1.16.10
MOGO_MODULE_SEARCH_VERSION=2.1.16.10
MOGO_MODULE_BACK_VERSION=2.1.16.10
MOGO_MODULE_V2X_VERSION=2.1.16.10
# 探路
MOGO_MODULE_TANLU_VERSION=2.1.16.8
MOGO_MODULE_TANLU_VERSION=2.1.16.10
# 推送
MOGO_MODULE_PUSH_VERSION=2.1.16.8
MOGO_MODULE_PUSH_BASE_VERSION=2.1.16.8
MOGO_MODULE_PUSH_NOOP_VERSION=2.1.16.8
MOGO_MODULE_PUSH_VERSION=2.1.16.10
MOGO_MODULE_PUSH_BASE_VERSION=2.1.16.10
MOGO_MODULE_PUSH_NOOP_VERSION=2.1.16.10
# 探路上报和分享模块
TANLULIB_VERSION=2.1.16.8
MOGO_TANLU_API_VERSION=2.1.16.8
TANLULIB_VERSION=2.1.16.10
MOGO_TANLU_API_VERSION=2.1.16.10
#左侧面板模块
MOGO_MODULE_LEFT_PANEL_VERSION=2.1.16.8
MOGO_MODULE_LEFT_PANEL_NOOP_VERSION=2.1.16.8
MOGO_MODULE_LEFT_PANEL_VERSION=2.1.16.10
MOGO_MODULE_LEFT_PANEL_NOOP_VERSION=2.1.16.10
# 小控件
MOGO_MODULE_WIDGETS_VERSION=2.1.16.8
MOGO_MODULE_WIDGETS_VERSION=2.1.16.10
# obu
MOGO_MODULE_OBU_VERSION=2.1.16.8
MOGO_MODULE_OBU_MOGO_VERSION=2.1.16.8
MOGO_MODULE_OBU_VERSION=2.1.16.10
MOGO_MODULE_OBU_MOGO_VERSION=2.1.16.10
# monitor
MOGO_MODULE_MONITOR_VERSION=2.1.16.8
MOGO_MODULE_MONITOR_VERSION=2.1.16.10
# bugly
CRASHREPORT_VERSION=2.1.16.8
CRASHREPORT_BUGLY_VERSION=2.1.16.8
CRASHREPORT_NOOP_VERSION=2.1.16.8
CRASHREPORT_APMBYTE_VERSION=2.1.16.8
CRASHREPORT_UPGRADE_VERSION=2.1.16.8
CRASHREPORT_VERSION=2.1.16.10
CRASHREPORT_BUGLY_VERSION=2.1.16.10
CRASHREPORT_NOOP_VERSION=2.1.16.10
CRASHREPORT_APMBYTE_VERSION=2.1.16.10
CRASHREPORT_UPGRADE_VERSION=2.1.16.10
## tts
TTS_BASE_VERSION=2.1.16.8
TTS_DI_VERSION=2.1.16.8
TTS_ZHI_VERSION=2.1.16.8
TTS_PAD_VERSION=2.1.16.8
TTS_NOOP_VERSION=2.1.16.8
TTS_BASE_VERSION=2.1.16.10
TTS_DI_VERSION=2.1.16.10
TTS_ZHI_VERSION=2.1.16.10
TTS_PAD_VERSION=2.1.16.10
TTS_NOOP_VERSION=2.1.16.10
# 自研地图
MAP_CUSTOM_VERSION=2.1.16.8
MOGO_MODULE_ADAS_VERSION=2.1.16.8
MAP_CUSTOM_VERSION=2.1.16.10
MOGO_MODULE_ADAS_VERSION=2.1.16.10
# 基础服务实现passport、socket、location
MOGO_BASE_WEBSOCKET_SDK_VERSION=2.1.16.8
MOGO_BASE_SERVICES_APK_VERSION=2.1.16.8
MOGO_BASE_SERVICES_SDK_VERSION=2.1.16.8
MOGO_MODULE_CHAT_VERSION=2.1.16.8
MOGO_BASE_WEBSOCKET_SDK_VERSION=2.1.16.10
MOGO_BASE_SERVICES_APK_VERSION=2.1.16.10
MOGO_BASE_SERVICES_SDK_VERSION=2.1.16.10
MOGO_MODULE_CHAT_VERSION=2.1.16.10
# 车聊聊
MOGO_MODULE_CARCHATTING_VERSION=2.1.16.8
MOGO_MODULE_CARCHATTING_VERSION=2.1.16.10
# 车聊聊接口
MOGO_MODULE_CARCHATTINGPROVIDER_VERSION=2.1.16.8
MOGO_MODULE_CARCHATTINGPROVIDER_VERSION=2.1.16.10
# 皮肤
MOGO_SKIN_SUPPORT_VERSION=2.1.16.8
MOGO_SKIN_LIGHT_VERSION=2.1.16.8
MOGO_SKIN_SUPPORT_IMPL_VERSION=2.1.16.8
MOGO_SKIN_SUPPORT_NOOP_VERSION=2.1.16.8
SKIN_SUPPORT_VERSION=2.1.16.8
SKIN_SUPPORT_APPCOMPAT_VERSION=2.1.16.8
SKIN_SUPPORT_CARDVIEW_VERSION=2.1.16.8
SKIN_SUPPORT_CONSTRAINT_LAYOUT_VERSION=2.1.16.8
SKIN_SUPPORT_DESIGN_VERSION=2.1.16.8
MOGO_SKIN_SUPPORT_VERSION=2.1.16.10
MOGO_SKIN_LIGHT_VERSION=2.1.16.10
MOGO_SKIN_SUPPORT_IMPL_VERSION=2.1.16.10
MOGO_SKIN_SUPPORT_NOOP_VERSION=2.1.16.10
SKIN_SUPPORT_VERSION=2.1.16.10
SKIN_SUPPORT_APPCOMPAT_VERSION=2.1.16.10
SKIN_SUPPORT_CARDVIEW_VERSION=2.1.16.10
SKIN_SUPPORT_CONSTRAINT_LAYOUT_VERSION=2.1.16.10
SKIN_SUPPORT_DESIGN_VERSION=2.1.16.10
# OCH
MOGO_OCH_VERSION=2.1.16.8-test
MOGO_OCH_VERSION=2.1.16.10-test
MOGO_OCH_BUS_VERSION=2.0.66
MOGO_OCH_NOOP_VERSION=2.0.66
MOGO_OCH_TAXI_VERSION=2.0.66
# mogoAiCloud sdk services
MOGO_AICLOUD_SERVICES_SDK_VERSION=2.1.16.8
MOGO_AICLOUD_SERVICES_SDK_VERSION=2.1.16.10
# v2x-sdk
MOGO_V2X_SDK_VERSION=1.0.1
MOGO_V2X_SDK_VERSION=1.3.30
################# 旧版本架构模块版本 #################

View File

@@ -1,5 +1,8 @@
package com.mogo.map.impl.custom;
import static com.mogo.map.uicontroller.VisualAngleMode.MAP_STYLE_VR_ANGLE_300;
import static com.mogo.map.uicontroller.VisualAngleMode.MAP_STYLE_VR_ANGLE_CROSS;
import static com.mogo.map.uicontroller.VisualAngleMode.MAP_STYLE_VR_ANGLE_TOP;
import static com.mogo.map.uicontroller.VisualAngleMode.MODE_CLOSE_SIGHT;
import static com.mogo.map.uicontroller.VisualAngleMode.MODE_LONG_SIGHT;
import static com.mogo.map.uicontroller.VisualAngleMode.MODE_MEDIUM_SIGHT;
@@ -767,6 +770,12 @@ public class AMapViewWrapper implements IMogoMapView,
return MODE_MEDIUM_SIGHT;
case 2:
return MODE_LONG_SIGHT;
case 3:
return MAP_STYLE_VR_ANGLE_300;
case 4:
return MAP_STYLE_VR_ANGLE_TOP;
case 5:
return MAP_STYLE_VR_ANGLE_CROSS;
default:
throw new IllegalStateException("mode is unCorrect");
}

1
libraries/map-usbcamera/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,73 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-android-extensions'
id 'kotlin-kapt'
id 'com.alibaba.arouter'
}
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
// buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode Integer.valueOf(VERSION_CODE)
versionName getValueFromRootProperties("${project.name.replace("-", "_").toUpperCase()}_VERSION")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
//ARouter apt 参数
kapt {
useBuildCache = false
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
repositories {
flatDir {
dirs 'libs'
}
}
sourceSets{
main{
jniLibs.srcDir(['libs'])
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation rootProject.ext.dependencies.androidxconstraintlayout
implementation rootProject.ext.dependencies.arouter
kapt rootProject.ext.dependencies.aroutercompiler
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
} else {
}
}
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()

View File

@@ -0,0 +1,3 @@
GROUP=com.mogo.camera
POM_ARTIFACT_ID=map-usbcamera
VERSION_CODE=1

Binary file not shown.

View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.usbcamera">
<!-- 声明使用usb -->
<uses-feature
android:name="android.hardware.usb.host"
android:required="true" />
<application>
</application>
</manifest>

Binary file not shown.

View File

@@ -0,0 +1,39 @@
package com.mogo.usbcamera;
import com.serenegiant.usb.USBMonitor;
/**
* @author donghongyu
* USB摄像头方案获取权限、获取YUV数据
*/
public class USBCameraHelper {
private static USBCameraHelper mCameraHelper;
/**
* USB Manager
*/
private USBMonitor mUSBMonitor;
private USBCameraHelper() {
}
public static USBCameraHelper getInstance() {
if (mCameraHelper == null) {
mCameraHelper = new USBCameraHelper();
}
return mCameraHelper;
}
public void registerUSB() {
if (mUSBMonitor != null) {
mUSBMonitor.register();
}
}
public void unregisterUSB() {
if (mUSBMonitor != null) {
mUSBMonitor.unregister();
}
}
}

View File

@@ -0,0 +1,373 @@
package com.mogo.usbcamera;
import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.hardware.usb.UsbDevice;
import android.os.Environment;
import com.serenegiant.usb.DeviceFilter;
import com.serenegiant.usb.Size;
import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usb.common.AbstractUVCCameraHandler;
import com.serenegiant.usb.common.UVCCameraHandler;
import com.serenegiant.usb.encoder.RecordParams;
import com.serenegiant.usb.widget.CameraViewInterface;
import org.easydarwin.sw.TxtOverlay;
import java.io.File;
import java.util.List;
import java.util.Objects;
/**
* UVCCamera Helper class
*/
public class UVCCameraHelper {
public static final String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator;
public static final String SUFFIX_JPEG = ".jpg";
public static final String SUFFIX_MP4 = ".mp4";
private static final String TAG = "UVCCameraHelper";
private int previewWidth = 640;
private int previewHeight = 480;
public static final int FRAME_FORMAT_YUYV = UVCCamera.FRAME_FORMAT_YUYV;
// Default using MJPEG
// if your device is connected,but have no images
// please try to change it to FRAME_FORMAT_YUYV
public static final int FRAME_FORMAT_MJPEG = UVCCamera.FRAME_FORMAT_MJPEG;
public static final int MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS;
public static final int MODE_CONTRAST = UVCCamera.PU_CONTRAST;
private int mFrameFormat = FRAME_FORMAT_MJPEG;
private static UVCCameraHelper mCameraHelper;
// USB Manager
private USBMonitor mUSBMonitor;
// Camera Handler
private UVCCameraHandler mCameraHandler;
private USBMonitor.UsbControlBlock mCtrlBlock;
private Activity mActivity;
private CameraViewInterface mCamView;
private UVCCameraHelper() {
}
public static UVCCameraHelper getInstance() {
if (mCameraHelper == null) {
mCameraHelper = new UVCCameraHelper();
}
return mCameraHelper;
}
public void closeCamera() {
if (mCameraHandler != null) {
mCameraHandler.close();
}
}
public interface OnMyDevConnectListener {
void onAttachDev(UsbDevice device);
void onDettachDev(UsbDevice device);
void onConnectDev(UsbDevice device, boolean isConnected);
void onDisConnectDev(UsbDevice device);
void onCancelDev(UsbDevice device);
}
public void initUSBMonitor(Activity activity, CameraViewInterface cameraView, final OnMyDevConnectListener listener) {
this.mActivity = activity;
this.mCamView = cameraView;
mUSBMonitor = new USBMonitor(activity.getApplicationContext(), new USBMonitor.OnDeviceConnectListener() {
// called by checking usb device
// do request device permission
@Override
public void onAttach(UsbDevice device) {
if (listener != null) {
listener.onAttachDev(device);
}
}
// called by taking out usb device
// do close camera
@Override
public void onDettach(UsbDevice device) {
if (listener != null) {
listener.onDettachDev(device);
}
}
// called by connect to usb camera
// do open camera,start previewing
@Override
public void onConnect(final UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) {
mCtrlBlock = ctrlBlock;
openCamera(ctrlBlock);
new Thread(() -> {
// wait for camera created
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// start previewing
startPreview(mCamView);
}).start();
if (listener != null) {
listener.onConnectDev(device, true);
}
}
// called by disconnect to usb camera
// do nothing
@Override
public void onDisconnect(UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock) {
if (listener != null) {
listener.onDisConnectDev(device);
}
}
@Override
public void onCancel(UsbDevice device) {
if (listener != null) {
listener.onCancelDev(device);
}
}
});
createUVCCamera();
}
public void createUVCCamera() {
if (mCamView == null) {
throw new NullPointerException("CameraViewInterface cannot be null!");
}
// release resources for initializing camera handler
if (mCameraHandler != null) {
mCameraHandler.release();
mCameraHandler = null;
}
// initialize camera handler
mCamView.setAspectRatio(previewWidth / (float) previewHeight);
mCameraHandler = UVCCameraHandler.createHandler(mActivity, mCamView, 2,
previewWidth, previewHeight, mFrameFormat);
}
public void updateResolution(int width, int height) {
if (previewWidth == width && previewHeight == height) {
return;
}
this.previewWidth = width;
this.previewHeight = height;
if (mCameraHandler != null) {
mCameraHandler.release();
mCameraHandler = null;
}
mCamView.setAspectRatio(previewWidth / (float) previewHeight);
mCameraHandler = UVCCameraHandler.createHandler(mActivity, mCamView, 2,
previewWidth, previewHeight, mFrameFormat);
openCamera(mCtrlBlock);
new Thread(new Runnable() {
@Override
public void run() {
// wait for camera created
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// start previewing
startPreview(mCamView);
}
}).start();
}
public void registerUSB() {
if (mUSBMonitor != null) {
mUSBMonitor.register();
}
}
public void unregisterUSB() {
if (mUSBMonitor != null) {
mUSBMonitor.unregister();
}
}
public boolean checkSupportFlag(final int flag) {
return mCameraHandler != null && mCameraHandler.checkSupportFlag(flag);
}
public int getModelValue(final int flag) {
return mCameraHandler != null ? mCameraHandler.getValue(flag) : 0;
}
public int setModelValue(final int flag, final int value) {
return mCameraHandler != null ? mCameraHandler.setValue(flag, value) : 0;
}
public int resetModelValue(final int flag) {
return mCameraHandler != null ? mCameraHandler.resetValue(flag) : 0;
}
public void requestPermission(int index) {
List<UsbDevice> devList = getUsbDeviceList();
if (devList == null || devList.size() == 0) {
return;
}
int count = devList.size();
if (index >= count) {
new IllegalArgumentException("index illegal,should be < devList.size()");
}
if (mUSBMonitor != null) {
mUSBMonitor.requestPermission(getUsbDeviceList().get(index));
}
}
public int getUsbDeviceCount() {
List<UsbDevice> devList = getUsbDeviceList();
if (devList == null || devList.size() == 0) {
return 0;
}
return devList.size();
}
public List<UsbDevice> getUsbDeviceList() {
List<DeviceFilter> deviceFilters = DeviceFilter
.getDeviceFilters(mActivity.getApplicationContext(), R.xml.device_filter);
if (mUSBMonitor == null || deviceFilters == null) {
// throw new NullPointerException("mUSBMonitor ="+mUSBMonitor+"deviceFilters=;"+deviceFilters);
return null;
}
// matching all of filter devices
return mUSBMonitor.getDeviceList(deviceFilters);
}
public void capturePicture(String savePath, AbstractUVCCameraHandler.OnCaptureListener listener) {
if (mCameraHandler != null && mCameraHandler.isOpened()) {
File file = new File(savePath);
if (!Objects.requireNonNull(file.getParentFile()).exists()) {
file.getParentFile().mkdirs();
}
mCameraHandler.captureStill(savePath, listener);
}
}
public void startPusher(AbstractUVCCameraHandler.OnEncodeResultListener listener) {
if (mCameraHandler != null && !isPushing()) {
mCameraHandler.startRecording(null, listener);
}
}
public void startPusher(RecordParams params, AbstractUVCCameraHandler.OnEncodeResultListener listener) {
if (mCameraHandler != null && !isPushing()) {
if (params.isSupportOverlay()) {
TxtOverlay.install(mActivity.getApplicationContext());
}
mCameraHandler.startRecording(params, listener);
}
}
public void stopPusher() {
if (mCameraHandler != null && isPushing()) {
mCameraHandler.stopRecording();
}
}
public boolean isPushing() {
if (mCameraHandler != null) {
return mCameraHandler.isRecording();
}
return false;
}
public boolean isCameraOpened() {
if (mCameraHandler != null) {
return mCameraHandler.isOpened();
}
return false;
}
public void release() {
if (mCameraHandler != null) {
mCameraHandler.release();
mCameraHandler = null;
}
if (mUSBMonitor != null) {
mUSBMonitor.destroy();
mUSBMonitor = null;
}
}
public USBMonitor getUSBMonitor() {
return mUSBMonitor;
}
public void setOnPreviewFrameListener(AbstractUVCCameraHandler.OnPreViewResultListener listener) {
if (mCameraHandler != null) {
mCameraHandler.setOnPreViewResultListener(listener);
}
}
private void openCamera(USBMonitor.UsbControlBlock ctrlBlock) {
if (mCameraHandler != null) {
mCameraHandler.open(ctrlBlock);
}
}
public void startPreview(CameraViewInterface cameraView) {
SurfaceTexture st = cameraView.getSurfaceTexture();
if (mCameraHandler != null) {
mCameraHandler.startPreview(st);
}
}
public void stopPreview() {
if (mCameraHandler != null) {
mCameraHandler.stopPreview();
}
}
public void startCameraFoucs() {
if (mCameraHandler != null) {
mCameraHandler.startCameraFoucs();
}
}
public List<Size> getSupportedPreviewSizes() {
if (mCameraHandler == null) {
return null;
}
return mCameraHandler.getSupportedPreviewSizes();
}
public void setDefaultPreviewSize(int defaultWidth, int defaultHeight) {
if (mUSBMonitor != null) {
throw new IllegalStateException("setDefaultPreviewSize should be call before initMonitor");
}
this.previewWidth = defaultWidth;
this.previewHeight = defaultHeight;
}
public void setDefaultFrameFormat(int format) {
if (mUSBMonitor != null) {
throw new IllegalStateException("setDefaultFrameFormat should be call before initMonitor");
}
this.mFrameFormat = format;
}
public int getPreviewWidth() {
return previewWidth;
}
public int getPreviewHeight() {
return previewHeight;
}
}

View File

@@ -0,0 +1,61 @@
package com.mogo.usbcamera.utils;
import android.os.Environment;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
*
* Created by jiangdongguo on 2017/10/18.
*/
public class FileUtils {
private static BufferedOutputStream outputStream;
public static String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator;
public static void createfile(String path){
File file = new File(path);
if(file.exists()){
file.delete();
}
try {
outputStream = new BufferedOutputStream(new FileOutputStream(file));
} catch (Exception e){
e.printStackTrace();
}
}
public static void releaseFile(){
try {
if(outputStream != null) {
outputStream.flush();
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void putFileStream(byte[] data,int offset,int length){
if(outputStream != null) {
try {
outputStream.write(data,offset,length);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void putFileStream(byte[] data){
if(outputStream != null) {
try {
outputStream.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,23 @@
package com.serenegiant.dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
public abstract class DialogFragmentEx extends DialogFragment {
protected static final String ARGS_KEY_REQUEST_CODE = "requestCode";
protected static final String ARGS_KEY_ID_TITLE = "title";
protected static final String ARGS_KEY_ID_MESSAGE = "message";
protected static final String ARGS_KEY_TAG = "tag";
@Override
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
final Bundle args = getArguments();
if (args != null) {
outState.putAll(args);
}
}
}

View File

@@ -0,0 +1,136 @@
package com.serenegiant.dialog;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import com.serenegiant.utils.BuildCheck;
/**
* パーミッション要求前に説明用のダイアログを表示するためのDialogFragment
*/
@SuppressWarnings("deprecation")
@Deprecated
public class MessageDialogFragment extends DialogFragment {
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
private static final String TAG = MessageDialogFragment.class.getSimpleName();
public static interface MessageDialogListener {
public void onMessageDialogResult(final MessageDialogFragment dialog, final int requestCode, final String[] permissions, final boolean result);
}
public static MessageDialogFragment showDialog(final Activity parent, final int requestCode, final int id_title, final int id_message, final String[] permissions) {
final MessageDialogFragment dialog = newInstance(requestCode, id_title, id_message, permissions);
dialog.show(parent.getFragmentManager(), TAG);
return dialog;
}
public static MessageDialogFragment showDialog(final Fragment parent, final int requestCode, final int id_title, final int id_message, final String[] permissions) {
final MessageDialogFragment dialog = newInstance(requestCode, id_title, id_message, permissions);
dialog.setTargetFragment(parent, parent.getId());
dialog.show(parent.getFragmentManager(), TAG);
return dialog;
}
public static MessageDialogFragment newInstance(final int requestCode, final int id_title, final int id_message, final String[] permissions) {
final MessageDialogFragment fragment = new MessageDialogFragment();
final Bundle args = new Bundle();
// ここでパラメータをセットする
args.putInt("requestCode", requestCode);
args.putInt("title", id_title);
args.putInt("message", id_message);
args.putStringArray("permissions", permissions != null ? permissions : new String[]{});
fragment.setArguments(args);
return fragment;
}
private MessageDialogListener mDialogListener;
public MessageDialogFragment() {
super();
// デフォルトコンストラクタが必要
}
@SuppressLint("NewApi")
@Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
// コールバックインターフェースを取得
if (activity instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)activity;
}
if (mDialogListener == null) {
final Fragment fragment = getTargetFragment();
if (fragment instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)fragment;
}
}
if (mDialogListener == null) {
if (BuildCheck.isAndroid4_2()) {
final Fragment target = getParentFragment();
if (target instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)target;
}
}
}
if (mDialogListener == null) {
// Log.w(TAG, "caller activity/fragment must implement PermissionDetailDialogFragmentListener");
throw new ClassCastException(activity.toString());
}
}
// @Override
// public void onCreate(final Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// final Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
// }
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
final int requestCode = getArguments().getInt("requestCode");
final int id_title = getArguments().getInt("title");
final int id_message = getArguments().getInt("message");
final String[] permissions = args.getStringArray("permissions");
return new AlertDialog.Builder(getActivity())
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(id_title)
.setMessage(id_message)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
// 本当はここでパーミッション要求をしたいだけどこのダイアログがdismissしてしまって結果を受け取れないので
// 呼び出し側へ返してそこでパーミッション要求する。なのでこのダイアログは単にメッセージを表示するだけ
try {
mDialogListener.onMessageDialogResult(MessageDialogFragment.this, requestCode, permissions, true);
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}
)
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, int whichButton) {
try {
mDialogListener.onMessageDialogResult(MessageDialogFragment.this, requestCode, permissions, false);
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}
)
.create();
}
}

View File

@@ -0,0 +1,213 @@
package com.serenegiant.dialog;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.serenegiant.utils.BuildCheck;
public class MessageDialogFragmentV4 extends DialogFragmentEx {
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
private static final String TAG = MessageDialogFragmentV4.class.getSimpleName();
private static final String ARGS_KEY_PERMISSIONS = "permissions";
/**
* ダイアログの表示結果を受け取るためのコールバックリスナー
*/
public static interface MessageDialogListener {
public void onMessageDialogResult(
@NonNull final MessageDialogFragmentV4 dialog, final int requestCode,
@NonNull final String[] permissions, final boolean result);
}
/**
* ダイアログ表示のためのヘルパーメソッド
* @param parent
* @param requestCode
* @param id_title
* @param id_message
* @param permissions
* @return
* @throws IllegalStateException
*/
public static MessageDialogFragmentV4 showDialog(
@NonNull final FragmentActivity parent, final int requestCode,
@StringRes final int id_title, @StringRes final int id_message,
@NonNull final String[] permissions) throws IllegalStateException {
final MessageDialogFragmentV4 dialog
= newInstance(requestCode, id_title, id_message, permissions);
dialog.show(parent.getSupportFragmentManager(), TAG);
return dialog;
}
/**
* ダイアログ表示のためのヘルパーメソッド
* @param parent
* @param requestCode
* @param id_title
* @param id_message
* @param permissions
* @return
* @throws IllegalStateException
*/
public static MessageDialogFragmentV4 showDialog(
@NonNull final Fragment parent, final int requestCode,
@StringRes final int id_title, @StringRes final int id_message,
@NonNull final String[] permissions) throws IllegalStateException {
final MessageDialogFragmentV4 dialog
= newInstance(requestCode, id_title, id_message, permissions);
dialog.setTargetFragment(parent, parent.getId());
dialog.show(parent.requireFragmentManager(), TAG);
return dialog;
}
/**
* ダイアログ生成のためのヘルパーメソッド
* ダイアログ自体を直接生成せずにこのメソッドを呼び出すこと
* @param requestCode
* @param id_title
* @param id_message
* @param permissions
* @return
*/
public static MessageDialogFragmentV4 newInstance(
final int requestCode,
@StringRes final int id_title, @StringRes final int id_message,
@NonNull final String[] permissions) {
final MessageDialogFragmentV4 fragment = new MessageDialogFragmentV4();
final Bundle args = new Bundle();
// ここでパラメータをセットする
args.putInt(ARGS_KEY_REQUEST_CODE, requestCode);
args.putInt(ARGS_KEY_ID_TITLE, id_title);
args.putInt(ARGS_KEY_ID_MESSAGE, id_message);
args.putStringArray(ARGS_KEY_PERMISSIONS, permissions);
fragment.setArguments(args);
return fragment;
}
private MessageDialogListener mDialogListener;
/**
* コンストラクタ, 直接生成せずに#newInstanceを使うこと
*/
public MessageDialogFragmentV4() {
super();
// デフォルトコンストラクタが必要
}
@Override
public void onAttach(final Context context) {
super.onAttach(context);
// コールバックインターフェースを取得
if (context instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)context;
}
if (mDialogListener == null) {
final Fragment fragment = getTargetFragment();
if (fragment instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)fragment;
}
}
if (mDialogListener == null) {
if (BuildCheck.isAndroid4_2()) {
final Fragment target = getParentFragment();
if (target instanceof MessageDialogListener) {
mDialogListener = (MessageDialogListener)target;
}
}
}
if (mDialogListener == null) {
// Log.w(TAG, "caller activity/fragment must implement PermissionDetailDialogFragmentListener");
throw new ClassCastException(context.toString());
}
}
// @Override
// public void onCreate(final Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// final Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
// }
@NonNull
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Bundle args = savedInstanceState != null ? savedInstanceState : requireArguments();
final int id_title = args.getInt(ARGS_KEY_ID_TITLE);
final int id_message = args.getInt(ARGS_KEY_ID_MESSAGE);
final Activity activity = requireActivity();
return new AlertDialog.Builder(activity)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(id_title)
.setMessage(id_message)
.setPositiveButton(android.R.string.ok, mOnClickListener)
.setNegativeButton(android.R.string.cancel, mOnClickListener)
.create();
}
private final DialogInterface.OnClickListener mOnClickListener
= new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
// 本当はここでパーミッション要求をしたいだけどこのダイアログがdismissしてしまって結果を受け取れないので
// 呼び出し側へ返してそこでパーミッション要求する。なのでこのダイアログは単にメッセージを表示するだけ
callOnMessageDialogResult(which == DialogInterface.BUTTON_POSITIVE);
}
};
@Override
public void onCancel(final DialogInterface dialog) {
super.onCancel(dialog);
callOnMessageDialogResult(false);
}
/**
* コールバックリスナー呼び出しのためのヘルパーメソッド
* @param result
*/
private void callOnMessageDialogResult(final boolean result)
throws IllegalStateException {
final Bundle args = requireArguments();
final int requestCode = args.getInt(ARGS_KEY_REQUEST_CODE);
final String[] permissions = args.getStringArray(ARGS_KEY_PERMISSIONS);
try {
mDialogListener.onMessageDialogResult(
MessageDialogFragmentV4.this,
requestCode, permissions, result);
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}

View File

@@ -0,0 +1,248 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.graphics.SurfaceTexture;
import android.util.Log;
import android.view.Surface;
import androidx.annotation.NonNull;
/**
* OpenGL|ESでのSurfaceへの描画処理をDelegaterを介して行うためのIRenderer
*/
public class DumbRenderer implements IRenderer {
// private static final boolean DEBUG = BuildConfig.DEBUG && false;
private static final String TAG = DumbRenderer.class.getSimpleName();
public interface RendererDelegater {
public void onStart(final EGLBase eglBase);
public void onStop(final EGLBase eglBase);
public void onSetSurface(final EGLBase eglBase, final Object surface);
public void onResize(final EGLBase eglBase, final int width, final int height);
/**
* 描画実行
* @param eglBase
* @param args #requestRenderの引数
*/
public void onDraw(final EGLBase eglBase, final Object... args);
public void onMirror(final EGLBase eglBase, final int mirror);
}
/** レンダリングスレッドの排他制御用オブジェクト */
private final Object mSync = new Object();
private RendererTask mRendererTask;
@MirrorMode
private int mMirror = MIRROR_NORMAL;
public DumbRenderer(final EGLBase.IContext sharedContext,
final int flags, final RendererDelegater delegater) {
this(3, sharedContext, flags, delegater);
}
public DumbRenderer(final int maxClientVersion,
final EGLBase.IContext sharedContext,
final int flags, final RendererDelegater delegater) {
mRendererTask = new RendererTask(maxClientVersion, sharedContext, flags, delegater);
new Thread(mRendererTask, TAG).start();
if (!mRendererTask.waitReady()) {
// 初期化に失敗した時
throw new RuntimeException("failed to start renderer thread");
}
}
@Override
public void release() {
synchronized (mSync) {
if (mRendererTask != null) {
// 描画タスクを開放
mRendererTask.release();
mRendererTask = null;
}
}
}
@Override
public void setSurface(final Surface surface) {
synchronized (mSync) {
if (mRendererTask != null) {
mRendererTask.offer(REQUEST_SET_SURFACE, surface);
}
}
}
@Override
public void setSurface(final SurfaceTexture surface) {
synchronized (mSync) {
if (mRendererTask != null) {
mRendererTask.offer(REQUEST_SET_SURFACE, surface);
}
}
}
@Override
public void setMirror(@MirrorMode final int mirror) {
synchronized (mSync) {
if (mMirror != mirror) {
mMirror = mirror;
if (mRendererTask != null) {
mRendererTask.offer(REQUEST_MIRROR, mirror % 4);
}
}
}
}
@Override
@MirrorMode
public int getMirror() {
return mMirror;
}
@Override
public void resize(final int width, final int height) {
synchronized (mSync) {
if (mRendererTask != null) {
mRendererTask.offer(REQUEST_RESIZE, width, height);
}
}
}
@Override
public void requestRender(final Object... args) {
synchronized (mSync) {
if (mRendererTask != null) {
mRendererTask.offer(REQUEST_DRAW, args);
}
}
}
private static final int REQUEST_SET_SURFACE = 1;
private static final int REQUEST_DRAW = 2;
private static final int REQUEST_RESIZE = 3;
private static final int REQUEST_MIRROR = 4;
private static class RendererTask extends EglTask {
private final RendererDelegater mDelegater;
/** 最後にレンダリングしたフレームサイズ, 0ならまで一度も描画されていない */
private int frameWidth, frameHeight, frameRotation;
/** 描画先Surfaceのサイズ */
private int surfaceWidth, surfaceHeight;
/** 映像を左右反転させるかどうか */
private boolean mirror;
public RendererTask(final EGLBase.IContext sharedContext,
final int flags, @NonNull final RendererDelegater delegater) {
this(3, sharedContext, flags, delegater);
}
public RendererTask(final int maxClientVersion,
final EGLBase.IContext sharedContext,
final int flags, @NonNull final RendererDelegater delegater) {
super(maxClientVersion, sharedContext, flags);
mDelegater = delegater;
}
@Override
protected void onStart() {
makeCurrent();
try {
mDelegater.onStart(getEgl());
} catch (final Exception e) {
Log.w(TAG, e);
}
}
@Override
protected void onStop() {
makeCurrent();
try {
mDelegater.onStop(getEgl());
} catch (final Exception e) {
Log.w(TAG, e);
}
}
@Override
protected Object processRequest(final int request,
final int arg1, final int arg2, final Object obj) throws TaskBreak {
switch (request) {
case REQUEST_SET_SURFACE:
handleSetSurface(obj);
break;
case REQUEST_DRAW:
handleDraw(obj);
break;
case REQUEST_RESIZE:
handleResize(arg1, arg2);
break;
case REQUEST_MIRROR:
handleMirror(arg1);
break;
}
return null;
}
private void handleSetSurface(final Object surface) {
makeCurrent();
try {
mDelegater.onSetSurface(getEgl(), surface);
} catch (final Exception e) {
Log.w(TAG, e);
}
}
private void handleResize(final int width, final int height) {
if ((surfaceWidth != width) || (surfaceHeight != height)) {
surfaceWidth = width;
surfaceHeight = height;
makeCurrent();
try {
mDelegater.onResize(getEgl(), width, height);
} catch (final Exception e) {
Log.w(TAG, e);
}
handleDraw();
}
}
private void handleDraw(final Object... args) {
makeCurrent();
try {
mDelegater.onDraw(getEgl(), args);
} catch (final Exception e) {
Log.w(TAG, e);
}
}
private void handleMirror(final int mirror) {
makeCurrent();
try {
mDelegater.onMirror(getEgl(), mirror);
} catch (final Exception e) {
Log.w(TAG, e);
}
}
}
}

View File

@@ -0,0 +1,183 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.os.Build;
/**
* EGLレンダリングコンテキストを生成使用するためのヘルパークラス
*/
public abstract class EGLBase {
public static final Object EGL_LOCK = new Object();
public static final int EGL_RECORDABLE_ANDROID = 0x3142;
public static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public static final int EGL_OPENGL_ES2_BIT = 4;
public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040;
// public static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
/**
* EGL生成のヘルパーメソッド, 環境に応じてEGLBase10またはEGLBase14を生成する
* maxClientVersion=3, ステンシルバッファなし
* @param sharedContext
* @param withDepthBuffer
* @param isRecordable
* @return
*/
public static EGLBase createFrom(final IContext sharedContext,
final boolean withDepthBuffer, final boolean isRecordable) {
return createFrom(3, sharedContext, withDepthBuffer, 0, isRecordable);
}
/**
* EGL生成のヘルパーメソッド, 環境に応じてEGLBase10またはEGLBase14を生成する
* maxClientVersion=3
* @param sharedContext
* @param withDepthBuffer
* @param stencilBits
* @param isRecordable
* @return
*/
public static EGLBase createFrom(final IContext sharedContext,
final boolean withDepthBuffer, final int stencilBits, final boolean isRecordable) {
return createFrom(3, sharedContext,
withDepthBuffer, stencilBits, isRecordable);
}
/**
* EGL生成のヘルパーメソッド, 環境に応じてEGLBase10またはEGLBase14を生成する
* @param maxClientVersion
* @param sharedContext
* @param withDepthBuffer trueなら16ビットのデプスバッファ有り, falseならデプスバッファなし
* @param stencilBits 0以下ならステンシルバッファなし
* @param isRecordable
* @return
*/
public static EGLBase createFrom(final int maxClientVersion,
final IContext sharedContext, final boolean withDepthBuffer,
final int stencilBits, final boolean isRecordable) {
if (isEGL14Supported() && ((sharedContext == null)
|| (sharedContext instanceof EGLBase14.Context))) {
return new EGLBase14(maxClientVersion,
(EGLBase14.Context)sharedContext,
withDepthBuffer, stencilBits, isRecordable);
} else {
return new EGLBase10(maxClientVersion,
(EGLBase10.Context)sharedContext,
withDepthBuffer, stencilBits, isRecordable);
}
}
/**
* EGLレンダリングコンテキストのホルダークラス
*/
public static abstract class IContext {
public abstract long getNativeHandle();
public abstract Object getEGLContext();
}
/**
* EGLコンフィグのホルダークラス
*/
public static abstract class IConfig {
}
/**
* EGLレンダリングコンテキストに紐付ける描画オブジェクト
*/
public interface IEglSurface {
public void makeCurrent();
public void swap();
public IContext getContext();
/**
* swap with presentation time[ns]
* only works well now when using EGLBase14
* @param presentationTimeNs
*/
public void swap(final long presentationTimeNs);
public void release();
public boolean isValid();
}
public static boolean isEGL14Supported() {
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2);
}
/**
* 関連するリソースを破棄する
*/
public abstract void release();
/**
* GLESに文字列を問い合わせる
* @param what
* @return
*/
public abstract String queryString(final int what);
/**
* GLESバージョンを取得する
* @return 1, 2または3
*/
public abstract int getGlVersion();
/**
* EGLレンダリングコンテキストを取得する
* このEGLBaseインスタンスを使って生成したEglSurfaceをmakeCurrentした状態で
* eglGetCurrentContextを呼び出すのと一緒
* @return
*/
public abstract IContext getContext();
/**
* EGLコンフィグを取得する
* @return
*/
public abstract IConfig getConfig();
/**
* 指定したSurfaceからEglSurfaceを生成する
* 生成したEglSurfaceをmakeCurrentした状態で戻る
* @param nativeWindow Surface/SurfaceTexture/SurfaceHolder
* @return
*/
public abstract IEglSurface createFromSurface(final Object nativeWindow);
/**
* 指定した大きさのオフスクリーンEglSurfaceを生成する
* 生成したEglSurfaceをmakeCurrentした状態で戻る
* @param width PBufferオフスクリーンのサイズ(0以下はだめ)
* @param height
* @return
*/
public abstract IEglSurface createOffscreen(final int width, final int height);
/**
* EGLレンダリングコンテキストとスレッドの紐付けを解除する
*/
public abstract void makeDefault();
/**
* eglWaitGLとeglWaitNativeを呼ぶ
*
* eglWaitGL: コマンドキュー内のコマンドをすべて転送する, GLES20.glFinish()と同様の効果
* eglWaitNative: GPU側の描画処理が終了するまで実行をブロックする
*/
public abstract void sync();
}

View File

@@ -0,0 +1,724 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.opengl.GLES10;
import android.opengl.GLES20;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.serenegiant.utils.BuildCheck;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
/**
* EGLレンダリングコンテキストを生成使用するためのヘルパークラス
*/
/*package*/ class EGLBase10 extends EGLBase {
// private static final boolean DEBUG = false; // FIXME set false on release
private static final String TAG = "EGLBase10";
private EGL10 mEgl = null;
private EGLDisplay mEglDisplay = null;
private Config mEglConfig = null;
private int mGlVersion = 2;
private static final Context EGL_NO_CONTEXT = new Context(EGL10.EGL_NO_CONTEXT);
@NonNull private Context mContext = EGL_NO_CONTEXT;
/**
* EGLレンダリングコンテキストのホルダークラス
*/
public static class Context extends IContext {
public final EGLContext eglContext;
private Context(final EGLContext context) {
eglContext = context;
}
@Override
public long getNativeHandle() {
return 0L;
}
@Override
public Object getEGLContext() {
return eglContext;
}
}
public static class Config extends IConfig {
public final EGLConfig eglConfig;
private Config(final EGLConfig eglConfig) {
this.eglConfig = eglConfig;
}
}
/**
* Android4.1.2だとSurfaceを使えない。
* SurfaceTexture/SurfaceHolderの場合は内部でSurfaceを生成して使っているにもかかわらず。
* SurfaceHolderはインターフェースなのでSurfaceHolderを継承したダミークラスを生成して食わす
*/
public static class MySurfaceHolder implements SurfaceHolder {
private final Surface surface;
public MySurfaceHolder(final Surface surface) {
this.surface = surface;
}
@Override
public Surface getSurface() {
return surface;
}
// ここより下はどないでもええ
@Override
public void addCallback(final Callback callback) {
}
@Override
public void removeCallback(final Callback callback) {
}
@Override
public boolean isCreating() {
return false;
}
@Override
public void setType(final int type) {
}
@Override
public void setFixedSize(final int width, final int height) {
}
@Override
public void setSizeFromLayout() {
}
@Override
public void setFormat(final int format) {
}
@Override
public void setKeepScreenOn(final boolean screenOn) {
}
@Override
public Canvas lockCanvas() {
return null;
}
@Override
public Canvas lockCanvas(final Rect dirty) {
return null;
}
@Override
public void unlockCanvasAndPost(final Canvas canvas) {
}
@Override
public Rect getSurfaceFrame() {
return null;
}
}
/**
* EGLレンダリングコンテキストに紐付ける描画オブジェクト
*/
public static class EglSurface implements IEglSurface {
private final EGLBase10 mEglBase;
private EGLSurface mEglSurface = EGL10.EGL_NO_SURFACE;
/**
* Surface(Surface/SurfaceTexture/SurfaceHolder)に関係付けられたEglSurface
* @param eglBase
* @param surface
*/
private EglSurface(final EGLBase10 eglBase, final Object surface)
throws IllegalArgumentException {
// if (DEBUG) Log.v(TAG, "EglSurface:");
mEglBase = eglBase;
if ((surface instanceof Surface) && !BuildCheck.isAndroid4_2()) {
// Android4.1.2だとSurfaceを使えない。
// SurfaceTexture/SurfaceHolderの場合は内部で
// Surfaceを生成して使っているにもかかわらず。
// SurfaceHolderはインターフェースなのでSurfaceHolderを
// 継承したダミークラスを生成して食わす
mEglSurface = mEglBase.createWindowSurface(
new MySurfaceHolder((Surface) surface));
} else if ((surface instanceof Surface)
|| (surface instanceof SurfaceHolder)
|| (surface instanceof SurfaceTexture)
|| (surface instanceof SurfaceView)) {
mEglSurface = mEglBase.createWindowSurface(surface);
} else {
throw new IllegalArgumentException("unsupported surface");
}
}
/**
* 指定した大きさを持つオフスクリーンEglSurface(PBuffer)
* @param eglBase
* @param width
* @param height
*/
private EglSurface(final EGLBase10 eglBase, final int width, final int height) {
// if (DEBUG) Log.v(TAG, "EglSurface:");
mEglBase = eglBase;
if ((width <= 0) || (height <= 0)) {
mEglSurface = mEglBase.createOffscreenSurface(1, 1);
} else {
mEglSurface = mEglBase.createOffscreenSurface(width, height);
}
}
/**
* 指定したEGLSurfaceをカレントの描画Surfaceに設定する
* Surface全面に描画できるようにViewportも変更するので必要であればswapの後に変更すること
*/
@Override
public void makeCurrent() {
mEglBase.makeCurrent(mEglSurface);
if (mEglBase.getGlVersion() >= 2) {
GLES20.glViewport(0, 0,
mEglBase.getSurfaceWidth(mEglSurface), mEglBase.getSurfaceHeight(mEglSurface));
} else {
GLES10.glViewport(0, 0,
mEglBase.getSurfaceWidth(mEglSurface), mEglBase.getSurfaceHeight(mEglSurface));
}
}
/**
* 描画を終了してダブルバッファを切り替える
*/
@Override
public void swap() {
mEglBase.swap(mEglSurface);
}
@Override
public void swap(final long presentationTimeNs) {
mEglBase.swap(mEglSurface, presentationTimeNs);
}
@Override
public IContext getContext() {
return mEglBase.getContext();
}
public void setPresentationTime(final long presentationTimeNs) {
// EGLExt.eglPresentationTimeANDROID(mEglBase.mEglDisplay,
// mEglSurface, presentationTimeNs);
}
/**
* EGLSurfaceが有効かどうかを取得
* @return
*/
@Override
public boolean isValid() {
return (mEglSurface != null)
&& (mEglSurface != EGL10.EGL_NO_SURFACE)
&& (mEglBase.getSurfaceWidth(mEglSurface) > 0)
&& (mEglBase.getSurfaceHeight(mEglSurface) > 0);
}
/**
* 破棄処理
*/
@Override
public void release() {
// if (DEBUG) Log.v(TAG, "EglSurface:release:");
mEglBase.makeDefault();
mEglBase.destroyWindowSurface(mEglSurface);
mEglSurface = EGL10.EGL_NO_SURFACE;
}
}
/**
* コンストラクタ
* @param maxClientVersion
* @param sharedContext 共有コンテキストを使用する場合に指定
* @param withDepthBuffer
* @param isRecordable true MediaCodec等の録画用Surfaceを使用する場合に、
* EGL_RECORDABLE_ANDROIDフラグ付きでコンフィグする
*/
public EGLBase10(final int maxClientVersion,
final Context sharedContext, final boolean withDepthBuffer,
final int stencilBits, final boolean isRecordable) {
// if (DEBUG) Log.v(TAG, "Constructor:");
init(maxClientVersion, sharedContext, withDepthBuffer, stencilBits, isRecordable);
}
/**
* 関連するリソースを破棄する
*/
@Override
public void release() {
// if (DEBUG) Log.v(TAG, "release:");
destroyContext();
mContext = EGL_NO_CONTEXT;
if (mEgl == null) return;
mEgl.eglMakeCurrent(mEglDisplay,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
// mEgl.eglReleaseThread(); // XXX これを入れるとハングアップする機種がある
mEgl.eglTerminate(mEglDisplay);
mEglDisplay = null;
mEglConfig = null;
mEgl = null;
}
/**
* 指定したSurfaceからEglSurfaceを生成する
* 生成したEglSurfaceをmakeCurrentした状態で戻る
* @param nativeWindow Surface/SurfaceTexture/SurfaceHolder
* @return
*/
@Override
public EglSurface createFromSurface(final Object nativeWindow) {
// if (DEBUG) Log.v(TAG, "createFromSurface:");
final EglSurface eglSurface = new EglSurface(this, nativeWindow);
eglSurface.makeCurrent();
return eglSurface;
}
/**
* 指定した大きさのオフスクリーンEglSurfaceを生成する
* 生成したEglSurfaceをmakeCurrentした状態で戻る
* @param width PBufferオフスクリーンのサイズ(0以下はだめ)
* @param height
* @return
*/
@Override
public EglSurface createOffscreen(final int width, final int height) {
// if (DEBUG) Log.v(TAG, "createOffscreen:");
final EglSurface eglSurface = new EglSurface(this, width, height);
eglSurface.makeCurrent();
return eglSurface;
}
/**
* EGLレンダリングコンテキストを取得する
* このEGLBaseインスタンスを使って生成したEglSurfaceをmakeCurrentした状態で
* eglGetCurrentContextを呼び出すのと一緒
* @return
*/
@Override
public Context getContext() {
return mContext;
}
/**
* EGLコンフィグを取得する
* @return
*/
@Override
public Config getConfig() {
return mEglConfig;
}
/**
* EGLレンダリングコンテキストとスレッドの紐付けを解除する
*/
@Override
public void makeDefault() {
// if (DEBUG) Log.v(TAG, "makeDefault:");
if (!mEgl.eglMakeCurrent(mEglDisplay,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)) {
Log.w(TAG, "makeDefault:eglMakeCurrent:err=" + mEgl.eglGetError());
}
}
/**
* eglWaitGLとeglWaitNativeを呼ぶ
*
* eglWaitGL: コマンドキュー内のコマンドをすべて転送する, GLES20.glFinish()と同様の効果
* eglWaitNative: GPU側の描画処理が終了するまで実行をブロックする
*/
@Override
public void sync() {
mEgl.eglWaitGL(); // GLES20.glFinish()と同様の効果
mEgl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, null);
}
/**
* GLESに文字列を問い合わせる
* @param what
* @return
*/
@Override
public String queryString(final int what) {
return mEgl.eglQueryString(mEglDisplay, what);
}
/**
* GLESバージョンを取得する
* @return 1, 2または3
*/
@Override
public int getGlVersion() {
return mGlVersion;
}
/**
* 初期化の下請け
* @param maxClientVersion
* @param sharedContext
* @param withDepthBuffer
* @param isRecordable
*/
private final void init(final int maxClientVersion,
@Nullable Context sharedContext,
final boolean withDepthBuffer, final int stencilBits, final boolean isRecordable) {
// if (DEBUG) Log.v(TAG, "init:");
sharedContext = (sharedContext != null) ? sharedContext : EGL_NO_CONTEXT;
if (mEgl == null) {
mEgl = (EGL10)EGLContext.getEGL();
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
// EGLのバージョンを取得
final int[] version = new int[2];
if (!mEgl.eglInitialize(mEglDisplay, version)) {
mEglDisplay = null;
throw new RuntimeException("eglInitialize failed");
}
}
EGLConfig config;
if (maxClientVersion >= 3) {
// GLES3で取得できるかどうか試してみる
config = getConfig(3, withDepthBuffer, stencilBits, isRecordable);
if (config != null) {
final EGLContext context = createContext(sharedContext, config, 3);
if ((mEgl.eglGetError()) == EGL10.EGL_SUCCESS) {
// ここは例外生成したくないのでcheckEglErrorの代わりに自前でチェック
//Log.d(TAG, "Got GLES 3 config");
mEglConfig = new Config(config);
mContext = new Context(context);
mGlVersion = 3;
}
}
}
// GLES3で取得できなかった時はGLES2を試みる
if ((maxClientVersion >= 2)
&& ((mContext == null) || (mContext.eglContext == EGL10.EGL_NO_CONTEXT))) {
config = getConfig(2, withDepthBuffer, stencilBits, isRecordable);
if (config == null) {
throw new RuntimeException("chooseConfig failed");
}
try {
// create EGL rendering context
final EGLContext context = createContext(sharedContext, config, 2);
checkEglError("eglCreateContext");
mEglConfig = new Config(config);
mContext = new Context(context);
mGlVersion = 2;
} catch (final Exception e) {
if (isRecordable) {
config = getConfig(2, withDepthBuffer, stencilBits, false);
if (config == null) {
throw new RuntimeException("chooseConfig failed");
}
// create EGL rendering context
final EGLContext context = createContext(sharedContext, config, 2);
checkEglError("eglCreateContext");
mEglConfig = new Config(config);
mContext = new Context(context);
mGlVersion = 2;
}
}
}
if ((mContext == null) || (mContext.eglContext == EGL10.EGL_NO_CONTEXT)) {
config = getConfig(1, withDepthBuffer, stencilBits, isRecordable);
if (config == null) {
throw new RuntimeException("chooseConfig failed");
}
// create EGL rendering context
final EGLContext context = createContext(sharedContext, config, 1);
checkEglError("eglCreateContext");
mEglConfig = new Config(config);
mContext = new Context(context);
mGlVersion = 1;
}
// confirm whether the EGL rendering context is successfully created
final int[] values = new int[1];
mEgl.eglQueryContext(mEglDisplay,
mContext.eglContext, EGL_CONTEXT_CLIENT_VERSION, values);
Log.d(TAG, "EGLContext created, client version " + values[0]);
makeDefault();
}
/**
* change context to draw this window surface
* @return
*/
private final boolean makeCurrent(final EGLSurface surface) {
// if (DEBUG) Log.v(TAG, "makeCurrent:");
/* if (mEglDisplay == null) {
if (DEBUG) Log.d(TAG, "makeCurrent:eglDisplay not initialized");
} */
if (surface == null || surface == EGL10.EGL_NO_SURFACE) {
final int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
Log.e(TAG, "makeCurrent:EGL_BAD_NATIVE_WINDOW");
}
return false;
}
// attach EGL rendering context to specific EGL window surface
if (!mEgl.eglMakeCurrent(mEglDisplay, surface, surface, mContext.eglContext)) {
Log.w("TAG", "eglMakeCurrent" + mEgl.eglGetError());
return false;
}
return true;
}
private final int swap(final EGLSurface surface) {
// if (DEBUG) Log.v(TAG, "swap:");
if (!mEgl.eglSwapBuffers(mEglDisplay, surface)) {
final int err = mEgl.eglGetError();
// if (DEBUG) Log.w(TAG, "swap:err=" + err);
return err;
}
return EGL10.EGL_SUCCESS;
}
/**
* swap rendering buffer with presentation time[ns]
* presentationTimeNs is ignored on this method
* @param surface
* @param ignored
* @return
*/
private final int swap(final EGLSurface surface, final long ignored) {
// if (DEBUG) Log.v(TAG, "swap:");
// EGLExt.eglPresentationTimeANDROID(mEglDisplay, surface, presentationTimeNs);
if (!mEgl.eglSwapBuffers(mEglDisplay, surface)) {
final int err = mEgl.eglGetError();
// if (DEBUG) Log.w(TAG, "swap:err=" + err);
return err;
}
return EGL10.EGL_SUCCESS;
}
private final EGLContext createContext(
@NonNull final Context sharedContext,
final EGLConfig config, final int version) {
// if (DEBUG) Log.v(TAG, "createContext:");
final int[] attrib_list = {
EGL_CONTEXT_CLIENT_VERSION, version,
EGL10.EGL_NONE
};
final EGLContext context = mEgl.eglCreateContext(
mEglDisplay, config, sharedContext.eglContext, attrib_list);
// checkEglError("eglCreateContext");
return context;
}
private final void destroyContext() {
// if (DEBUG) Log.v(TAG, "destroyContext:");
if (!mEgl.eglDestroyContext(mEglDisplay, mContext.eglContext)) {
Log.e("destroyContext", "display:" + mEglDisplay
+ " context: " + mContext.eglContext);
Log.e(TAG, "eglDestroyContext:" + mEgl.eglGetError());
}
mContext = EGL_NO_CONTEXT;
}
private final int getSurfaceWidth(final EGLSurface surface) {
final int[] value = new int[1];
final boolean ret = mEgl.eglQuerySurface(mEglDisplay,
surface, EGL10.EGL_WIDTH, value);
if (!ret) value[0] = 0;
return value[0];
}
private final int getSurfaceHeight(final EGLSurface surface) {
final int[] value = new int[1];
final boolean ret = mEgl.eglQuerySurface(mEglDisplay,
surface, EGL10.EGL_HEIGHT, value);
if (!ret) value[0] = 0;
return value[0];
}
/**
* nativeWindow should be one of the SurfaceView, Surface, SurfaceHolder and SurfaceTexture
* @param nativeWindow
* @return
*/
private final EGLSurface createWindowSurface(final Object nativeWindow) {
// if (DEBUG) Log.v(TAG, "createWindowSurface:nativeWindow=" + nativeWindow);
final int[] surfaceAttribs = {
EGL10.EGL_NONE
};
EGLSurface result = null;
try {
result = mEgl.eglCreateWindowSurface(mEglDisplay,
mEglConfig.eglConfig, nativeWindow, surfaceAttribs);
if (result == null || result == EGL10.EGL_NO_SURFACE) {
final int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
Log.e(TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
}
throw new RuntimeException("createWindowSurface failed error=" + error);
}
makeCurrent(result);
// 画面サイズ・フォーマットの取得
} catch (final Exception e) {
Log.e(TAG, "eglCreateWindowSurface", e);
throw new IllegalArgumentException(e);
}
return result;
}
/**
* Creates an EGL surface associated with an offscreen buffer.
*/
private final EGLSurface createOffscreenSurface(final int width, final int height) {
// if (DEBUG) Log.v(TAG, "createOffscreenSurface:");
final int[] surfaceAttribs = {
EGL10.EGL_WIDTH, width,
EGL10.EGL_HEIGHT, height,
EGL10.EGL_NONE
};
mEgl.eglWaitGL();
EGLSurface result = null;
try {
result = mEgl.eglCreatePbufferSurface(mEglDisplay,
mEglConfig.eglConfig, surfaceAttribs);
checkEglError("eglCreatePbufferSurface");
if (result == null) {
throw new RuntimeException("surface was null");
}
} catch (final IllegalArgumentException e) {
Log.e(TAG, "createOffscreenSurface", e);
} catch (final RuntimeException e) {
Log.e(TAG, "createOffscreenSurface", e);
}
return result;
}
private final void destroyWindowSurface(EGLSurface surface) {
// if (DEBUG) Log.v(TAG, "destroySurface:");
if (surface != EGL10.EGL_NO_SURFACE) {
mEgl.eglMakeCurrent(mEglDisplay,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
mEgl.eglDestroySurface(mEglDisplay, surface);
}
surface = EGL10.EGL_NO_SURFACE;
// if (DEBUG) Log.v(TAG, "destroySurface:finished");
}
private final void checkEglError(final String msg) {
int error;
if ((error = mEgl.eglGetError()) != EGL10.EGL_SUCCESS) {
throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
}
}
private final EGLConfig getConfig(final int version,
final boolean hasDepthBuffer, final int stencilBits, final boolean isRecordable) {
int renderableType = EGL_OPENGL_ES2_BIT;
if (version >= 3) {
renderableType |= EGL_OPENGL_ES3_BIT_KHR;
}
// final int swapBehavior = dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
final int[] attribList = {
EGL10.EGL_RENDERABLE_TYPE, renderableType,
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
// EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT | swapBehavior,
EGL10.EGL_NONE, EGL10.EGL_NONE, //EGL10.EGL_STENCIL_SIZE, 8,
// this flag need to recording of MediaCodec
EGL10.EGL_NONE, EGL10.EGL_NONE, // EGL_RECORDABLE_ANDROID, 1,
EGL10.EGL_NONE, EGL10.EGL_NONE, // with_depth_buffer ? EGL10.EGL_DEPTH_SIZE : EGL10.EGL_NONE,
// with_depth_buffer ? 16 : 0,
EGL10.EGL_NONE
};
int offset = 10;
if (stencilBits > 0) { // ステンシルバッファ(常時未使用)
attribList[offset++] = EGL10.EGL_STENCIL_SIZE;
attribList[offset++] = 8;
}
if (hasDepthBuffer) { // デプスバッファ
attribList[offset++] = EGL10.EGL_DEPTH_SIZE;
attribList[offset++] = 16;
}
if (isRecordable && BuildCheck.isAndroid4_3()) {
// MediaCodecの入力用Surfaceの場合
// A-1000F(Android4.1.2)はこのフラグをつけるとうまく動かない
attribList[offset++] = EGL_RECORDABLE_ANDROID;
attribList[offset++] = 1;
}
for (int i = attribList.length - 1; i >= offset; i--) {
attribList[i] = EGL10.EGL_NONE;
}
EGLConfig config = internalGetConfig(attribList);
if ((config == null) && (version == 2)) {
if (isRecordable) {
// EGL_RECORDABLE_ANDROIDをつけると失敗する機種もあるので取り除く
final int n = attribList.length;
for (int i = 10; i < n - 1; i += 2) {
if (attribList[i] == EGL_RECORDABLE_ANDROID) {
for (int j = i; j < n; j++) {
attribList[j] = EGL10.EGL_NONE;
}
break;
}
}
config = internalGetConfig(attribList);
}
}
if (config == null) {
Log.w(TAG, "try to fallback to RGB565");
attribList[3] = 5;
attribList[5] = 6;
attribList[7] = 5;
config = internalGetConfig(attribList);
}
return config;
}
private EGLConfig internalGetConfig(final int[] attribList) {
final EGLConfig[] configs = new EGLConfig[1];
final int[] numConfigs = new int[1];
if (!mEgl.eglChooseConfig(mEglDisplay,
attribList, configs, configs.length, numConfigs)) {
return null;
}
return configs[0];
}
}

View File

@@ -0,0 +1,631 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.graphics.SurfaceTexture;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLExt;
import android.opengl.EGLSurface;
import android.opengl.GLES10;
import android.opengl.GLES20;
import android.os.Build;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import androidx.annotation.NonNull;
import com.serenegiant.utils.BuildCheck;
/**
* EGLレンダリングコンテキストを生成使用するためのヘルパークラス
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
/*package*/ class EGLBase14 extends EGLBase { // API >= 17
// private static final boolean DEBUG = false; // TODO set false on release
private static final String TAG = "EGLBase14";
private static final Context EGL_NO_CONTEXT = new Context(EGL14.EGL_NO_CONTEXT);
private Config mEglConfig = null;
@NonNull private Context mContext = EGL_NO_CONTEXT;
// private EGLContext mEglContext = EGL14.EGL_NO_CONTEXT;
private EGLDisplay mEglDisplay = EGL14.EGL_NO_DISPLAY;
private EGLContext mDefaultContext = EGL14.EGL_NO_CONTEXT;
private int mGlVersion = 2;
/**
* EGLレンダリングコンテキストのホルダークラス
*/
public static class Context extends IContext {
public final EGLContext eglContext;
private Context(final EGLContext context) {
eglContext = context;
}
@Override
@SuppressLint("NewApi")
public long getNativeHandle() {
return eglContext != null ?
(BuildCheck.isLollipop()
? eglContext.getNativeHandle() : eglContext.getHandle()) : 0L;
}
@Override
public Object getEGLContext() {
return eglContext;
}
}
public static class Config extends IConfig {
public final EGLConfig eglConfig;
private Config(final EGLConfig eglConfig) {
this.eglConfig = eglConfig;
}
}
/**
* EGLレンダリングコンテキストに紐付ける描画オブジェクト
*/
public static class EglSurface implements IEglSurface {
private final EGLBase14 mEglBase;
private EGLSurface mEglSurface = EGL14.EGL_NO_SURFACE;
private EglSurface(final EGLBase14 eglBase, final Object surface)
throws IllegalArgumentException {
// if (DEBUG) Log.v(TAG, "EglSurface:");
mEglBase = eglBase;
if ((surface instanceof Surface)
|| (surface instanceof SurfaceHolder)
|| (surface instanceof SurfaceTexture)
|| (surface instanceof SurfaceView)) {
mEglSurface = mEglBase.createWindowSurface(surface);
} else {
throw new IllegalArgumentException("unsupported surface");
}
}
/**
* 指定した大きさを持つオフスクリーンEglSurface(PBuffer)
* @param eglBase
* @param width
* @param height
*/
private EglSurface(final EGLBase14 eglBase,
final int width, final int height) {
// if (DEBUG) Log.v(TAG, "EglSurface:");
mEglBase = eglBase;
if ((width <= 0) || (height <= 0)) {
mEglSurface = mEglBase.createOffscreenSurface(1, 1);
} else {
mEglSurface = mEglBase.createOffscreenSurface(width, height);
}
}
@Override
public void makeCurrent() {
mEglBase.makeCurrent(mEglSurface);
if (mEglBase.getGlVersion() >= 2) {
GLES20.glViewport(0, 0,
mEglBase.getSurfaceWidth(mEglSurface),
mEglBase.getSurfaceHeight(mEglSurface));
} else {
GLES10.glViewport(0, 0,
mEglBase.getSurfaceWidth(mEglSurface),
mEglBase.getSurfaceHeight(mEglSurface));
}
}
@Override
public void swap() {
mEglBase.swap(mEglSurface);
}
@Override
public void swap(final long presentationTimeNs) {
mEglBase.swap(mEglSurface, presentationTimeNs);
}
public void setPresentationTime(final long presentationTimeNs) {
EGLExt.eglPresentationTimeANDROID(mEglBase.mEglDisplay,
mEglSurface, presentationTimeNs);
}
@Override
public IContext getContext() {
return mEglBase.getContext();
}
@Override
public boolean isValid() {
return (mEglSurface != null)
&& (mEglSurface != EGL14.EGL_NO_SURFACE)
&& (mEglBase.getSurfaceWidth(mEglSurface) > 0)
&& (mEglBase.getSurfaceHeight(mEglSurface) > 0);
}
@Override
public void release() {
// if (DEBUG) Log.v(TAG, "EglSurface:release:");
mEglBase.makeDefault();
mEglBase.destroyWindowSurface(mEglSurface);
mEglSurface = EGL14.EGL_NO_SURFACE;
}
}
/**
* コンストラクタ
* @param maxClientVersion
* @param sharedContext
* @param withDepthBuffer
* @param isRecordable
*/
public EGLBase14(final int maxClientVersion,
final Context sharedContext, final boolean withDepthBuffer,
final int stencilBits, final boolean isRecordable) {
// if (DEBUG) Log.v(TAG, "Constructor:");
init(maxClientVersion, sharedContext, withDepthBuffer, stencilBits, isRecordable);
}
/**
* 関連するリソースを破棄する
*/
@Override
public void release() {
// if (DEBUG) Log.v(TAG, "release:");
if (mEglDisplay != EGL14.EGL_NO_DISPLAY) {
destroyContext();
EGL14.eglTerminate(mEglDisplay);
EGL14.eglReleaseThread();
}
mEglDisplay = EGL14.EGL_NO_DISPLAY;
mContext = EGL_NO_CONTEXT;
}
/**
* 指定したSurfaceからEglSurfaceを生成する
* 生成したEglSurfaceをmakeCurrentした状態で戻る
* @param nativeWindow Surface/SurfaceTexture/SurfaceHolder
* @return
*/
@Override
public EglSurface createFromSurface(final Object nativeWindow) {
// if (DEBUG) Log.v(TAG, "createFromSurface:");
final EglSurface eglSurface = new EglSurface(this, nativeWindow);
eglSurface.makeCurrent();
return eglSurface;
}
/**
* 指定した大きさのオフスクリーンEglSurfaceを生成する
* 生成したEglSurfaceをmakeCurrentした状態で戻る
* @param width PBufferオフスクリーンのサイズ(0以下はだめ)
* @param height
* @return
*/
@Override
public EglSurface createOffscreen(final int width, final int height) {
// if (DEBUG) Log.v(TAG, "createOffscreen:");
final EglSurface eglSurface = new EglSurface(this, width, height);
eglSurface.makeCurrent();
return eglSurface;
}
/**
* GLESに文字列を問い合わせる
* @param what
* @return
*/
public String queryString(final int what) {
return EGL14.eglQueryString(mEglDisplay, what);
}
/**
* GLESバージョンを取得する
* @return 1, 2または3
*/
@Override
public int getGlVersion() {
return mGlVersion;
}
/**
* EGLレンダリングコンテキストを取得する
* このEGLBaseインスタンスを使って生成したEglSurfaceをmakeCurrentした状態で
* eglGetCurrentContextを呼び出すのと一緒
* @return
*/
@Override
public Context getContext() {
return mContext;
}
/**
* EGLコンフィグを取得する
* @return
*/
@Override
public Config getConfig() {
return mEglConfig;
}
/**
* EGLレンダリングコンテキストとスレッドの紐付けを解除する
*/
@Override
public void makeDefault() {
// if (DEBUG) Log.v(TAG, "makeDefault:");
if (!EGL14.eglMakeCurrent(mEglDisplay,
EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)) {
Log.w("TAG", "makeDefault" + EGL14.eglGetError());
}
}
/**
* eglWaitGLとeglWaitNativeを呼ぶ
*
* eglWaitGL: コマンドキュー内のコマンドをすべて転送する, GLES20.glFinish()と同様の効果
* eglWaitNative: GPU側の描画処理が終了するまで実行をブロックする
*/
@Override
public void sync() {
EGL14.eglWaitGL(); // GLES20.glFinish()と同様の効果
EGL14.eglWaitNative(EGL14.EGL_CORE_NATIVE_ENGINE);
}
private void init(final int maxClientVersion, Context sharedContext,
final boolean withDepthBuffer, final int stencilBits, final boolean isRecordable) {
// if (DEBUG) Log.v(TAG, "init:");
if (mEglDisplay != EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("EGL already set up");
}
mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
// EGLのバージョンを取得
final int[] version = new int[2];
if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
mEglDisplay = null;
throw new RuntimeException("eglInitialize failed");
}
sharedContext = (sharedContext != null) ? sharedContext : EGL_NO_CONTEXT;
EGLConfig config;
if (maxClientVersion >= 3) {
// GLES3で取得できるかどうか試してみる
config = getConfig(3, withDepthBuffer, stencilBits, isRecordable);
if (config != null) {
final EGLContext context = createContext(sharedContext, config, 3);
if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) {
// ここは例外生成したくないのでcheckEglErrorの代わりに自前でチェック
mEglConfig = new Config(config);
mContext = new Context(context);
mGlVersion = 3;
}
}
}
// GLES3で取得できなかった時はGLES2を試みる
if ((maxClientVersion >= 2)
&& ((mContext == null) || (mContext.eglContext == EGL14.EGL_NO_CONTEXT))) {
config = getConfig(2, withDepthBuffer, stencilBits, isRecordable);
if (config == null) {
throw new RuntimeException("chooseConfig failed");
}
try {
// create EGL rendering context
final EGLContext context = createContext(sharedContext, config, 2);
checkEglError("eglCreateContext");
mEglConfig = new Config(config);
mContext = new Context(context);
mGlVersion = 2;
} catch (final Exception e) {
if (isRecordable) {
config = getConfig(2, withDepthBuffer, stencilBits, false);
if (config == null) {
throw new RuntimeException("chooseConfig failed");
}
// create EGL rendering context
final EGLContext context = createContext(sharedContext, config, 2);
checkEglError("eglCreateContext");
mEglConfig = new Config(config);
mContext = new Context(context);
mGlVersion = 2;
}
}
}
if ((mContext == null) || (mContext.eglContext == EGL14.EGL_NO_CONTEXT)) {
config = getConfig(1, withDepthBuffer, stencilBits, isRecordable);
if (config == null) {
throw new RuntimeException("chooseConfig failed");
}
// create EGL rendering context
final EGLContext context = createContext(sharedContext, config, 1);
checkEglError("eglCreateContext");
mEglConfig = new Config(config);
mContext = new Context(context);
mGlVersion = 1;
}
// confirm whether the EGL rendering context is successfully created
final int[] values = new int[1];
EGL14.eglQueryContext(mEglDisplay,
mContext.eglContext, EGL14.EGL_CONTEXT_CLIENT_VERSION, values, 0);
Log.d(TAG, "EGLContext created, client version " + values[0]);
makeDefault(); // makeCurrent(EGL14.EGL_NO_SURFACE);
}
/**
* change context to draw this window surface
* @return
*/
private boolean makeCurrent(final EGLSurface surface) {
// if (DEBUG) Log.v(TAG, "makeCurrent:");
/* if (mEglDisplay == null) {
if (DEBUG) Log.d(TAG, "makeCurrent:eglDisplay not initialized");
} */
if (surface == null || surface == EGL14.EGL_NO_SURFACE) {
final int error = EGL14.eglGetError();
if (error == EGL14.EGL_BAD_NATIVE_WINDOW) {
Log.e(TAG, "makeCurrent:returned EGL_BAD_NATIVE_WINDOW.");
}
return false;
}
// attach EGL rendering context to specific EGL window surface
if (!EGL14.eglMakeCurrent(mEglDisplay, surface, surface, mContext.eglContext)) {
Log.w("TAG", "eglMakeCurrent" + EGL14.eglGetError());
return false;
}
return true;
}
private int swap(final EGLSurface surface) {
// if (DEBUG) Log.v(TAG, "swap:");
if (!EGL14.eglSwapBuffers(mEglDisplay, surface)) {
final int err = EGL14.eglGetError();
// if (DEBUG) Log.w(TAG, "swap:err=" + err);
return err;
}
return EGL14.EGL_SUCCESS;
}
private int swap(final EGLSurface surface, final long presentationTimeNs) {
// if (DEBUG) Log.v(TAG, "swap:");
EGLExt.eglPresentationTimeANDROID(mEglDisplay, surface, presentationTimeNs);
if (!EGL14.eglSwapBuffers(mEglDisplay, surface)) {
final int err = EGL14.eglGetError();
// if (DEBUG) Log.w(TAG, "swap:err=" + err);
return err;
}
return EGL14.EGL_SUCCESS;
}
private EGLContext createContext(final Context sharedContext,
final EGLConfig config, final int version) {
// if (DEBUG) Log.v(TAG, "createContext:");
final int[] attrib_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, version,
EGL14.EGL_NONE
};
final EGLContext context = EGL14.eglCreateContext(mEglDisplay,
config, sharedContext.eglContext, attrib_list, 0);
// checkEglError("eglCreateContext");
return context;
}
private void destroyContext() {
// if (DEBUG) Log.v(TAG, "destroyContext:");
if (!EGL14.eglDestroyContext(mEglDisplay, mContext.eglContext)) {
Log.e("destroyContext", "display:" + mEglDisplay
+ " context: " + mContext.eglContext);
Log.e(TAG, "eglDestroyContext:" + EGL14.eglGetError());
}
mContext = EGL_NO_CONTEXT;
if (mDefaultContext != EGL14.EGL_NO_CONTEXT) {
if (!EGL14.eglDestroyContext(mEglDisplay, mDefaultContext)) {
Log.e("destroyContext", "display:" + mEglDisplay
+ " context: " + mDefaultContext);
Log.e(TAG, "eglDestroyContext:" + EGL14.eglGetError());
}
mDefaultContext = EGL14.EGL_NO_CONTEXT;
}
}
private final int[] mSurfaceDimension = new int[2];
private final int getSurfaceWidth(final EGLSurface surface) {
final boolean ret = EGL14.eglQuerySurface(mEglDisplay,
surface, EGL14.EGL_WIDTH, mSurfaceDimension, 0);
if (!ret) mSurfaceDimension[0] = 0;
return mSurfaceDimension[0];
}
private final int getSurfaceHeight(final EGLSurface surface) {
final boolean ret = EGL14.eglQuerySurface(mEglDisplay,
surface, EGL14.EGL_HEIGHT, mSurfaceDimension, 1);
if (!ret) mSurfaceDimension[1] = 0;
return mSurfaceDimension[1];
}
/**
* nativeWindow should be one of the Surface, SurfaceHolder and SurfaceTexture
* @param nativeWindow
* @return
*/
private final EGLSurface createWindowSurface(final Object nativeWindow) {
// if (DEBUG) Log.v(TAG, "createWindowSurface:nativeWindow=" + nativeWindow);
final int[] surfaceAttribs = {
EGL14.EGL_NONE
};
EGLSurface result = null;
try {
result = EGL14.eglCreateWindowSurface(mEglDisplay,
mEglConfig.eglConfig, nativeWindow, surfaceAttribs, 0);
if (result == null || result == EGL14.EGL_NO_SURFACE) {
final int error = EGL14.eglGetError();
if (error == EGL14.EGL_BAD_NATIVE_WINDOW) {
Log.e(TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
}
throw new RuntimeException("createWindowSurface failed error=" + error);
}
makeCurrent(result);
// 画面サイズ・フォーマットの取得
} catch (final Exception e) {
Log.e(TAG, "eglCreateWindowSurface", e);
throw new IllegalArgumentException(e);
}
return result;
}
/**
* Creates an EGL surface associated with an offscreen buffer.
*/
private final EGLSurface createOffscreenSurface(final int width, final int height) {
// if (DEBUG) Log.v(TAG, "createOffscreenSurface:");
final int[] surfaceAttribs = {
EGL14.EGL_WIDTH, width,
EGL14.EGL_HEIGHT, height,
EGL14.EGL_NONE
};
EGLSurface result = null;
try {
result = EGL14.eglCreatePbufferSurface(mEglDisplay,
mEglConfig.eglConfig, surfaceAttribs, 0);
checkEglError("eglCreatePbufferSurface");
if (result == null) {
throw new RuntimeException("surface was null");
}
} catch (final IllegalArgumentException e) {
Log.e(TAG, "createOffscreenSurface", e);
} catch (final RuntimeException e) {
Log.e(TAG, "createOffscreenSurface", e);
}
return result;
}
private void destroyWindowSurface(EGLSurface surface) {
// if (DEBUG) Log.v(TAG, "destroySurface:");
if (surface != EGL14.EGL_NO_SURFACE) {
EGL14.eglMakeCurrent(mEglDisplay,
EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
EGL14.eglDestroySurface(mEglDisplay, surface);
}
surface = EGL14.EGL_NO_SURFACE;
// if (DEBUG) Log.v(TAG, "destroySurface:finished");
}
private void checkEglError(final String msg) {
int error;
if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
}
}
private EGLConfig getConfig(final int version,
final boolean hasDepthBuffer, final int stencilBits, final boolean isRecordable) {
int renderableType = EGL_OPENGL_ES2_BIT;
if (version >= 3) {
renderableType |= EGL_OPENGL_ES3_BIT_KHR;
}
final int[] attribList = {
EGL14.EGL_RENDERABLE_TYPE, renderableType,
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
// EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT | swapBehavior,
EGL14.EGL_NONE, EGL14.EGL_NONE, //EGL14.EGL_STENCIL_SIZE, 8,
// this flag need to recording of MediaCodec
EGL14.EGL_NONE, EGL14.EGL_NONE, //EGL_RECORDABLE_ANDROID, 1,
EGL14.EGL_NONE, EGL14.EGL_NONE, // with_depth_buffer ? EGL14.EGL_DEPTH_SIZE : EGL14.EGL_NONE,
// with_depth_buffer ? 16 : 0,
EGL14.EGL_NONE
};
int offset = 10;
if (stencilBits > 0) { // ステンシルバッファ(常時未使用)
attribList[offset++] = EGL14.EGL_STENCIL_SIZE;
attribList[offset++] = stencilBits;
}
if (hasDepthBuffer) { // デプスバッファ
attribList[offset++] = EGL14.EGL_DEPTH_SIZE;
attribList[offset++] = 16;
}
if (isRecordable && BuildCheck.isAndroid4_3()) {// MediaCodecの入力用Surfaceの場合
attribList[offset++] = EGL_RECORDABLE_ANDROID;
attribList[offset++] = 1;
}
for (int i = attribList.length - 1; i >= offset; i--) {
attribList[i] = EGL14.EGL_NONE;
}
EGLConfig config = internalGetConfig(attribList);
if ((config == null) && (version == 2)) {
if (isRecordable) {
// EGL_RECORDABLE_ANDROIDをつけると失敗する機種もあるので取り除く
final int n = attribList.length;
for (int i = 10; i < n - 1; i += 2) {
if (attribList[i] == EGL_RECORDABLE_ANDROID) {
for (int j = i; j < n; j++) {
attribList[j] = EGL14.EGL_NONE;
}
break;
}
}
config = internalGetConfig(attribList);
}
}
if (config == null) {
Log.w(TAG, "try to fallback to RGB565");
attribList[3] = 5;
attribList[5] = 6;
attribList[7] = 5;
config = internalGetConfig(attribList);
}
return config;
}
private EGLConfig internalGetConfig(final int[] attribList) {
final EGLConfig[] configs = new EGLConfig[1];
final int[] numConfigs = new int[1];
if (!EGL14.eglChooseConfig(mEglDisplay,
attribList, 0, configs, 0, configs.length, numConfigs, 0)) {
return null;
}
return configs[0];
}
}

View File

@@ -0,0 +1,548 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import static com.serenegiant.glutils.ShaderConst.FRAGMENT_SHADER_SIMPLE_OES;
import static com.serenegiant.glutils.ShaderConst.FUNC_HSV2RGB;
import static com.serenegiant.glutils.ShaderConst.FUNC_RGB2HSV;
import static com.serenegiant.glutils.ShaderConst.HEADER_OES;
import static com.serenegiant.glutils.ShaderConst.SAMPLER_OES;
import static com.serenegiant.glutils.ShaderConst.SHADER_VERSION;
import android.annotation.SuppressLint;
import android.opengl.GLES20;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* GL_TEXTURE_EXTERNAL_OESテクスチャを受け取ってSurfaceへ分配描画するクラス
* RendererHolderにフラグメントシェーダーでのフィルター処理を追加
* ...カラーマトリックスを掛けるほうがいいかなぁ
* ...色はuniform変数で渡す方がいいかも
*/
public class EffectRendererHolder extends AbstractRendererHolder {
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
private static final String TAG = EffectRendererHolder.class.getSimpleName();
private static final int MAX_PARAM_NUM = 18;
public static final int EFFECT_NON = 0;
public static final int EFFECT_GRAY = 1;
public static final int EFFECT_GRAY_REVERSE = 2;
public static final int EFFECT_BIN = 3;
public static final int EFFECT_BIN_YELLOW = 4;
public static final int EFFECT_BIN_GREEN = 5;
public static final int EFFECT_BIN_REVERSE = 6;
public static final int EFFECT_BIN_REVERSE_YELLOW = 7;
public static final int EFFECT_BIN_REVERSE_GREEN = 8;
/**
* 赤色黄色を強調
* setParamsはfloat[12] {
* 0.17f, 0.85f, // 赤色&黄色の色相下側閾値, 上側閾値
* 0.50f, 1.0f, // 強調する彩度下限, 上限
* 0.40f, 1.0f, // 強調する明度下限, 上限
* 1.0f, 1.0f, 5.0f, // 強調時のファクター(H, S, Vの順) 明度(x5.0) = 1.0
* 1.0f, 0.5f, 0.8f, // 通常時のファクター(H, S, Vの順) 彩度(x0.5)と明度(x0.8)を少し落とす
* }
*/
public static final int EFFECT_EMPHASIZE_RED_YELLOW = 9;
/**
* 赤色黄色と白を強調
* setParamsはfloat[12] {
* 0.17f, 0.85f, // 赤色&黄色の色相下側閾値, 上側閾値
* 0.50f, 1.0f, // 強調する彩度下限, 上限
* 0.40f, 1.0f, // 強調する明度下限, 上限
* 1.0f, 1.0f, 5.0f, // 強調時のファクター(H, S, Vの順) 明度(x5.0) = 1.0
* 1.0f, 0.5f, 0.8f, // 通常時のファクター(H, S, Vの順) 彩度(x0.5)と明度(x0.8)を少し落とす
* 白のパラメータは今はなし
*/
public static final int EFFECT_EMPHASIZE_RED_YELLOW_WHITE = 10;
/**
* 黄色と白を強調
* setParamsはfloat[12] {
* 0.17f, 0.85f, // 赤色&黄色の色相下側閾値, 上側閾値 FIXME 未調整
* 0.50f, 1.0f, // 強調する彩度下限, 上限
* 0.40f, 1.0f, // 強調する明度下限, 上限
* 1.0f, 1.0f, 5.0f, // 強調時のファクター(H, S, Vの順) 明度(x5.0) = 1.0
* 1.0f, 0.5f, 0.8f, // 通常時のファクター(H, S, Vの順) 彩度(x0.5)と明度(x0.8)を少し落とす
* 白のパラメータは今はなし
*/
public static final int EFFECT_EMPHASIZE_YELLOW_WHITE = 11;
/** 内蔵映像効果の数 */
public static final int EFFECT_NUM = 12;
/**
* グレースケール変換のためのフラグメントシェーダーのベース文字列
* header(HEADER_OESかHEADER_2D)とサンプラーの種類文字列(SAMPLER_OESかSAMPLER_2D)を渡すこと
*/
private static final String FRAGMENT_SHADER_GRAY_BASE = SHADER_VERSION +
"%s" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform %s sTexture;\n" +
"const vec3 conv = vec3(0.3, 0.59, 0.11);\n" +
"void main() {\n" +
" vec4 tc = texture2D(sTexture, vTextureCoord);\n" +
" float color = dot(tc.rgb, conv);\n" +
" vec3 cl3 = vec3(color, color, color);\n" +
" gl_FragColor = vec4(cl3, 1.0);\n" +
"}\n";
private static final String FRAGMENT_SHADER_GRAY_OES
= String.format(FRAGMENT_SHADER_GRAY_BASE, HEADER_OES, SAMPLER_OES);
/**
* 白黒反転したグレースケール変換のためのフラグメントシェーダーのベース文字列
* header(HEADER_OESかHEADER_2D)とサンプラーの種類文字列(SAMPLER_OESかSAMPLER_2D)を渡すこと
*/
private static final String FRAGMENT_SHADER_GRAY_REVERSE_BASE = SHADER_VERSION +
"%s" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform %s sTexture;\n" +
"const vec3 conv = vec3(0.3, 0.59, 0.11);\n" +
"void main() {\n" +
" vec4 tc = texture2D(sTexture, vTextureCoord);\n" +
" float color = dot(tc.rgb, conv);\n" +
" vec3 cl3 = vec3(color, color, color);\n" +
" gl_FragColor = vec4(clamp(vec3(1.0, 1.0, 1.0) - cl3, 0.0, 1.0), 1.0);\n" +
"}\n";
private static final String FRAGMENT_SHADER_GRAY_REVERSE_OES
= String.format(FRAGMENT_SHADER_GRAY_REVERSE_BASE, HEADER_OES, SAMPLER_OES);
/**
* 2値化のためのフラグメントシェーダーのベース文字列
* header(HEADER_OESかHEADER_2D)とサンプラーの種類文字列(SAMPLER_OESかSAMPLER_2D)、
* 変換後の明るい部分用の色を指定するための文字列(R, G, Bの順)を渡すこと
*/
private static final String FRAGMENT_SHADER_BIN_BASE = SHADER_VERSION +
"%s" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform %s sTexture;\n" +
"const vec3 conv = vec3(0.3, 0.59, 0.11);\n" +
"const vec3 cl = vec3(%s);\n" +
"void main() {\n" +
" vec4 tc = texture2D(sTexture, vTextureCoord);\n" +
" float color = dot(tc.rgb, conv);\n" +
" vec3 bin = step(0.3, vec3(color, color, color));\n" +
" gl_FragColor = vec4(cl * bin, 1.0);\n" +
"}\n";
private static final String FRAGMENT_SHADER_BIN_OES
= String.format(FRAGMENT_SHADER_BIN_BASE, HEADER_OES, SAMPLER_OES, "1.0, 1.0, 1.0");
private static final String FRAGMENT_SHADER_BIN_YELLOW_OES
= String.format(FRAGMENT_SHADER_BIN_BASE, HEADER_OES, SAMPLER_OES, "1.0, 1.0, 0.0");
private static final String FRAGMENT_SHADER_BIN_GREEN_OES
= String.format(FRAGMENT_SHADER_BIN_BASE, HEADER_OES, SAMPLER_OES, "0.0, 1.0, 0.0");
/**
* 反転した2値化のためのフラグメントシェーダーのベース文字列
* header(HEADER_OESかHEADER_2D)とサンプラーの種類文字列(SAMPLER_OESかSAMPLER_2D)、
* 変換後の明るい部分用の色を指定するための文字列(R, G, Bの順)を渡すこと
*/
private static final String FRAGMENT_SHADER_BIN_REVERSE_BASE = SHADER_VERSION +
"%s" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform %s sTexture;\n" +
"const vec3 conv = vec3(0.3, 0.59, 0.11);\n" +
"const vec3 cl = vec3(%s);\n" +
"void main() {\n" +
" vec4 tc = texture2D(sTexture, vTextureCoord);\n" +
" float color = dot(tc.rgb, conv);\n" +
" vec3 bin = step(0.3, vec3(color, color, color));\n" +
" gl_FragColor = vec4(cl * (vec3(1.0, 1.0, 1.0) - bin), 1.0);\n" +
"}\n";
private static final String FRAGMENT_SHADER_BIN_REVERSE_OES
= String.format(FRAGMENT_SHADER_BIN_REVERSE_BASE, HEADER_OES, SAMPLER_OES, "1.0, 1.0, 1.0");
private static final String FRAGMENT_SHADER_BIN_REVERSE_YELLOW_OES
= String.format(FRAGMENT_SHADER_BIN_REVERSE_BASE, HEADER_OES, SAMPLER_OES, "1.0, 1.0, 0.0");
private static final String FRAGMENT_SHADER_BIN_REVERSE_GREEN_OES
= String.format(FRAGMENT_SHADER_BIN_REVERSE_BASE, HEADER_OES, SAMPLER_OES, "0.0, 1.0, 0.0");
/**
* 赤と黄色を強調するためのフラグメントシェーダーのベース文字列
*/
private static final String FRAGMENT_SHADER_EMPHASIZE_RED_YELLOW_BASE = SHADER_VERSION +
"%s" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform %s sTexture;\n" +
"uniform float uParams[" + MAX_PARAM_NUM + "];\n" +
FUNC_RGB2HSV +
FUNC_HSV2RGB +
"void main() {\n" +
" vec3 hsv = rgb2hsv(texture2D(sTexture, vTextureCoord).rgb);\n" + // RGBをHSVに変換
" if ( ((hsv.g >= uParams[2]) && (hsv.g <= uParams[3]))\n" + // s
" && ((hsv.b >= uParams[4]) && (hsv.b <= uParams[5]))\n" + // v
" && ((hsv.r <= uParams[0]) || (hsv.r >= uParams[1])) ) {\n" + // h
" hsv = hsv * vec3(uParams[6], uParams[7], uParams[8]);\n" + // 赤色と黄色の範囲
" } else {\n" +
" hsv = hsv * vec3(uParams[9], uParams[10], uParams[11]);\n" + // それ以外なら
" }\n" +
" gl_FragColor = vec4(hsv2rgb(clamp(hsv, 0.0, 1.0)), 1.0);\n" + // HSVをRGBに戻す
"}\n";
private static final String FRAGMENT_SHADER_EMPHASIZE_RED_YELLOW_OES
= String.format(FRAGMENT_SHADER_EMPHASIZE_RED_YELLOW_BASE, HEADER_OES, SAMPLER_OES);
/**
* 赤と黄色と白色を強調するためのフラグメントシェーダーのベース文字列
*/
private static final String FRAGMENT_SHADER_EMPHASIZE_RED_YELLOW_WHITE_BASE = SHADER_VERSION +
"%s" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform %s sTexture;\n" +
"uniform float uParams[" + MAX_PARAM_NUM + "];\n" +
FUNC_RGB2HSV +
FUNC_HSV2RGB +
"void main() {\n" +
" vec3 hsv = rgb2hsv(texture2D(sTexture, vTextureCoord).rgb);\n" + // RGBをHSVに変換
" if ( ((hsv.g >= uParams[2]) && (hsv.g <= uParams[3]))\n" + // s
" && ((hsv.b >= uParams[4]) && (hsv.b <= uParams[5]))\n" + // v
" && ((hsv.r <= uParams[0]) || (hsv.r >= uParams[1])) ) {\n" + // h
" hsv = hsv * vec3(uParams[6], uParams[7], uParams[8]);\n" + // 赤色と黄色の範囲
" } else if ((hsv.g < uParams[12]) && (hsv.b < uParams[13])) {\n" + // 彩度が一定以下, 明度が一定以下なら
" hsv = hsv * vec3(1.0, 0.0, 2.0);\n" + // 色相そのまま, 彩度0, 明度x2
" } else {\n" +
" hsv = hsv * vec3(uParams[9], uParams[10], uParams[11]);\n" + // それ以外なら
" }\n" +
" gl_FragColor = vec4(hsv2rgb(clamp(hsv, 0.0, 1.0)), 1.0);\n" + // HSVをRGBに戻す
"}\n";
private static final String FRAGMENT_SHADER_EMPHASIZE_RED_YELLOW_WHITE_OES
= String.format(FRAGMENT_SHADER_EMPHASIZE_RED_YELLOW_WHITE_BASE, HEADER_OES, SAMPLER_OES);
/**
* 黄色と白を強調するためのフラグメントシェーダーのベース文字列
* 今はFRAGMENT_SHADER_EMPHASIZE_RED_YELLOW_WHITE_BASEと同じ(違うパラメータ渡せば良いだけなので)
*/
private static final String FRAGMENT_SHADER_EMPHASIZE_YELLOW_WHITE_BASE = SHADER_VERSION +
"%s" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform %s sTexture;\n" +
"uniform float uParams[" + MAX_PARAM_NUM + "];\n" +
FUNC_RGB2HSV +
FUNC_HSV2RGB +
"void main() {\n" +
" vec3 rgb = texture2D(sTexture, vTextureCoord).rgb;\n" + // RGB
" vec3 hsv = rgb2hsv(rgb);\n" + // RGBをHSVに変換
" if ( ((hsv.r >= uParams[0]) && (hsv.r <= uParams[1]))\n" + // h
" && ((hsv.g >= uParams[2]) && (hsv.g <= uParams[3]))\n" + // s
" && ((hsv.b >= uParams[4]) && (hsv.b <= uParams[5])) ) {\n" + // v
" hsv = hsv * vec3(uParams[6], uParams[7], uParams[8]);\n" + // 黄色の範囲
" } else if ((hsv.g < uParams[12]) && (hsv.b > uParams[13])) {\n" + // 彩度が一定以下, 明度が一定以上なら
" hsv = hsv * vec3(1.0, 0.0, 2.0);\n" + // 色相そのまま, 彩度0, 明度x2
" } else {\n" +
" hsv = hsv * vec3(uParams[9], uParams[10], uParams[11]);\n" + // それ以外なら
" }\n" +
" gl_FragColor = vec4(hsv2rgb(clamp(hsv, 0.0, 1.0)), 1.0);\n" + // HSVをRGBに戻す
"}\n";
private static final String FRAGMENT_SHADER_EMPHASIZE_YELLOW_WHITE_OES
= String.format(FRAGMENT_SHADER_EMPHASIZE_YELLOW_WHITE_BASE, HEADER_OES, SAMPLER_OES);
public EffectRendererHolder(final int width, final int height,
@Nullable final RenderHolderCallback callback) {
this(width, height,
3, null, EglTask.EGL_FLAG_RECORDABLE,
callback);
}
public EffectRendererHolder(final int width, final int height,
final int maxClientVersion, final EGLBase.IContext sharedContext, final int flags,
@Nullable final RenderHolderCallback callback) {
super(width, height,
maxClientVersion, sharedContext, flags,
callback);
}
@Override
@NonNull
protected RendererTask createRendererTask(final int width, final int height,
final int maxClientVersion, final EGLBase.IContext sharedContext, final int flags) {
return new MyRendererTask(this, width, height,
maxClientVersion, sharedContext, flags);
}
//================================================================================
// クラス固有publicメソッド
//================================================================================
/**
* 映像効果をセット
* 継承して独自の映像効果を追加する時はEFFECT_NUMよりも大きい値を使うこと
* @param effect
*/
public void changeEffect(final int effect) {
((MyRendererTask)mRendererTask).changeEffect(effect);
}
/**
* 現在の映像効果番号を取得
* @return
*/
public int getCurrentEffect() {
return ((MyRendererTask)mRendererTask).mEffect;
}
/**
* 現在選択中の映像フィルタにパラメータ配列をセット
* 現在対応しているのは色強調用の映像効果のみ(n=12以上必要)
* @param params
*/
public void setParams(@NonNull final float[] params) {
((MyRendererTask)mRendererTask).setParams(-1, params);
}
/**
* 指定した映像フィルタにパラメータ配列をセット
* 現在対応しているのは色強調用の映像効果のみ(n=12以上必要)
* @param effect EFFECT_NONより大きいこと
* @param params
* @throws IllegalArgumentException effectが範囲外ならIllegalArgumentException生成
*/
public void setParams(final int effect, @NonNull final float[] params)
throws IllegalArgumentException {
if (effect > EFFECT_NON) {
((MyRendererTask)mRendererTask).setParams(effect, params);
} else {
throw new IllegalArgumentException("invalid effect number:" + effect);
}
}
/**
* 内蔵映像効果以外のeffectを指定したときの処理
* 描画用のワーカースレッド上で呼び出される
* このクラスでは無変換のフラグメントシェーダーを適用する
* @param effect
* @param drawer GLDrawer2Dインスタンス
*/
protected void handleDefaultEffect(final int effect,
@NonNull final IDrawer2dES2 drawer) {
if (drawer instanceof GLDrawer2D) {
((GLDrawer2D) drawer).resetShader();
}
}
//================================================================================
// 実装
//================================================================================
private static final int REQUEST_CHANGE_EFFECT = 100;
private static final int REQUEST_SET_PARAMS = 101;
/**
* ワーカースレッド上でOpenGL|ESを用いてマスター映像を分配描画するためのインナークラス
*/
protected static final class MyRendererTask extends RendererTask {
private final SparseArray<float[]> mParams = new SparseArray<float[]>();
private int muParamsLoc;
private float[] mCurrentParams;
private int mEffect;
public MyRendererTask(final EffectRendererHolder parent,
final int width, final int height) {
super(parent, width, height);
}
public MyRendererTask(@NonNull final AbstractRendererHolder parent,
final int width, final int height,
final int maxClientVersion,
final EGLBase.IContext sharedContext, final int flags) {
super(parent, width, height, maxClientVersion, sharedContext, flags);
}
/**
* ワーカースレッド開始時の処理(ここはワーカースレッド上)
*/
@SuppressLint("NewApi")
@Override
protected void internalOnStart() {
// if (DEBUG) Log.v(TAG, "onStart:");
super.internalOnStart();
mParams.clear();
mParams.put(EFFECT_EMPHASIZE_RED_YELLOW, new float[] {
0.17f, 0.85f, // 赤色&黄色の色相下側閾値, 上側閾値
0.50f, 1.0f, // 強調する彩度下限, 上限
0.40f, 1.0f, // 強調する明度下限, 上限
1.0f, 1.0f, 5.0f, // 強調時のファクター(H, S, Vの順) 明度(x5.0) = 1.0
1.0f, 1.0f, 1.0f, // 通常時のファクター(H, S, Vの順)
});
mParams.put(EFFECT_EMPHASIZE_RED_YELLOW_WHITE, new float[] {
0.17f, 0.85f, // 赤色&黄色の色相下側閾値, 上側閾値
0.50f, 1.0f, // 強調する彩度下限, 上限
0.40f, 1.0f, // 強調する明度下限, 上限
1.0f, 1.0f, 5.0f, // 強調時のファクター(H, S, Vの順) 明度(x5.0) = 1.0
1.0f, 1.0f, 1.0f, // 通常時のファクター(H, S, Vの順)
});
mParams.put(EFFECT_EMPHASIZE_YELLOW_WHITE, new float[] {
0.10f, 0.19f, // 黄色の色相h下側閾値, 上側閾値
0.30f, 1.00f, // 強調する彩度s下限, 上限
0.30f, 1.00f, // 強調する明度v下限, 上限
1.00f, 1.00f, 5.00f, // 強調時のファクター(H, S, Vの順) 明度(x5.0) = 1.0
1.00f, 0.80f, 0.80f, // 通常時のファクター(H, S, Vの順) 彩度(x0.8)と明度(x0.8)を少し落とす
0.15f, 0.40f, // 白強調時の彩度上限, 白強調時の明度下限
0, 0, 0, 0, // ダミー
});
mEffect = EFFECT_NON;
handleChangeEffect(EFFECT_NON);
// if (DEBUG) Log.v(TAG, "onStart:finished");
}
@Override
protected Object processRequest(final int request,
final int arg1, final int arg2, final Object obj) {
Object result = null;
switch (request) {
case REQUEST_CHANGE_EFFECT:
handleChangeEffect(arg1);
break;
case REQUEST_SET_PARAMS:
handleSetParam(arg1, (float[])obj);
break;
default:
result = super.processRequest(request, arg1, arg2, obj);
break;
}
return result;
}
public void changeEffect(final int effect) {
checkFinished();
if (mEffect != effect) {
offer(REQUEST_CHANGE_EFFECT, effect);
}
}
public void setParams(final int effect, @NonNull final float[] params) {
checkFinished();
offer(REQUEST_SET_PARAMS, effect, 0, params);
}
//================================================================================
// ワーカースレッド上での処理
//================================================================================
/**
* 映像効果を変更
* @param effect
*/
protected void handleChangeEffect(final int effect) {
mEffect = effect;
switch (effect) {
case EFFECT_NON:
mDrawer.updateShader(FRAGMENT_SHADER_SIMPLE_OES);
break;
case EFFECT_GRAY:
mDrawer.updateShader(FRAGMENT_SHADER_GRAY_OES);
break;
case EFFECT_GRAY_REVERSE:
mDrawer.updateShader(FRAGMENT_SHADER_GRAY_REVERSE_OES);
break;
case EFFECT_BIN:
mDrawer.updateShader(FRAGMENT_SHADER_BIN_OES);
break;
case EFFECT_BIN_YELLOW:
mDrawer.updateShader(FRAGMENT_SHADER_BIN_YELLOW_OES);
break;
case EFFECT_BIN_GREEN:
mDrawer.updateShader(FRAGMENT_SHADER_BIN_GREEN_OES);
break;
case EFFECT_BIN_REVERSE:
mDrawer.updateShader(FRAGMENT_SHADER_BIN_REVERSE_OES);
break;
case EFFECT_BIN_REVERSE_YELLOW:
mDrawer.updateShader(FRAGMENT_SHADER_BIN_REVERSE_YELLOW_OES);
break;
case EFFECT_BIN_REVERSE_GREEN:
mDrawer.updateShader(FRAGMENT_SHADER_BIN_REVERSE_GREEN_OES);
break;
case EFFECT_EMPHASIZE_RED_YELLOW:
mDrawer.updateShader(FRAGMENT_SHADER_EMPHASIZE_RED_YELLOW_OES);
break;
case EFFECT_EMPHASIZE_RED_YELLOW_WHITE:
mDrawer.updateShader(FRAGMENT_SHADER_EMPHASIZE_RED_YELLOW_WHITE_OES);
break;
case EFFECT_EMPHASIZE_YELLOW_WHITE:
mDrawer.updateShader(FRAGMENT_SHADER_EMPHASIZE_YELLOW_WHITE_OES);
break;
default:
try {
((EffectRendererHolder)getParent())
.handleDefaultEffect(effect, mDrawer);
} catch (final Exception e) {
mDrawer.resetShader();
Log.w(TAG, e);
}
break;
}
muParamsLoc = mDrawer.glGetUniformLocation("uParams");
mCurrentParams = mParams.get(effect);
updateParams();
}
/**
* 映像効果用のパラメーターをセット
* @param effect
* @param params
*/
private void handleSetParam(final int effect, @NonNull final float[] params) {
if ((effect < EFFECT_NON) || (mEffect == effect)) {
mCurrentParams = params;
mParams.put(mEffect, params);
updateParams();
} else {
mParams.put(effect, params);
}
}
/**
* 映像効果用のパラメータをGPUへ適用
*/
private void updateParams() {
final int n = Math.min(mCurrentParams != null
? mCurrentParams.length : 0, MAX_PARAM_NUM);
if ((muParamsLoc >= 0) && (n > 0)) {
mDrawer.glUseProgram();
GLES20.glUniform1fv(muParamsLoc, n, mCurrentParams, 0);
}
}
}
}

View File

@@ -0,0 +1,122 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import androidx.annotation.Nullable;
import com.serenegiant.utils.MessageTask;
public abstract class EglTask extends MessageTask {
// private static final boolean DEBUG = false;
// private static final String TAG = "EglTask";
public static final int EGL_FLAG_DEPTH_BUFFER = 0x01;
public static final int EGL_FLAG_RECORDABLE = 0x02;
public static final int EGL_FLAG_STENCIL_1BIT = 0x04;
// public static final int EGL_FLAG_STENCIL_2BIT = 0x08;
// public static final int EGL_FLAG_STENCIL_4BIT = 0x10;
public static final int EGL_FLAG_STENCIL_8BIT = 0x20;
private EGLBase mEgl = null;
private EGLBase.IEglSurface mEglHolder;
public EglTask(final EGLBase.IContext sharedContext, final int flags) {
// if (DEBUG) Log.i(TAG, "shared_context=" + shared_context);
init(flags, 3, sharedContext);
}
public EglTask(final int maxClientVersion,
final EGLBase.IContext sharedContext, final int flags) {
// if (DEBUG) Log.i(TAG, "shared_context=" + shared_context);
init(flags, maxClientVersion, sharedContext);
}
/**
* @param flags
* @param maxClientVersion
* @param sharedContext
*/
@Override
protected void onInit(final int flags,
final int maxClientVersion, final Object sharedContext) {
if ((sharedContext == null)
|| (sharedContext instanceof EGLBase.IContext)) {
final int stencilBits
= (flags & EGL_FLAG_STENCIL_1BIT) == EGL_FLAG_STENCIL_1BIT ? 1
: ((flags & EGL_FLAG_STENCIL_8BIT) == EGL_FLAG_STENCIL_8BIT ? 8 : 0);
mEgl = EGLBase.createFrom(maxClientVersion, (EGLBase.IContext)sharedContext,
(flags & EGL_FLAG_DEPTH_BUFFER) == EGL_FLAG_DEPTH_BUFFER,
stencilBits,
(flags & EGL_FLAG_RECORDABLE) == EGL_FLAG_RECORDABLE);
}
if (mEgl == null) {
callOnError(new RuntimeException("failed to create EglCore"));
releaseSelf();
} else {
mEglHolder = mEgl.createOffscreen(1, 1);
mEglHolder.makeCurrent();
}
}
@Override
protected Request takeRequest() throws InterruptedException {
final Request result = super.takeRequest();
mEglHolder.makeCurrent();
return result;
}
@Override
protected void onBeforeStop() {
mEglHolder.makeCurrent();
}
@Override
protected void onRelease() {
mEglHolder.release();
mEgl.release();
}
protected EGLBase getEgl() {
return mEgl;
}
protected EGLBase.IContext getEGLContext() {
return mEgl.getContext();
}
protected EGLBase.IConfig getConfig() {
return mEgl.getConfig();
}
@Nullable
protected EGLBase.IContext getContext() {
return mEgl != null ? mEgl.getContext() : null;
}
protected void makeCurrent() {
mEglHolder.makeCurrent();
}
protected boolean isGLES3() {
return (mEgl != null) && (mEgl.getGlVersion() > 2);
}
}

View File

@@ -0,0 +1,307 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import static com.serenegiant.glutils.ShaderConst.FRAGMENT_SHADER_SIMPLE;
import static com.serenegiant.glutils.ShaderConst.FRAGMENT_SHADER_SIMPLE_OES;
import static com.serenegiant.glutils.ShaderConst.GL_TEXTURE_2D;
import static com.serenegiant.glutils.ShaderConst.GL_TEXTURE_EXTERNAL_OES;
import static com.serenegiant.glutils.ShaderConst.VERTEX_SHADER;
import android.opengl.GLES20;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
/**
* 描画領域全面にテクスチャを2D描画するためのヘルパークラス
*/
public class GLDrawer2D implements IDrawer2dES2 {
// private static final boolean DEBUG = false; // FIXME set false on release
// private static final String TAG = "GLDrawer2D";
private static final float[] VERTICES = { 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f };
private static final float[] TEXCOORD = { 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f };
private static final int FLOAT_SZ = Float.SIZE / 8;
private final int VERTEX_NUM;
private final int VERTEX_SZ;
private final FloatBuffer pVertex;
private final FloatBuffer pTexCoord;
private final int mTexTarget;
private int hProgram;
int maPositionLoc;
int maTextureCoordLoc;
int muMVPMatrixLoc;
int muTexMatrixLoc;
private final float[] mMvpMatrix = new float[16];
/**
* コンストラクタ
* GLコンテキスト/EGLレンダリングコンテキストが有効な状態で呼ばないとダメ
* @param isOES 外部テクスチャ(GL_TEXTURE_EXTERNAL_OES)を使う場合はtrue。
* 通常の2Dテキスチャならfalse
*/
public GLDrawer2D(final boolean isOES) {
this(VERTICES, TEXCOORD, isOES);
}
/**
* コンストラクタ
* GLコンテキスト/EGLレンダリングコンテキストが有効な状態で呼ばないとダメ
* @param vertices 頂点座標, floatを8個 = (x,y) x 4ペア
* @param texcoord テクスチャ座標, floatを8個 = (s,t) x 4ペア
* @param isOES 外部テクスチャ(GL_TEXTURE_EXTERNAL_OES)を使う場合はtrue。
* 通常の2Dテキスチャならfalse
*/
public GLDrawer2D(final float[] vertices,
final float[] texcoord, final boolean isOES) {
VERTEX_NUM = Math.min(
vertices != null ? vertices.length : 0,
texcoord != null ? texcoord.length : 0) / 2;
VERTEX_SZ = VERTEX_NUM * 2;
mTexTarget = isOES ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
pVertex = ByteBuffer.allocateDirect(VERTEX_SZ * FLOAT_SZ)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
pVertex.put(vertices);
pVertex.flip();
pTexCoord = ByteBuffer.allocateDirect(VERTEX_SZ * FLOAT_SZ)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
pTexCoord.put(texcoord);
pTexCoord.flip();
if (isOES) {
hProgram = GLHelper.loadShader(VERTEX_SHADER, FRAGMENT_SHADER_SIMPLE_OES);
} else {
hProgram = GLHelper.loadShader(VERTEX_SHADER, FRAGMENT_SHADER_SIMPLE);
}
// モデルビュー変換行列を初期化
Matrix.setIdentityM(mMvpMatrix, 0);
init();
}
/**
* 破棄処理。GLコンテキスト/EGLレンダリングコンテキスト内で呼び出さないとダメ
*/
@Override
public void release() {
if (hProgram >= 0) {
GLES20.glDeleteProgram(hProgram);
}
hProgram = -1;
}
/**
* 外部テクスチャを使うかどうか
* @return
*/
public boolean isOES() {
return mTexTarget == GL_TEXTURE_EXTERNAL_OES;
}
/**
* モデルビュー変換行列を取得(内部配列を直接返すので変更時は要注意)
* @return
*/
@Override
public float[] getMvpMatrix() {
return mMvpMatrix;
}
/**
* モデルビュー変換行列に行列を割り当てる
* @param matrix 領域チェックしていないのでoffsetから16個以上必須
* @param offset
* @return
*/
@Override
public IDrawer2D setMvpMatrix(final float[] matrix, final int offset) {
System.arraycopy(matrix, offset, mMvpMatrix, 0, 16);
return this;
}
/**
* モデルビュー変換行列のコピーを取得
* @param matrix 領域チェックしていないのでoffsetから16個以上必須
* @param offset
*/
@Override
public void getMvpMatrix(final float[] matrix, final int offset) {
System.arraycopy(mMvpMatrix, 0, matrix, offset, 16);
}
/**
* 指定したテクスチャを指定したテクスチャ変換行列を使って描画領域全面に描画するためのヘルパーメソッド
* このクラスインスタンスのモデルビュー変換行列が設定されていればそれも適用された状態で描画する
* @param texId texture ID
* @param tex_matrix テクスチャ変換行列、nullならば以前に適用したものが再利用される。
* 領域チェックしていないのでoffsetから16個以上確保しておくこと
*/
@Override
public synchronized void draw(final int texId,
final float[] tex_matrix, final int offset) {
// if (DEBUG) Log.v(TAG, "draw");
if (hProgram < 0) return;
GLES20.glUseProgram(hProgram);
if (tex_matrix != null) {
// テクスチャ変換行列が指定されている時
GLES20.glUniformMatrix4fv(muTexMatrixLoc, 1, false, tex_matrix, offset);
}
// モデルビュー変換行列をセット
GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mMvpMatrix, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(mTexTarget, texId);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, VERTEX_NUM);
GLES20.glBindTexture(mTexTarget, 0);
GLES20.glUseProgram(0);
}
/**
* Textureオブジェクトを描画するためのヘルパーメソッド
* Textureオブジェクトで管理しているテクスチャ名とテクスチャ座標変換行列を使って描画する
* @param texture
*/
@Override
public void draw(final ITexture texture) {
draw(texture.getTexture(), texture.getTexMatrix(), 0);
}
/**
* TextureOffscreenオブジェクトを描画するためのヘルパーメソッド
* @param offscreen
*/
@Override
public void draw(final TextureOffscreen offscreen) {
draw(offscreen.getTexture(), offscreen.getTexMatrix(), 0);
}
/**
* テクスチャ名生成のヘルパーメソッド
* GLHelper#initTexを呼び出すだけ
* @return texture ID
*/
public int initTex() {
return GLHelper.initTex(mTexTarget, GLES20.GL_NEAREST);
}
/**
* テクスチャ名破棄のヘルパーメソッド
* GLHelper.deleteTexを呼び出すだけ
* @param hTex
*/
public void deleteTex(final int hTex) {
GLHelper.deleteTex(hTex);
}
/**
* 頂点シェーダー・フラグメントシェーダーを変更する
* GLコンテキスト/EGLレンダリングコンテキスト内で呼び出さないとダメ
* glUseProgramが呼ばれた状態で返る
* @param vs 頂点シェーダー文字列
* @param fs フラグメントシェーダー文字列
*/
public synchronized void updateShader(final String vs, final String fs) {
release();
hProgram = GLHelper.loadShader(vs, fs);
init();
}
/**
* フラグメントシェーダーを変更する
* GLコンテキスト/EGLレンダリングコンテキスト内で呼び出さないとダメ
* glUseProgramが呼ばれた状態で返る
* @param fs フラグメントシェーダー文字列
*/
public void updateShader(final String fs) {
updateShader(VERTEX_SHADER, fs);
}
/**
* 頂点シェーダー・フラグメントシェーダーをデフォルトに戻す
*/
public void resetShader() {
release();
if (isOES()) {
hProgram = GLHelper.loadShader(VERTEX_SHADER, FRAGMENT_SHADER_SIMPLE_OES);
} else {
hProgram = GLHelper.loadShader(VERTEX_SHADER, FRAGMENT_SHADER_SIMPLE);
}
init();
}
/**
* アトリビュート変数のロケーションを取得
* glUseProgramが呼ばれた状態で返る
* @param name
* @return
*/
@Override
public int glGetAttribLocation(final String name) {
GLES20.glUseProgram(hProgram);
return GLES20.glGetAttribLocation(hProgram, name);
}
/**
* ユニフォーム変数のロケーションを取得
* glUseProgramが呼ばれた状態で返る
* @param name
* @return
*/
@Override
public int glGetUniformLocation(final String name) {
GLES20.glUseProgram(hProgram);
return GLES20.glGetUniformLocation(hProgram, name);
}
/**
* glUseProgramが呼ばれた状態で返る
*/
@Override
public void glUseProgram() {
GLES20.glUseProgram(hProgram);
}
/**
* シェーダープログラム変更時の初期化処理
* glUseProgramが呼ばれた状態で返る
*/
private void init() {
GLES20.glUseProgram(hProgram);
maPositionLoc = GLES20.glGetAttribLocation(hProgram, "aPosition");
maTextureCoordLoc = GLES20.glGetAttribLocation(hProgram, "aTextureCoord");
muMVPMatrixLoc = GLES20.glGetUniformLocation(hProgram, "uMVPMatrix");
muTexMatrixLoc = GLES20.glGetUniformLocation(hProgram, "uTexMatrix");
//
GLES20.glUniformMatrix4fv(muMVPMatrixLoc,
1, false, mMvpMatrix, 0);
GLES20.glUniformMatrix4fv(muTexMatrixLoc,
1, false, mMvpMatrix, 0);
GLES20.glVertexAttribPointer(maPositionLoc,
2, GLES20.GL_FLOAT, false, VERTEX_SZ, pVertex);
GLES20.glVertexAttribPointer(maTextureCoordLoc,
2, GLES20.GL_FLOAT, false, VERTEX_SZ, pTexCoord);
GLES20.glEnableVertexAttribArray(maPositionLoc);
GLES20.glEnableVertexAttribArray(maTextureCoordLoc);
}
}

View File

@@ -0,0 +1,417 @@
package com.serenegiant.glutils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.opengl.GLES20;
import android.opengl.GLES30;
import android.opengl.GLUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import com.serenegiant.utils.AssetsHelper;
import com.serenegiant.utils.BuildCheck;
import java.io.IOException;
/**
* OpenGL|ES2/3用のヘルパークラス
*/
public final class GLHelper {
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
private static final String TAG = "GLHelper";
/**
* OpenGL|ESのエラーをチェックしてlogCatに出力する
* @param op
*/
public static void checkGlError(final String op) {
final int error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
final String msg = op + ": glError 0x" + Integer.toHexString(error);
Log.e(TAG, msg);
new Throwable(msg).printStackTrace();
// if (DEBUG) {
// throw new RuntimeException(msg);
// }
}
}
/**
* テクスチャ名を生成, テクスチャユニットはGL_TEXTURE0, クランプ方法はGL_CLAMP_TO_EDGE
* @param texTarget
* @param filter_param テクスチャの補完方法を指定, min/mag共に同じ値になる, GL_LINEARとかGL_NEAREST
* @return
*/
public static int initTex(final int texTarget, final int filter_param) {
return initTex(texTarget, GLES20.GL_TEXTURE0,
filter_param, filter_param, GLES20.GL_CLAMP_TO_EDGE);
}
/**
* テクスチャ名を生成(GL_TEXTURE0のみ)
* @param texTarget
* @param texUnit テクスチャユニット, GL_TEXTURE0...GL_TEXTURE31
* @param min_filter テクスチャの補間方法を指定, GL_LINEARとかGL_NEAREST
* @param mag_filter テクスチャの補間方法を指定, GL_LINEARとかGL_NEAREST
* @param wrap テクスチャのクランプ方法, GL_CLAMP_TO_EDGE
* @return
*/
public static int initTex(final int texTarget, final int texUnit,
final int min_filter, final int mag_filter, final int wrap) {
// if (DEBUG) Log.v(TAG, "initTex:target=" + texTarget);
final int[] tex = new int[1];
GLES20.glActiveTexture(texUnit);
GLES20.glGenTextures(1, tex, 0);
GLES20.glBindTexture(texTarget, tex[0]);
GLES20.glTexParameteri(texTarget, GLES20.GL_TEXTURE_WRAP_S, wrap);
GLES20.glTexParameteri(texTarget, GLES20.GL_TEXTURE_WRAP_T, wrap);
GLES20.glTexParameteri(texTarget, GLES20.GL_TEXTURE_MIN_FILTER, min_filter);
GLES20.glTexParameteri(texTarget, GLES20.GL_TEXTURE_MAG_FILTER, mag_filter);
return tex[0];
}
/**
* テクスチャ名配列を生成(前から順にGL_TEXTURE0, GL_TEXTURE1, ...)
* @param n 生成するテキスチャ名の数, 最大で32個(GL_MAX_TEXTURE_IMAGE_UNITS以下)
* @param texTarget
* @param filter_param
* @return
*/
public static int[] initTexes(final int n,
final int texTarget, final int filter_param) {
return initTexes(new int[n], texTarget,
filter_param, filter_param, GLES20.GL_CLAMP_TO_EDGE);
}
/**
* テクスチャ名配列を生成(前から順にGL_TEXTURE0, GL_TEXTURE1, ...)
* @param texIds テクスチャ名配列, 最大で32個(GL_MAX_TEXTURE_IMAGE_UNITS以下)
* @param texTarget
* @param filter_param
* @return
*/
public static int[] initTexes(@NonNull final int[] texIds,
final int texTarget, final int filter_param) {
return initTexes(texIds, texTarget,
filter_param, filter_param, GLES20.GL_CLAMP_TO_EDGE);
}
/**
* テクスチャ名配列を生成(前から順にGL_TEXTURE0, GL_TEXTURE1, ...)
* @param n 生成するテキスチャ名の数, 最大32
* @param texTarget
* @param min_filter
* @param mag_filter
* @param wrap
* @return
*/
public static int[] initTexes(final int n,
final int texTarget, final int min_filter, final int mag_filter, final int wrap) {
return initTexes(new int[n], texTarget, min_filter, mag_filter, wrap);
}
/**
* テクスチャ名配列を生成(前から順にGL_TEXTURE0, GL_TEXTURE1, ...)
* @param texIds テクスチャ名配列, 最大で32個(GL_MAX_TEXTURE_IMAGE_UNITS以下)
* @param texTarget
* @param min_filter
* @param mag_filter
* @param wrap
* @return
*/
public static int[] initTexes(@NonNull final int[] texIds,
final int texTarget, final int min_filter, final int mag_filter, final int wrap) {
int[] textureUnits = new int[1];
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_IMAGE_UNITS, textureUnits, 0);
Log.v(TAG, "GL_MAX_TEXTURE_IMAGE_UNITS=" + textureUnits[0]);
final int n = texIds.length > textureUnits[0]
? textureUnits[0] : texIds.length;
for (int i = 0; i < n; i++) {
texIds[i] = GLHelper.initTex(texTarget, ShaderConst.TEX_NUMBERS[i],
min_filter, mag_filter, wrap);
}
return texIds;
}
/**
* テクスチャ名配列を生成(こっちは全部同じテクスチャユニット)
* @param n 最大で32個(GL_MAX_TEXTURE_IMAGE_UNITS以下)
* @param texTarget
* @param texUnit
* @param min_filter
* @param mag_filter
* @param wrap
* @return
*/
public static int[] initTexes(final int n,
final int texTarget, final int texUnit,
final int min_filter, final int mag_filter, final int wrap) {
return initTexes(new int[n], texTarget, texUnit,
min_filter, mag_filter, wrap);
}
/**
* テクスチャ名配列を生成(こっちは全部同じテクスチャユニット)
* @param texIds 最大で32個(GL_MAX_TEXTURE_IMAGE_UNITS以下)
* @param texTarget
* @param texUnit
* @param filter_param
* @return
*/
public static int[] initTexes(@NonNull final int[] texIds,
final int texTarget, final int texUnit, final int filter_param) {
return initTexes(texIds, texTarget, texUnit,
filter_param, filter_param, GLES20.GL_CLAMP_TO_EDGE);
}
/**
* テクスチャ名配列を生成(こっちは全部同じテクスチャユニット)
* @param texIds
* @param texTarget
* @param texUnit
* @param min_filter
* @param mag_filter
* @param wrap
* @return
*/
public static int[] initTexes(@NonNull final int[] texIds,
final int texTarget, final int texUnit,
final int min_filter, final int mag_filter, final int wrap) {
int[] textureUnits = new int[1];
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_IMAGE_UNITS, textureUnits, 0);
final int n = texIds.length > textureUnits[0]
? textureUnits[0] : texIds.length;
for (int i = 0; i < n; i++) {
texIds[i] = GLHelper.initTex(texTarget, texUnit,
min_filter, mag_filter, wrap);
}
return texIds;
}
/**
* delete specific texture
*/
public static void deleteTex(final int hTex) {
// if (DEBUG) Log.v(TAG, "deleteTex:");
final int[] tex = new int[] {hTex};
GLES20.glDeleteTextures(1, tex, 0);
}
/**
* delete specific texture
*/
public static void deleteTex(@NonNull final int[] tex) {
// if (DEBUG) Log.v(TAG, "deleteTex:");
GLES20.glDeleteTextures(tex.length, tex, 0);
}
public static int loadTextureFromResource(final Context context, final int resId) {
return loadTextureFromResource(context, resId, null);
}
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
public static int loadTextureFromResource(final Context context, final int resId, final Resources.Theme theme) {
// Create an empty, mutable bitmap
final Bitmap bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888);
// get a canvas to paint over the bitmap
final Canvas canvas = new Canvas(bitmap);
canvas.drawARGB(0,0,255,0);
// get a background image from resources
// note the image format must match the bitmap format
final Drawable background;
if (BuildCheck.isAndroid5()) {
background = context.getResources().getDrawable(resId, theme);
} else {
background = context.getResources().getDrawable(resId);
}
background.setBounds(0, 0, 256, 256);
background.draw(canvas); // draw the background to our bitmap
final int[] textures = new int[1];
//Generate one texture pointer...
GLES20.glGenTextures(1, textures, 0);
//...and bind it to our array
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
//Create Nearest Filtered Texture
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
//Different possible texture parameters, e.g. GLES20.GL_CLAMP_TO_EDGE
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
//Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
//Clean up
bitmap.recycle();
return textures[0];
}
public static int createTextureWithTextContent (final String text) {
// Create an empty, mutable bitmap
final Bitmap bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888);
// get a canvas to paint over the bitmap
final Canvas canvas = new Canvas(bitmap);
canvas.drawARGB(0,0,255,0);
// Draw the text
final Paint textPaint = new Paint();
textPaint.setTextSize(32);
textPaint.setAntiAlias(true);
textPaint.setARGB(0xff, 0xff, 0xff, 0xff);
// draw the text centered
canvas.drawText(text, 16, 112, textPaint);
final int texture = initTex(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE0, GLES20.GL_NEAREST, GLES20.GL_LINEAR, GLES20.GL_REPEAT);
// Alpha blending
// GLES20.glEnable(GLES20.GL_BLEND);
// GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
// Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Clean up
bitmap.recycle();
return texture;
}
/**
* load, compile and link shader from Assets files
* @param context
* @param vss_asset source file name in Assets of vertex shader
* @param fss_asset source file name in Assets of fragment shader
* @return
*/
public static int loadShader(@NonNull final Context context,
final String vss_asset, final String fss_asset) {
int program = 0;
try {
final String vss = AssetsHelper.loadString(context.getAssets(), vss_asset);
final String fss = AssetsHelper.loadString(context.getAssets(), vss_asset);
program = loadShader(vss, fss);
} catch (IOException e) {
}
return program;
}
/**
* load, compile and link shader
* @param vss source of vertex shader
* @param fss source of fragment shader
* @return
*/
public static int loadShader(final String vss, final String fss) {
// if (DEBUG) Log.v(TAG, "loadShader:");
final int[] compiled = new int[1];
// 頂点シェーダーをコンパイル
final int vs = loadShader(GLES20.GL_VERTEX_SHADER, vss);
if (vs == 0) {
return 0;
}
// フラグメントシェーダーをコンパイル
int fs = loadShader(GLES20.GL_FRAGMENT_SHADER, fss);
if (fs == 0) {
return 0;
}
// リンク
final int program = GLES20.glCreateProgram();
checkGlError("glCreateProgram");
if (program == 0) {
Log.e(TAG, "Could not create program");
}
GLES20.glAttachShader(program, vs);
checkGlError("glAttachShader");
GLES20.glAttachShader(program, fs);
checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
final int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e(TAG, "Could not link program: ");
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
return 0;
}
return program;
}
/**
* Compiles the provided shader source.
*
* @return A handle to the shader, or 0 on failure.
*/
public static int loadShader(final int shaderType, final String source) {
int shader = GLES20.glCreateShader(shaderType);
checkGlError("glCreateShader type=" + shaderType);
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
final int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "Could not compile shader " + shaderType + ":");
Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
return shader;
}
/**
* Checks to see if the location we obtained is valid. GLES returns -1 if a label
* could not be found, but does not set the GL error.
* <p>
* Throws a RuntimeException if the location is invalid.
*/
public static void checkLocation(final int location, final String label) {
if (location < 0) {
throw new RuntimeException("Unable to locate '" + label + "' in program");
}
}
/**
* Writes GL version info to the log.
*/
@SuppressLint("InlinedApi")
public static void logVersionInfo() {
Log.i(TAG, "vendor : " + GLES20.glGetString(GLES20.GL_VENDOR));
Log.i(TAG, "renderer: " + GLES20.glGetString(GLES20.GL_RENDERER));
Log.i(TAG, "version : " + GLES20.glGetString(GLES20.GL_VERSION));
if (BuildCheck.isAndroid4_3()) {
final int[] values = new int[1];
GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, values, 0);
final int majorVersion = values[0];
GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, values, 0);
final int minorVersion = values[0];
if (GLES30.glGetError() == GLES30.GL_NO_ERROR) {
Log.i(TAG, "version: " + majorVersion + "." + minorVersion);
}
}
}
}

View File

@@ -0,0 +1,66 @@
package com.serenegiant.glutils;
/**
* Created by saki on 2018/02/10.
* 共有コンテキストのマスターをを保持するためだけのクラス
* Applicationクラス等でシングルトンとして使う
*/
public class GLMasterContext {
private static final String TAG = GLMasterContext.class.getSimpleName();
private MasterTask mMasterTask;
public GLMasterContext(final int maxClientVersion, final int flags) {
mMasterTask = new MasterTask(maxClientVersion, flags);
new Thread(mMasterTask, TAG).start();
mMasterTask.waitReady();
}
@Override
protected void finalize() throws Throwable {
try {
release();
} finally {
super.finalize();
}
}
public synchronized void release() {
if (mMasterTask != null) {
mMasterTask.release();
mMasterTask = null;
}
}
public synchronized EGLBase.IContext getContext()
throws IllegalStateException {
if (mMasterTask != null) {
return mMasterTask.getContext();
} else {
throw new IllegalStateException("already released");
}
}
private static class MasterTask extends EglTask {
public MasterTask(final int maxClientVersion, final int flags) {
super(maxClientVersion, null, flags);
}
@Override
protected void onStart() {
// do nothing
}
@Override
protected void onStop() {
// do nothing
}
@Override
protected Object processRequest(final int request,
final int arg1, final int arg2, final Object obj) throws TaskBreak {
// do nothing
return null;
}
}
}

View File

@@ -0,0 +1,234 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.text.TextUtils;
import java.io.IOException;
/**
* OpenGL|ESのテクスチャ操作用のヘルパークラス
*/
public class GLTexture implements ITexture {
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
// private static final String TAG = "GLTexture";
/* package */final int mTextureTarget;
/* package */final int mTextureUnit ;
/* package */int mTextureId;
/* package */final float[] mTexMatrix = new float[16]; // テクスチャ変換行列
/* package */int mTexWidth, mTexHeight;
/* package */int mImageWidth, mImageHeight;
/**
* コンストラクタ
* テクスチャユニットが常時GL_TEXTURE0なので複数のテクスチャを同時に使えない
* @param width テクスチャサイズ
* @param height テクスチャサイズ
* @param filter_param テクスチャの補間方法を指定 GL_LINEARとかGL_NEAREST
*/
public GLTexture(final int width, final int height, final int filter_param) {
this(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE0, width, height, filter_param);
}
/**
* コンストラクタ
* @param texTarget GL_TEXTURE_EXTERNAL_OESはだめ
* @param texUnit
* @param width テクスチャサイズ
* @param height テクスチャサイズ
* @param filter_param テクスチャの補間方法を指定 GL_LINEARとかGL_NEAREST
*/
public GLTexture(final int texTarget, final int texUnit,
final int width, final int height, final int filter_param) {
// if (DEBUG) Log.v(TAG, String.format("コンストラクタ(%d,%d)", width, height));
mTextureTarget = texTarget;
mTextureUnit = texUnit;
// テクスチャに使うビットマップは縦横サイズが2の乗数でないとダメ。
// 更に、ミップマップするなら正方形でないとダメ
// 指定したwidth/heightと同じか大きい2の乗数にする
int w = 32;
for (; w < width; w <<= 1);
int h = 32;
for (; h < height; h <<= 1);
if (mTexWidth != w || mTexHeight != h) {
mTexWidth = w;
mTexHeight = h;
}
// if (DEBUG) Log.v(TAG, String.format("texSize(%d,%d)", mTexWidth, mTexHeight));
mTextureId = GLHelper.initTex(mTextureTarget, filter_param);
// テクスチャのメモリ領域を確保する
GLES20.glTexImage2D(mTextureTarget,
0, // ミップマップレベル0(ミップマップしない)
GLES20.GL_RGBA, // 内部フォーマット
mTexWidth, mTexHeight, // サイズ
0, // 境界幅
GLES20.GL_RGBA, // 引き渡すデータのフォーマット
GLES20.GL_UNSIGNED_BYTE, // データの型
null); // ピクセルデータ無し
// テクスチャ変換行列を初期化
Matrix.setIdentityM(mTexMatrix, 0);
mTexMatrix[0] = width / (float)mTexWidth;
mTexMatrix[5] = height / (float)mTexHeight;
// if (DEBUG) Log.v(TAG, "GLTexture:id=" + mTextureId);
}
@Override
protected void finalize() throws Throwable {
try {
release(); // GLコンテキスト内じゃない可能性があるのであまり良くないけど
} finally {
super.finalize();
}
}
/**
* テクスチャを破棄
* GLコンテキスト/EGLレンダリングコンテキスト内で呼び出すこと
*/
@Override
public void release() {
// if (DEBUG) Log.v(TAG, "release:");
if (mTextureId > 0) {
GLHelper.deleteTex(mTextureId);
mTextureId = 0;
}
}
/**
* このインスタンスで管理しているテクスチャを有効にする(バインドする)
*/
@Override
public void bind() {
// if (DEBUG) Log.v(TAG, "bind:");
GLES20.glActiveTexture(mTextureUnit); // テクスチャユニットを選択
GLES20.glBindTexture(mTextureTarget, mTextureId);
}
/**
* このインスタンスで管理しているテクスチャを無効にする(アンバインドする)
*/
@Override
public void unbind() {
// if (DEBUG) Log.v(TAG, "unbind:");
GLES20.glActiveTexture(mTextureUnit); // テクスチャユニットを選択
GLES20.glBindTexture(mTextureTarget, 0);
}
/**
* テクスチャターゲットを取得(GL_TEXTURE_2D)
* @return
*/
@Override
public int getTexTarget() { return mTextureTarget; }
/**
* テクスチャ名を取得
* @return
*/
@Override
public int getTexture() { return mTextureId; }
/**
* テクスチャ座標変換行列を取得(内部配列をそのまま返すので変更時は要注意)
* @return
*/
@Override
public float[] getTexMatrix() { return mTexMatrix; }
/**
* テクスチャ座標変換行列のコピーを取得
* @param matrix 領域チェックしていないのでoffset位置から16個以上確保しておくこと
* @param offset
*/
@Override
public void getTexMatrix(final float[] matrix, final int offset) {
System.arraycopy(mTexMatrix, 0, matrix, offset, mTexMatrix.length);
}
/**
* テクスチャ幅を取得
* @return
*/
@Override
public int getTexWidth() { return mTexWidth; }
/**
* テクスチャ高さを取得
* @return
*/
@Override
public int getTexHeight() { return mTexHeight; }
/**
* 指定したファイルから画像をテクスチャに読み込む
* ファイルが存在しないか読み込めなければIOException/NullPointerExceptionを生成
* @param filePath
*/
@Override
public void loadTexture(final String filePath) throws NullPointerException, IOException {
// if (DEBUG) Log.v(TAG, "loadTexture:path=" + filePath);
if (TextUtils.isEmpty(filePath))
throw new NullPointerException("image file path should not be a null");
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // Bitmapを生成せずにサイズ等の情報だけを取得する
BitmapFactory.decodeFile(filePath, options);
// テキスチャサイズ内に指定したイメージが収まるためのサブサンプリングを値を求める
final int imageWidth = options.outWidth;
final int imageHeight = options.outHeight;
int inSampleSize = 1; // サブサンプリングサイズ
if ((imageHeight > mTexHeight) || (imageWidth > mTexWidth)) {
if (imageWidth > imageHeight) {
inSampleSize = (int)Math.ceil(imageHeight / (float)mTexHeight);
} else {
inSampleSize = (int)Math.ceil(imageWidth / (float)mTexWidth);
}
}
// if (DEBUG) Log.v(TAG, String.format("image(%d,%d),tex(%d,%d),inSampleSize=%d",
// imageWidth, imageHeight, mTexWidth, mTexHeight, inSampleSize));
// 実際の読み込み処理
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
loadTexture(BitmapFactory.decodeFile(filePath, options));
}
/**
* 指定したビットマップをテクスチャに読み込む
* @param bitmap
*/
public void loadTexture(final Bitmap bitmap) throws NullPointerException {
mImageWidth = bitmap.getWidth(); // 読み込んだイメージのサイズを取得
mImageHeight = bitmap.getHeight();
Bitmap texture = Bitmap.createBitmap(mTexWidth, mTexHeight, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(texture);
canvas.drawBitmap(bitmap, 0, 0, null);
bitmap.recycle();
// テクスチャ座標変換行列を設定(読み込んだイメージサイズがテクスチャサイズにフィットするようにスケール変換)
Matrix.setIdentityM(mTexMatrix, 0);
mTexMatrix[0] = mImageWidth / (float)mTexWidth;
mTexMatrix[5] = mImageHeight / (float)mTexHeight;
// if (DEBUG) Log.v(TAG, String.format("image(%d,%d),scale(%f,%f)",
// mImageWidth, mImageHeight, mMvpMatrix[0], mMvpMatrix[5]));
bind();
GLUtils.texImage2D(mTextureTarget, 0, texture, 0);
unbind();
texture.recycle();
}
}

View File

@@ -0,0 +1,29 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
public interface IDrawer2D {
public void release();
public float[] getMvpMatrix();
public IDrawer2D setMvpMatrix(final float[] matrix, final int offset);
public void getMvpMatrix(final float[] matrix, final int offset);
public void draw(final int texId, final float[] tex_matrix, final int offset);
public void draw(final ITexture texture);
public void draw(final TextureOffscreen offscreen);
}

View File

@@ -0,0 +1,25 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
public interface IDrawer2dES2 extends IDrawer2D {
public int glGetAttribLocation(final String name);
public int glGetUniformLocation(final String name);
public void glUseProgram();
}

View File

@@ -0,0 +1,55 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.graphics.SurfaceTexture;
import android.view.Surface;
public interface IRenderer extends IRendererCommon {
/**
* 関係するすべてのリソースを開放する。再利用できない
*/
public void release();
/**
* 描画先のSurfaceをセット
* @param surface
*/
public void setSurface(final Surface surface);
/**
* 描画先のSurfaceをセット
* @param surface
*/
public void setSurface(final SurfaceTexture surface);
/**
* Surfaceサイズを変更
* @param width
* @param height
*/
public void resize(final int width, final int height);
/**
* 描画要求
* @param args
*/
public void requestRender(final Object... args);
}

View File

@@ -0,0 +1,48 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public interface IRendererCommon {
public static final int MIRROR_NORMAL = 0;
public static final int MIRROR_HORIZONTAL = 1;
public static final int MIRROR_VERTICAL = 2;
public static final int MIRROR_BOTH = 3;
public static final int MIRROR_NUM = 4;
@IntDef({MIRROR_NORMAL, MIRROR_HORIZONTAL, MIRROR_VERTICAL, MIRROR_BOTH})
@Retention(RetentionPolicy.SOURCE)
public @interface MirrorMode {}
/**
* 映像を上下左右反転させるかどうかをセット
* @param mirror 0:通常, 1:左右反転, 2:上下反転, 3:上下左右反転
*/
public void setMirror(@MirrorMode final int mirror);
/**
* 映像を上下左右反転させるかどうかを取得
* @return 0:通常, 1:左右反転, 2:上下反転, 3:上下左右反転
*/
public @MirrorMode int getMirror();
}

View File

@@ -0,0 +1,223 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.graphics.SurfaceTexture;
import android.view.Surface;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 分配描画インターフェース
*/
public interface IRendererHolder extends IRendererCommon {
public static final int DEFAULT_CAPTURE_COMPRESSION = 80;
public static final int OUTPUT_FORMAT_JPEG = 0; // Bitmap.CompressFormat.JPEG
public static final int OUTPUT_FORMAT_PNG = 1; // Bitmap.CompressFormat.PNG
public static final int OUTPUT_FORMAT_WEBP = 2; // Bitmap.CompressFormat.WEBP
@IntDef({OUTPUT_FORMAT_JPEG, OUTPUT_FORMAT_PNG, OUTPUT_FORMAT_WEBP})
@Retention(RetentionPolicy.SOURCE)
public @interface StillCaptureFormat {}
/**
* 実行中かどうか
* @return
*/
public boolean isRunning();
/**
* 関係するすべてのリソースを開放する。再利用できない
*/
public void release();
@Nullable
public EGLBase.IContext getContext();
/**
* マスター用の映像を受け取るためのSurfaceを取得
* @return
*/
public Surface getSurface();
/**
* マスター用の映像を受け取るためのSurfaceTextureを取得
* @return
*/
public SurfaceTexture getSurfaceTexture();
/**
* マスター用の映像を受け取るためのマスターをチェックして無効なら再生成要求する
*/
public void reset();
/**
* マスター映像サイズをサイズ変更要求
* @param width
* @param height
*/
public void resize(final int width, final int height)
throws IllegalStateException;
/**
* 分配描画用のSurfaceを追加
* このメソッドは指定したSurfaceが追加されるか
* interruptされるまでカレントスレッドをブロックする。
* @param id 普通は#hashCodeを使う
* @param surface, should be one of Surface, SurfaceTexture or SurfaceHolder
* @param isRecordable
*/
public void addSurface(final int id, final Object surface,
final boolean isRecordable)
throws IllegalStateException, IllegalArgumentException;
/**
* 分配描画用のSurfaceを追加
* このメソッドは指定したSurfaceが追加されるか
* interruptされるまでカレントスレッドをブロックする。
* @param id 普通は#hashCodeを使う
* @param surface, should be one of Surface, SurfaceTexture or SurfaceHolder
* @param isRecordable
* @param maxFps 0以下なら制限しない
*/
public void addSurface(final int id, final Object surface,
final boolean isRecordable, final int maxFps)
throws IllegalStateException, IllegalArgumentException;
/**
* 分配描画用のSurfaceを削除
* このメソッドは指定したSurfaceが削除されるか
* interruptされるまでカレントスレッドをブロックする。
* @param id
*/
public void removeSurface(final int id);
/**
* 分配描画用のSurfaceを全て削除
* このメソッドはSurfaceが削除されるか
* interruptされるまでカレントスレッドをブロックする。
*/
public void removeSurfaceAll();
/**
* 分配描画用のSurfaceを指定した色で塗りつぶす
* @param id
* @param color
*/
public void clearSurface(final int id, final int color);
/**
* 分配描画用のSurfaceを指定した色で塗りつぶす
* @param color
*/
public void clearSurfaceAll(final int color);
/**
* モデルビュー変換行列をセット
* @param id
* @param offset
* @param matrix offset以降に16要素以上
*/
public void setMvpMatrix(final int id,
final int offset, @NonNull final float[] matrix);
/**
* 分配描画用のSurfaceへの描画が有効かどうかを取得
* @param id
* @return
*/
public boolean isEnabled(final int id);
/**
* 分配描画用のSurfaceへの描画の有効・無効を切替
* @param id
* @param enable
*/
public void setEnabled(final int id, final boolean enable);
/**
* 強制的に現在の最新のフレームを描画要求する
* 分配描画用Surface全てが更新されるので注意
*/
public void requestFrame();
/**
* 追加されている分配描画用のSurfaceの数を取得
* @return
*/
public int getCount();
/**
* 静止画を撮影する
* 撮影完了を待機しない
* @param path
*/
@Deprecated
public void captureStillAsync(@NonNull final String path)
throws FileNotFoundException, IllegalStateException;
/**
* 静止画を撮影する
* 撮影完了を待機しない
* @param path
* @param captureCompression JPEGの圧縮率, pngの時は無視
*/
@Deprecated
public void captureStillAsync(@NonNull final String path,
@IntRange(from = 1L,to = 99L) final int captureCompression)
throws FileNotFoundException, IllegalStateException;
/**
* 静止画を撮影する
* 撮影完了を待機する
* @param path
*/
public void captureStill(@NonNull final String path)
throws FileNotFoundException, IllegalStateException;
/**
* 静止画を撮影する
* 撮影完了を待機する
* @param path
* @param captureCompression JPEGの圧縮率, pngの時は無視
*/
public void captureStill(@NonNull final String path,
@IntRange(from = 1L,to = 99L) final int captureCompression)
throws FileNotFoundException, IllegalStateException;
/**
* 静止画を撮影する
* 撮影完了を待機する
* @param out
* @param stillCaptureFormat
* @param captureCompression
*/
public void captureStill(@NonNull final OutputStream out,
@StillCaptureFormat final int stillCaptureFormat,
@IntRange(from = 1L,to = 99L) final int captureCompression)
throws IllegalStateException;
}

View File

@@ -0,0 +1,42 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.graphics.Bitmap;
import java.io.IOException;
public interface ITexture {
void release();
void bind();
void unbind();
int getTexTarget();
int getTexture();
float[] getTexMatrix();
void getTexMatrix(float[] matrix, int offset);
int getTexWidth();
int getTexHeight();
void loadTexture(String filePath) throws NullPointerException, IOException;
void loadTexture(Bitmap bitmap) throws NullPointerException;
}

View File

@@ -0,0 +1,266 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.graphics.SurfaceTexture;
import android.opengl.GLES20;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
/**
* Draw shared texture on specific whole Surface using OpenGL|ES
* this will deprecate soon because I don't use this now
*/
@Deprecated
public final class RenderHandler extends Handler {
// private static final boolean DEBUG = false; // FIXME set false on release
private static final String TAG = "RenderHandler";
private static final int MSG_RENDER_SET_GLCONTEXT = 1;
private static final int MSG_RENDER_DRAW = 2;
private static final int MSG_CHECK_VALID = 3;
private static final int MSG_RENDER_QUIT = 9;
private int mTexId = -1;
private final RenderThread mThread;
public static RenderHandler createHandler() {
// if (DEBUG) Log.v(TAG, "createHandler:");
return createHandler("RenderThread");
}
public static final RenderHandler createHandler(final String name) {
// if (DEBUG) Log.v(TAG, "createHandler:name=" + name);
final RenderThread thread = new RenderThread(name);
thread.start();
return thread.getHandler();
}
public final void setEglContext(final EGLBase.IContext sharedContext,
final int tex_id, final Object surface, final boolean isRecordable) {
// if (DEBUG) Log.i(TAG, "RenderHandler:setEglContext:");
if (!(surface instanceof Surface)
&& !(surface instanceof SurfaceTexture)
&& !(surface instanceof SurfaceHolder))
throw new RuntimeException("unsupported window type:" + surface);
mTexId = tex_id;
sendMessage(obtainMessage(MSG_RENDER_SET_GLCONTEXT,
isRecordable ? 1 : 0, 0, new ContextParams(sharedContext, surface)));
}
public final void draw() {
sendMessage(obtainMessage(MSG_RENDER_DRAW, mTexId, 0, null));
}
public final void draw(final int tex_id) {
sendMessage(obtainMessage(MSG_RENDER_DRAW, tex_id, 0, null));
}
public final void draw(final float[] tex_matrix) {
sendMessage(obtainMessage(MSG_RENDER_DRAW, mTexId, 0, tex_matrix));
}
public final void draw(final int tex_id, final float[] tex_matrix) {
sendMessage(obtainMessage(MSG_RENDER_DRAW, tex_id, 0, tex_matrix));
}
public boolean isValid() {
synchronized (mThread.mSync) {
sendEmptyMessage(MSG_CHECK_VALID);
try {
mThread.mSync.wait();
} catch (final InterruptedException e) {
}
return mThread.mSurface != null && mThread.mSurface.isValid();
}
}
public final void release() {
// if (DEBUG) Log.i(TAG, "release:");
removeMessages(MSG_RENDER_SET_GLCONTEXT);
removeMessages(MSG_RENDER_DRAW);
sendEmptyMessage(MSG_RENDER_QUIT);
}
@Override
public final void handleMessage(final Message msg) {
switch (msg.what) {
case MSG_RENDER_SET_GLCONTEXT:
final ContextParams params = (ContextParams)msg.obj;
mThread.handleSetEglContext(params.sharedContext, params.surface, msg.arg1 != 0);
break;
case MSG_RENDER_DRAW:
mThread.handleDraw(msg.arg1, (float[])msg.obj);
break;
case MSG_CHECK_VALID:
synchronized (mThread.mSync) {
mThread.mSync.notify();
}
break;
case MSG_RENDER_QUIT:
Looper.myLooper().quit();
break;
default:
super.handleMessage(msg);
}
}
//********************************************************************************
//********************************************************************************
private RenderHandler(final RenderThread thread) {
// if (DEBUG) Log.i(TAG, "RenderHandler:");
mThread = thread;
}
private static final class ContextParams {
final EGLBase.IContext sharedContext;
final Object surface;
public ContextParams(final EGLBase.IContext sharedContext, final Object surface) {
this.sharedContext = sharedContext;
this.surface = surface;
}
}
/**
* Thread to execute render methods
* You can also use HandlerThread insted of this and create Handler from its Looper.
*/
private static final class RenderThread extends Thread {
private static final String TAG_THREAD = "RenderThread";
private final Object mSync = new Object();
private RenderHandler mHandler;
private EGLBase mEgl;
private EGLBase.IEglSurface mTargetSurface;
private Surface mSurface;
private GLDrawer2D mDrawer;
public RenderThread(final String name) {
super(name);
}
public final RenderHandler getHandler() {
synchronized (mSync) {
// create rendering thread
try {
mSync.wait();
} catch (final InterruptedException e) {
}
}
return mHandler;
}
/**
* Set shared context and Surface
* @param shardContext
* @param surface
*/
public final void handleSetEglContext(final EGLBase.IContext shardContext,
final Object surface, final boolean isRecordable) {
// if (DEBUG) Log.i(TAG_THREAD, "setEglContext:");
release();
synchronized (mSync) {
mSurface = surface instanceof Surface ? (Surface)surface
: (surface instanceof SurfaceTexture
? new Surface((SurfaceTexture)surface) : null);
}
mEgl = EGLBase.createFrom(3, shardContext, false, 0, isRecordable);
try {
mTargetSurface = mEgl.createFromSurface(surface);
mDrawer = new GLDrawer2D(isRecordable);
} catch (final Exception e) {
Log.w(TAG, e);
if (mTargetSurface != null) {
mTargetSurface.release();
mTargetSurface = null;
}
if (mDrawer != null) {
mDrawer.release();
mDrawer = null;
}
}
}
/**
* drawing
* @param tex_id
* @param tex_matrix
*/
public void handleDraw(final int tex_id, final float[] tex_matrix) {
// if (DEBUG) Log.i(TAG_THREAD, "draw");
if (tex_id >= 0 && mTargetSurface != null) {
mTargetSurface.makeCurrent();
mDrawer.draw(tex_id, tex_matrix, 0);
mTargetSurface.swap();
}
}
@Override
public final void run() {
// if (DEBUG) Log.v(TAG_THREAD, "started");
Looper.prepare();
synchronized (mSync) {
mHandler = new RenderHandler(this);
mSync.notify();
}
Looper.loop();
// if (DEBUG) Log.v(TAG_THREAD, "finishing");
release();
synchronized (mSync) {
mHandler = null;
}
// if (DEBUG) Log.v(TAG_THREAD, "finished");
}
private final void release() {
// if (DEBUG) Log.v(TAG_THREAD, "release:");
if (mDrawer != null) {
mDrawer.release();
mDrawer = null;
}
synchronized (mSync) {
mSurface = null;
}
if (mTargetSurface != null) {
clear();
mTargetSurface.release();
mTargetSurface = null;
}
if (mEgl != null) {
mEgl.release();
mEgl = null;
}
}
/**
* Fill black on specific Surface
*/
private final void clear() {
// if (DEBUG) Log.v(TAG_THREAD, "clear:");
mTargetSurface.makeCurrent();
GLES20.glClearColor(0, 0, 0, 1);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
mTargetSurface.swap();
}
}
}

View File

@@ -0,0 +1,30 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import android.view.Surface;
/**
* RenderHolderのコールバックリスナー
*/
public interface RenderHolderCallback {
public void onCreate(Surface surface);
public void onFrameAvailable();
public void onDestroy();
}

View File

@@ -0,0 +1,79 @@
package com.serenegiant.glutils;
/*
* libcommon
* utility/helper classes for myself
*
* Copyright (c) 2014-2018 saki t_saki@serenegiant.com
*
* 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.
*/
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Hold shared texture that has camera frame and draw them to registered surface if needs<br>
*/
public class RendererHolder extends AbstractRendererHolder {
// private static final boolean DEBUG = false; // FIXME 実働時はfalseにすること
private static final String TAG = RendererHolder.class.getSimpleName();
public RendererHolder(final int width, final int height,
@Nullable final RenderHolderCallback callback) {
this(width, height,
3, null, EglTask.EGL_FLAG_RECORDABLE,
callback);
}
public RendererHolder(final int width, final int height,
final int maxClientVersion, final EGLBase.IContext sharedContext, final int flags,
@Nullable final RenderHolderCallback callback) {
super(width, height,
maxClientVersion, sharedContext, flags,
callback);
}
@NonNull
protected RendererTask createRendererTask(final int width, final int height,
final int maxClientVersion, final EGLBase.IContext sharedContext, final int flags) {
return new MyRendererTask(this, width, height,
maxClientVersion, sharedContext, flags);
}
//================================================================================
// 実装
//================================================================================
/**
* ワーカースレッド上でOpenGL|ESを用いてマスター映像を分配描画するためのインナークラス
*/
protected static final class MyRendererTask extends RendererTask {
public MyRendererTask(final RendererHolder parent,
final int width, final int height) {
super(parent, width, height);
}
public MyRendererTask(@NonNull final AbstractRendererHolder parent,
final int width, final int height,
final int maxClientVersion,
final EGLBase.IContext sharedContext, final int flags) {
super(parent, width, height, maxClientVersion, sharedContext, flags);
}
}
}

Some files were not shown because too many files have changed in this diff Show More