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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
21
ZD_README/README_Utils.md
Normal 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)
|
||||
|
||||
```
|
||||
|
||||
|
||||
182
app/build.gradle
182
app/build.gradle
@@ -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"))
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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\""
|
||||
// 构建的是否是演示(美化)模式
|
||||
|
||||
@@ -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\""
|
||||
// 构建的是否是演示(美化)模式
|
||||
|
||||
@@ -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\""
|
||||
|
||||
@@ -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\""
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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:基于成熟的工具类开源框架下沉的,这里可以增添针对我们业务上的一些工具类
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
1
core/function-impl/mogo-core-function-carcorder/.gitignore
vendored
Normal file
1
core/function-impl/mogo-core-function-carcorder/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
75
core/function-impl/mogo-core-function-carcorder/build.gradle
Normal file
75
core/function-impl/mogo-core-function-carcorder/build.gradle
Normal 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()
|
||||
@@ -0,0 +1,3 @@
|
||||
GROUP=com.mogo.eagle.core.function.impl
|
||||
POM_ARTIFACT_ID=carcorder
|
||||
VERSION_CODE=1
|
||||
21
core/function-impl/mogo-core-function-carcorder/proguard-rules.pro
vendored
Normal file
21
core/function-impl/mogo-core-function-carcorder/proguard-rules.pro
vendored
Normal 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
|
||||
@@ -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>
|
||||
@@ -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}")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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("日志抓取默认计时结束")
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -68,7 +68,6 @@ abstract class AbsLogView : ILogView, TouchProxy.OnTouchEventListener {
|
||||
|
||||
fun show(context: Context) {
|
||||
if (isShow) {
|
||||
Log.d("EmArrow", "isShow : $isShow")
|
||||
return
|
||||
}
|
||||
performCreate(context)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 升级模式(提示升级、静默升级)
|
||||
|
||||
@@ -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("升级失败,请联系运维人员")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
<!--域控制器(工控机)配置信息-->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 模块
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ""
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,12 @@ object HmiBuildConfig {
|
||||
@JvmField
|
||||
var isShowBadCaseView = true
|
||||
|
||||
/**
|
||||
* 是否展示工控机升级提示UI
|
||||
*/
|
||||
@JvmField
|
||||
var isShowUpgradeTipsView = true
|
||||
|
||||
/**
|
||||
* 是否展示转向灯ui
|
||||
*/
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>)
|
||||
|
||||
}
|
||||
@@ -193,6 +193,12 @@ interface IMoGoWaringProvider {
|
||||
*/
|
||||
fun registerBadCaseCallback(onShow:() -> View, onHide: (() -> Unit)?)
|
||||
|
||||
/**
|
||||
*注册工控机升级提示圆点View的回调
|
||||
* @param 提示圆点View
|
||||
*/
|
||||
fun registerUpgradeTipsCallback(tipsView:() -> View)
|
||||
|
||||
/**
|
||||
* 工控机重启返回结果
|
||||
* @param code
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -273,6 +273,14 @@ object CallerHmiManager : CallerBase() {
|
||||
waringProviderApi?.registerBadCaseCallback(onShow, onHide)
|
||||
}
|
||||
|
||||
/**
|
||||
*注册工控机升级提示圆点View的回调
|
||||
* @param 提示圆点View
|
||||
*/
|
||||
fun registerUpgradeTipsCallback(tipsView:() -> View){
|
||||
waringProviderApi?.registerUpgradeTipsCallback(tipsView)
|
||||
}
|
||||
|
||||
/**
|
||||
* 工控机重启返回结果
|
||||
* @param code
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 = {
|
||||
"水瓶座", "双鱼座", "白羊座", "金牛座", "双子座", "巨蟹座",
|
||||
"狮子座", "处女座", "天秤座", "天蝎座", "射手座", "摩羯座"
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
################# 旧版本架构模块版本 #################
|
||||
|
||||
@@ -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
1
libraries/map-usbcamera/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
73
libraries/map-usbcamera/build.gradle
Normal file
73
libraries/map-usbcamera/build.gradle
Normal 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()
|
||||
0
libraries/map-usbcamera/consumer-rules.pro
Normal file
0
libraries/map-usbcamera/consumer-rules.pro
Normal file
3
libraries/map-usbcamera/gradle.properties
Normal file
3
libraries/map-usbcamera/gradle.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
GROUP=com.mogo.camera
|
||||
POM_ARTIFACT_ID=map-usbcamera
|
||||
VERSION_CODE=1
|
||||
BIN
libraries/map-usbcamera/libs/common-4.1.1.aar
Normal file
BIN
libraries/map-usbcamera/libs/common-4.1.1.aar
Normal file
Binary file not shown.
21
libraries/map-usbcamera/proguard-rules.pro
vendored
Normal file
21
libraries/map-usbcamera/proguard-rules.pro
vendored
Normal 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
|
||||
13
libraries/map-usbcamera/src/main/AndroidManifest.xml
Normal file
13
libraries/map-usbcamera/src/main/AndroidManifest.xml
Normal 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>
|
||||
BIN
libraries/map-usbcamera/src/main/assets/zk/SIMYOU.ttf
Normal file
BIN
libraries/map-usbcamera/src/main/assets/zk/SIMYOU.ttf
Normal file
Binary file not shown.
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user