desc:移除部分未使用代码

main
xiaowusky 2 years ago
parent 0b1f5b5dcf
commit 89f757fed7

@ -1 +0,0 @@
/build

@ -1,62 +0,0 @@
apply from: "${rootProject.rootDir}/buildCommon/commonLibConfig.gradle"
project.ext.setAppDefaultConfig project
android {
namespace 'com.yinuo.safetywatcher'
defaultConfig {
applicationId "com.yinuo.safetywatcher"
vectorDrawables {
useSupportLibrary true
}
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.3.2'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
var lifeCycle_version = '2.5.1'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifeCycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifeCycle_version"
implementation platform('androidx.compose:compose-bom:2022.10.00')
implementation 'androidx.activity:activity-compose:1.5.1'
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation 'androidx.navigation:navigation-runtime-ktx:2.5.2'
implementation "androidx.navigation:navigation-compose:2.4.0-rc01"
implementation 'io.reactivex.rxjava2:rxjava:2.1.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
var accompanist_version = '0.30.1'
implementation "com.google.accompanist:accompanist-permissions:$accompanist_version"
//excel
implementation rootProject.ext.dependencies.jxl
implementation project(path: ':library-push')
implementation project(path: ':library-ijkplayer')
// documentfile 访U
implementation "androidx.documentfile:documentfile:1.0.1"
implementation(name: 'libuvccamera-release', ext: 'aar') {
exclude module: 'support-v4'
exclude module: 'appcompat-v7'
}
}

Binary file not shown.

@ -1,21 +0,0 @@
# 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

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.usb.host"
android:required="true" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Easypusher">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Easypusher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

@ -1,13 +0,0 @@
package com.yinuo.safetywatcher
import android.app.Application
import com.lztek.toolkit.Lztek
import com.yinuo.safetywatcher.utils.LztekUtil
class App : Application() {
override fun onCreate() {
super.onCreate()
LztekUtil.setObject(Lztek.create(this))
}
}

@ -1,68 +0,0 @@
package com.yinuo.safetywatcher
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.rememberNavController
import com.yinuo.safetywatcher.navi.NavigationUtil
import com.yinuo.safetywatcher.navi.NavigationView
import com.yinuo.safetywatcher.ui.SplashView
import com.yinuo.safetywatcher.ui.theme.EasypusherTheme
import org.easydarwin.push.MediaStream
class MainActivity : ComponentActivity() {
var exitTime = 0L
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startMediaService()
setContent {
EasypusherTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val viewModel: MainViewModel = viewModel()
if (viewModel.isSplash) {
SplashView { viewModel.isSplash = false }
} else {
NavigationUtil.navHostController = rememberNavController()
NavigationView()
}
}
}
}
addBackListener()
}
private fun startMediaService() {
// 启动服务...
val intent = Intent(this, MediaStream::class.java)
startService(intent)
}
private fun addBackListener() {
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
//是主页
if (System.currentTimeMillis() - exitTime > 2000) {
Toast.makeText(this@MainActivity, "再点一次退出!", Toast.LENGTH_SHORT)
.show()
exitTime = System.currentTimeMillis()
} else {
finish()
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
}
}
})
}
}

@ -1,10 +0,0 @@
package com.yinuo.safetywatcher
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
class MainViewModel: ViewModel() {
var isSplash by mutableStateOf(true)
}

@ -1,11 +0,0 @@
package com.yinuo.safetywatcher.navi
/**
* @Description: todo
* @CreateDate: 2022/1/5 9:42
*/
sealed class ModelPath(val route: String) {
object Home : ModelPath("home")
object Setting : ModelPath("setting")
object Cloud : ModelPath("cloud")
}

@ -1,35 +0,0 @@
package com.yinuo.safetywatcher.navi
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.yinuo.safetywatcher.ui.cloud.CloudView
import com.yinuo.safetywatcher.ui.home.HomeView
import com.yinuo.safetywatcher.ui.setting.SettingView
/**
* @Description: todo
* @CreateDate: 2022/2/22 19:31
*/
@Composable
fun NavigationView() {
NavHost(navController = NavigationUtil.navHostController,
startDestination = ModelPath.Home.route) {
composable(ModelPath.Home.route) {
HomeView()
}
composable(ModelPath.Setting.route) {
SettingView(Modifier.fillMaxSize())
}
composable(ModelPath.Cloud.route){
CloudView()
}
}
}

@ -1,53 +0,0 @@
package com.yinuo.safetywatcher.navi
import android.annotation.SuppressLint
import android.os.Bundle
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.net.toUri
import androidx.navigation.*
/**
* @CreateDate: 2021/8/27 10:06
*/
object NavigationUtil {
@SuppressLint("StaticFieldLeak")
lateinit var navHostController: NavHostController
/**
* 跳转到某个页面
*/
fun to(screenName: ModelPath) {
navHostController.navigate(screenName.route)
}
/**
* 跳转到某个页面带参数
*/
fun toBundle(screenName: ModelPath, bundle: Bundle) {
navHostController.navigate(screenName.route, bundle)
}
private fun NavController.navigate(
route: String,
args: Bundle,
navOptions: NavOptions? = null,
navigatorExtras: Navigator.Extras? = null
) {
val routeLink = NavDeepLinkRequest.Builder.Companion.fromUri(
NavDestination.Companion.createRoute(route).toUri())
.build()
val deepLinkMatch = graph.matchDeepLink(routeLink)
if (deepLinkMatch != null) {
val destination = deepLinkMatch.destination
val id = destination.id
navigate(id, args, navOptions, navigatorExtras)
} else {
navigate(route, navOptions, navigatorExtras)
}
}
}

@ -1,55 +0,0 @@
package com.yinuo.safetywatcher.ui
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import kotlinx.coroutines.delay
/**
* @Description: todo
* @Author: yshh
* @CreateDate: 2022/2/22 14:19
*/
@Composable
fun SplashView(startMain: () -> Unit) {
var enabled by remember { mutableStateOf(false) }
val bgColor: Color by animateColorAsState(
if (enabled) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.primary.copy(alpha = 0.3f),
animationSpec = tween(durationMillis = 2000)
)
val textColor: Color by animateColorAsState(
if (enabled) Color.White
else Color.White.copy(alpha = 0.3f),
animationSpec = tween(durationMillis = 2000)
)
Box(
Modifier
.fillMaxSize()
.background(Color.White)
) {
Box(
Modifier
.fillMaxSize()
.background(bgColor),
contentAlignment = Alignment.Center
) {
Text(text = "Safety Watcher", color = textColor)
}
}
LaunchedEffect(Unit) {
enabled = true
delay(2000)
startMain.invoke()
}
}

@ -1,32 +0,0 @@
package com.yinuo.safetywatcher.ui.cloud
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun CloudView() {
// rememberLauncherForActivityResult(contract = ActivityResultContracts.OpenDocumentTree(), onResult ={} )
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
val context = LocalContext.current;
val viewModel: CloudViewModel = viewModel()
Button(onClick = { viewModel.exportExcel(context) }) {
Text(text = "导出数据")
}
}
}

@ -1,28 +0,0 @@
package com.yinuo.safetywatcher.ui.cloud
import android.content.Context
import androidx.lifecycle.ViewModel
import com.yinuo.safetywatcher.xls.SimpleCellValue
import com.yinuo.safetywatcher.xls.utils.ExcelUtils
class CloudViewModel : ViewModel() {
fun exportExcel(context: Context){
val allData: MutableList<List<SimpleCellValue>> = ArrayList()
val row1: MutableList<SimpleCellValue> = ArrayList()
row1.add(SimpleCellValue("5.22"))
row1.add(SimpleCellValue("1"))
row1.add(SimpleCellValue("2"))
row1.add(SimpleCellValue("3"))
val row2: MutableList<SimpleCellValue> = ArrayList()
row2.add(SimpleCellValue("5.23"))
row2.add(SimpleCellValue("4"))
row2.add(SimpleCellValue("5"))
row2.add(SimpleCellValue("6"))
allData.add(row1)
allData.add(row2)
ExcelUtils.writeStringListToExcel(allData, context)
}
}

@ -1,64 +0,0 @@
package com.yinuo.safetywatcher.ui.home
import android.view.TextureView
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.yinuo.safetywatcher.navi.ModelPath
import com.yinuo.safetywatcher.navi.NavigationUtil
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun HomeView() {
// 权限
// val permissionsState = rememberMultiplePermissionsState(
// permissions = listOf(
// android.Manifest.permission.MANAGE_EXTERNAL_STORAGE,
// android.Manifest.permission.CAMERA,
// )
// )
// if (permissionsState.allPermissionsGranted) {
val viewModel: HomeViewModel = viewModel()
val context = LocalContext.current
Row(modifier = Modifier.fillMaxSize()) {
AndroidView(factory = {
TextureView(it)
}, modifier = Modifier
.fillMaxHeight()
.width(200.dp), update = {
viewModel.setTextureView(it, context);
})
Column(
modifier = Modifier
.width(150.dp)
.fillMaxHeight()
.background(Color.Red)
) {
Button(onClick = { NavigationUtil.to(ModelPath.Setting) }) {
Text(text = "设置")
}
Button(onClick = { NavigationUtil.to(ModelPath.Cloud) }) {
Text(text = "云平台")
}
}
}
// } else {
// LaunchedEffect(Unit) {
// permissionsState.launchMultiplePermissionRequest()
// }
// }
}

@ -1,61 +0,0 @@
package com.yinuo.safetywatcher.ui.home
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.SurfaceTexture
import android.util.Log
import android.view.TextureView
import androidx.activity.ComponentActivity
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import com.yinuo.safetywatcher.utils.RxHelper
import com.yinuo.safetywatcher.utils.SurfaceTextureListenerWrapper
import io.reactivex.Single
import org.easydarwin.push.MediaStream
class HomeViewModel : ViewModel() {
@SuppressLint("StaticFieldLeak")
private var mediaStream: MediaStream? = null
private fun getMediaStream(context: Context, owner: LifecycleOwner): Single<MediaStream?>? {
val single: Single<MediaStream?> =
RxHelper.single(MediaStream.getBindedMediaStream(context, owner), mediaStream)
return if (mediaStream == null) {
single.doOnSuccess { ms -> mediaStream = ms }
} else {
single
}
}
@SuppressLint("CheckResult")
fun setTextureView(it: TextureView, context: Context) {
if (context is ComponentActivity) {
val activity: ComponentActivity = context
getMediaStream(context, activity)?.subscribe(
{ ms ->
if (it.isAvailable) {
ms?.setSurfaceTexture(it.surfaceTexture)
} else {
it.surfaceTextureListener = object : SurfaceTextureListenerWrapper() {
override fun onSurfaceTextureAvailable(
surfaceTexture: SurfaceTexture,
i: Int,
i1: Int
) {
ms?.setSurfaceTexture(surfaceTexture)
}
override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture): Boolean {
ms?.setSurfaceTexture(null)
return true
}
}
}
},
{
Log.w("HomeViewModel", "创建服务出错!" + it.message)
})
}
}
}

@ -1,13 +0,0 @@
package com.yinuo.safetywatcher.ui.setting
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun SettingView(modifier: Modifier = Modifier) {
Box(modifier = modifier){
Text(text = "这是设置页面")
}
}

@ -1,11 +0,0 @@
package com.yinuo.safetywatcher.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

@ -1,24 +0,0 @@
package com.yinuo.safetywatcher.ui.theme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
)
@Composable
fun EasypusherTheme(
content: @Composable () -> Unit
) {
val colorScheme = LightColorScheme
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

@ -1,34 +0,0 @@
package com.yinuo.safetywatcher.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)

@ -1,16 +0,0 @@
package com.yinuo.safetywatcher.utils
import com.lztek.toolkit.Lztek
object LztekUtil {
private var mLztek: Lztek? = null;
fun setObject(value: Lztek) {
mLztek = value
}
fun getLztek(): Lztek {
return mLztek!!
}
}

@ -1,47 +0,0 @@
package com.yinuo.safetywatcher.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.easydarwin.util.AbstractSubscriber;
import org.reactivestreams.Publisher;
import io.reactivex.Single;
import io.reactivex.subjects.PublishSubject;
/**
* Created by apple on 2017/12/22.
*/
public class RxHelper {
static boolean IGNORE_ERROR = false;
public static <T> Single<T> single(@NonNull Publisher<T> t, @Nullable T defaultValueIfNotNull){
if (defaultValueIfNotNull != null) return Single.just(defaultValueIfNotNull);
final PublishSubject sub = PublishSubject.create();
t.subscribe(new AbstractSubscriber<T>() {
@Override
public void onNext(T t) {
super.onNext(t);
sub.onNext(t);
}
@Override
public void onError(Throwable t) {
if (IGNORE_ERROR) {
super.onError(t);
sub.onComplete();
}else {
sub.onError(t);
}
}
@Override
public void onComplete() {
super.onComplete();
sub.onComplete();
}
});
return sub.firstOrError();
}
}

@ -1,27 +0,0 @@
package com.yinuo.safetywatcher.utils;
import android.graphics.SurfaceTexture;
import android.view.TextureView;
/**
* Created by apple on 2017/9/11.
*/
public abstract class SurfaceTextureListenerWrapper implements TextureView.SurfaceTextureListener{
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
}

@ -1,5 +0,0 @@
package com.yinuo.safetywatcher.xls
interface ICellValue {
fun getValue(): String
}

@ -1,7 +0,0 @@
package com.yinuo.safetywatcher.xls
data class SimpleCellValue(val content: String) : ICellValue {
override fun getValue(): String {
return content
}
}

@ -1,138 +0,0 @@
package com.yinuo.safetywatcher.xls.utils
import android.content.Context
import android.util.Log
import com.yinuo.safetywatcher.xls.ICellValue
import com.yinuo.safetywatcher.R
import jxl.Workbook
import jxl.WorkbookSettings
import jxl.write.Label
import jxl.write.WritableCellFormat
import jxl.write.WritableFont
import jxl.write.WritableWorkbook
import jxl.write.WriteException
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
/**
* excel表格工具
*/
object ExcelUtils {
private const val TAG: String = "ExcelUtils"
private const val UTF8_ENCODING = "UTF-8"
private var arial12format: WritableCellFormat? = null
private fun format() {
try {
val writableFont = WritableFont(WritableFont.ARIAL, 12)
arial12format = WritableCellFormat(writableFont)
arial12format?.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN)
} catch (exception: WriteException) {
Log.e(TAG, "format() e==" + exception.message)
}
}
private fun makeDir(filePath: File) {
if (!filePath.parentFile.exists()) {
makeDir(filePath.parentFile)
}
filePath.mkdir()
}
private fun deleteByPath(filePath: String) {
val file = File(filePath)
if (file.exists()) {
if (file.isFile || file.isDirectory) {
file.delete()
}
}
}
private fun deleteExistFile(excelFilePath: String) {
val file = File(excelFilePath)
if (file.exists()) {
val files: Array<File> = file.listFiles()
for (i in files.indices) {
deleteByPath(files[i].absolutePath)
}
}
}
private fun initExcel(
allColumNames: Array<String>,
filePath: String,
sheetName: String
) {
format()
var workbook: WritableWorkbook? = null
try {
val file = File(filePath)
if (!file.exists()) {
file.createNewFile()
}
workbook = Workbook.createWorkbook(file)
val sheet = workbook.createSheet(sheetName, 0)
allColumNames.forEachIndexed { index, s ->
sheet.addCell(Label(index, 0, s, arial12format))
}
workbook.write()
} catch (e: Exception) {
Log.e(TAG, "initExcel e==" + e.message)
} finally {
try {
workbook?.close()
} catch (e: Exception) {
Log.e(TAG, "initExcel e==" + e.message)
}
}
}
fun writeStringListToExcel(
allRowsData: List<List<ICellValue>>,
context: Context
): Boolean {
val fileName = PathUtils.getNowTimeFormat(PathUtils.DATE_TO_STRING_LONG_PATTERN) + ".xls"
PathUtils.EXCEL_EXPORT_NAME = fileName
val filePath =
PathUtils.getUDiskPath() + PathUtils.EXCEL_EXPORT_PATH + PathUtils.EXCEL_EXPORT_NAME
deleteExistFile(PathUtils.getUDiskPath() + PathUtils.EXCEL_EXPORT_PATH)
makeDir(File(PathUtils.getUDiskPath() + PathUtils.EXCEL_EXPORT_PATH))
val columns = context.resources.getStringArray(R.array.excel_column)
initExcel(columns, filePath, PathUtils.SHEET_NAME) //需要写入权限
if (PathUtils.isListEmpty(allRowsData) || context == null)
return false
var writebook: WritableWorkbook? = null
var inputStream: InputStream? = null
try {
val setEncode = WorkbookSettings()
setEncode.encoding = UTF8_ENCODING
inputStream = FileInputStream(File(filePath))
val workbook = Workbook.getWorkbook(inputStream)
val file = File(filePath)
writebook = Workbook.createWorkbook(file, workbook)
val sheet = writebook.getSheet(0)
for ((row, item) in allRowsData.withIndex()) {
item.forEachIndexed { index, cellValue ->
sheet.addCell(Label(index, row + 1, cellValue.getValue(), arial12format))
}
}
writebook.write()
Log.i(TAG, "Excelel 写入成功")
return true
} catch (e: Exception) {
Log.e(TAG, "writeStringListToExcel() e==" + e.message)
} finally {
writebook?.close()
inputStream?.close()
}
return false
}
}

@ -1,52 +0,0 @@
package com.yinuo.safetywatcher.xls.utils
import android.content.Context
import android.os.Environment
import com.yinuo.safetywatcher.utils.LztekUtil
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
/**
* 基础工具类
*/
object PathUtils {
private const val TAG: String = "BaseUtils"
const val SHEET_NAME = "表格1"
const val EXCEL_EXPORT_PATH = "/ExportExcel/"
lateinit var EXCEL_EXPORT_NAME: String
const val DATE_TO_STRING_LONG_PATTERN: String = "yyyy_MM_dd_HH_mm_ss"
fun <T> isListEmpty(list: List<T>?): Boolean {
return list == null || list.isEmpty()
}
/**
* 获取应用中文件存储
*/
fun getExternalStoragePath(context: Context): String? {
return context.getExternalFilesDir(null)?.path
}
/**
* 获取U盘存储
*/
fun getUDiskPath(): String {
return LztekUtil.getLztek().usbStoragePath
}
fun getExternalStorageDirectory(context: Context): String? {
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path
}
/**
* 外部存储目录
*/
fun getExternalStorageDirectory(): String? {
return Environment.getExternalStorageDirectory().absolutePath
}
fun getNowTimeFormat(dateToStringLongPattern: String): String {
return SimpleDateFormat(dateToStringLongPattern, Locale.ROOT).format(Date());
}
}

@ -1,30 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

@ -1,11 +0,0 @@
<resources>
<string name="app_name">app-compose</string>
<string-array name="excel_column">
<item>时间</item>
<item>属性</item>
<item></item>
<item>单位</item>
</string-array>
</resources>

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Easypusher" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

Binary file not shown.

@ -1,279 +0,0 @@
package com.yinuo.safetywatcher;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.TextureView;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.Observer;
import com.yinuo.safetywatcher.xls.SimpleCellValue;
import com.yinuo.safetywatcher.xls.utils.ExcelUtils;
import org.easydarwin.push.MediaStream;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Single;
import io.reactivex.functions.Consumer;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CAMERA_PERMISSION = 1000;
private static final int REQUEST_MEDIA_PROJECTION = 1001;
public static final String HOST = "192.168.51.189";
private MediaStream mediaStream;
private Single<MediaStream> getMediaStream() {
Single<MediaStream> single = RxHelper.single(MediaStream.getBindedMediaStream(this, this), mediaStream);
if (mediaStream == null) {
return single.doOnSuccess(new Consumer<MediaStream>() {
@Override
public void accept(MediaStream ms) throws Exception {
mediaStream = ms;
}
});
} else {
return single;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
CheckBox hevc_enable = findViewById(R.id.enable_265);
hevc_enable.setChecked(PreferenceManager.getDefaultSharedPreferences(this).getBoolean("try_265_encode", false));
hevc_enable.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
PreferenceManager.getDefaultSharedPreferences(MainActivity.this).edit().putBoolean("try_265_encode", isChecked).apply();
}
});
// 启动服务...
Intent intent = new Intent(this, MediaStream.class);
startService(intent);
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(final MediaStream ms) throws Exception {
ms.observeCameraPreviewResolution(MainActivity.this, new Observer<int[]>() {
@Override
public void onChanged(@Nullable int[] size) {
Toast.makeText(MainActivity.this, "当前摄像头分辨率为:" + size[0] + "*" + size[1], Toast.LENGTH_SHORT).show();
}
});
final TextView pushingStateText = findViewById(R.id.pushing_state);
final TextView pushingBtn = findViewById(R.id.pushing);
ms.observePushingState(MainActivity.this, new Observer<MediaStream.PushingState>() {
@Override
public void onChanged(@Nullable MediaStream.PushingState pushingState) {
if (pushingState.screenPushing) {
pushingStateText.setText("屏幕推送");
// 更改屏幕推送按钮状态.
TextView tview = findViewById(R.id.pushing_desktop);
if (ms.isScreenPushing()) {
tview.setText("取消推送");
} else {
tview.setText("推送屏幕");
}
findViewById(R.id.pushing_desktop).setEnabled(true);
} else {
pushingStateText.setText("推送");
if (ms.isCameraPushing()) {
pushingBtn.setText("停止");
} else {
pushingBtn.setText("推送");
}
}
pushingStateText.append(":\t" + pushingState.msg);
if (pushingState.state > 0) {
pushingStateText.append(pushingState.url);
pushingStateText.append("\n");
if ("avc".equals(pushingState.videoCodec)) {
pushingStateText.append("视频编码方式:" + "H264硬编码");
} else if ("hevc".equals(pushingState.videoCodec)) {
pushingStateText.append("视频编码方式:" + "H265硬编码");
} else if ("x264".equals(pushingState.videoCodec)) {
pushingStateText.append("视频编码方式:" + "x264");
}
}
}
});
TextureView textureView = findViewById(R.id.texture_view);
if (textureView.isAvailable()) {
ms.setSurfaceTexture(textureView.getSurfaceTexture());
} else {
textureView.setSurfaceTextureListener(new SurfaceTextureListenerWrapper() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
ms.setSurfaceTexture(surfaceTexture);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
ms.setSurfaceTexture(null);
return true;
}
});
}
if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA_PERMISSION);
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(MainActivity.this, "创建服务出错!", Toast.LENGTH_SHORT).show();
}
});
}
public void onPushing(View view) {
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(MediaStream mediaStream) throws Exception {
MediaStream.PushingState state = mediaStream.getPushingState();
if (state != null && state.state > 0) { // 终止推送和预览
mediaStream.stopStream();
mediaStream.closeCameraPreview();
} else { // 启动预览和推送.
mediaStream.openCameraPreview();
String id = PreferenceManager.getDefaultSharedPreferences(MainActivity.this).getString("caemra-id", null);
if (id == null) {
double v = Math.random() * 1000;
id = "c_" + (int) v;
PreferenceManager.getDefaultSharedPreferences(MainActivity.this).edit().putString("caemra-id", id).apply();
}
mediaStream.startStream(HOST, "554", id);
}
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_CAMERA_PERMISSION: {
if (grantResults.length > 1
&& grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(MediaStream mediaStream) throws Exception {
mediaStream.notifyPermissionGranted();
}
});
} else {
finish();
}
break;
}
}
}
// 推送屏幕.
public void onPushScreen(final View view) {
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(MediaStream mediaStream) {
if (mediaStream.isScreenPushing()) { // 正在推送,那取消推送。
// 取消推送。
mediaStream.stopPushScreen();
} else { // 没在推送,那启动推送。
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { // lollipop 以前版本不支持。
return;
}
MediaProjectionManager mMpMngr = (MediaProjectionManager) getApplicationContext().getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mMpMngr.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
// 防止点多次.
view.setEnabled(false);
}
}
});
}
@Override
protected void onActivityResult(int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_MEDIA_PROJECTION) {
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(MediaStream mediaStream) {
mediaStream.pushScreen(resultCode, data, HOST, "554", "screen111");
}
});
}
}
public void onSwitchCamera(View view) {
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(MediaStream mediaStream) throws Exception {
mediaStream.switchCamera();
}
});
}
public void onUVCCamera(View view) {
Intent intent = new Intent(this, UVCActivity.class);
// Intent intent = new Intent(this, ProVideoActivity.class);
// String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/mvtest.mp4";
// intent.putExtra("videoPath", path);
startActivity(intent);
// List<List<SimpleCellValue>> allData = new ArrayList<>();
// List<SimpleCellValue> row1 = new ArrayList<>();
// row1.add(new SimpleCellValue("5.22"));
// row1.add(new SimpleCellValue("1"));
// row1.add(new SimpleCellValue("2"));
// row1.add(new SimpleCellValue("3"));
// List<SimpleCellValue> row2 = new ArrayList<>();
// row2.add(new SimpleCellValue("5.23"));
// row2.add(new SimpleCellValue("4"));
// row2.add(new SimpleCellValue("5"));
// row2.add(new SimpleCellValue("6"));
//
// allData.add(row1);
// allData.add(row2);
//
// ExcelUtils.INSTANCE.writeStringListToExcel(allData, this);
}
public void OnSaveRecord(View view) {
if (mediaStream.isRecording()) {
mediaStream.stopRecord();
} else {
mediaStream.startRecord(this.getCacheDir().getAbsolutePath(), 10 * 1000);
}
}
}

@ -1,47 +0,0 @@
package com.yinuo.safetywatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.easydarwin.util.AbstractSubscriber;
import org.reactivestreams.Publisher;
import io.reactivex.Single;
import io.reactivex.subjects.PublishSubject;
/**
* Created by apple on 2017/12/22.
*/
public class RxHelper {
static boolean IGNORE_ERROR = false;
public static <T> Single<T> single(@NonNull Publisher<T> t, @Nullable T defaultValueIfNotNull){
if (defaultValueIfNotNull != null) return Single.just(defaultValueIfNotNull);
final PublishSubject sub = PublishSubject.create();
t.subscribe(new AbstractSubscriber<T>() {
@Override
public void onNext(T t) {
super.onNext(t);
sub.onNext(t);
}
@Override
public void onError(Throwable t) {
if (IGNORE_ERROR) {
super.onError(t);
sub.onComplete();
}else {
sub.onError(t);
}
}
@Override
public void onComplete() {
super.onComplete();
sub.onComplete();
}
});
return sub.firstOrError();
}
}

@ -1,27 +0,0 @@
package com.yinuo.safetywatcher;
import android.graphics.SurfaceTexture;
import android.view.TextureView;
/**
* Created by apple on 2017/9/11.
*/
public abstract class SurfaceTextureListenerWrapper implements TextureView.SurfaceTextureListener{
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
}

@ -1,208 +0,0 @@
package com.yinuo.safetywatcher;
import androidx.lifecycle.Observer;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.os.Environment;
import android.preference.PreferenceManager;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.TextureView;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import org.easydarwin.push.MediaStream;
import io.reactivex.Single;
import io.reactivex.functions.Consumer;
public class UVCActivity extends AppCompatActivity {
private MediaStream mediaStream;
private static final int REQUEST_CAMERA_PERMISSION = 1000;
private Single<MediaStream> getMediaStream() {
Single<MediaStream> single = RxHelper.single(MediaStream.getBindedMediaStream(this, this), mediaStream);
if (mediaStream == null) {
return single.doOnSuccess(new Consumer<MediaStream>() {
@Override
public void accept(MediaStream ms) throws Exception {
mediaStream = ms;
}
});
} else {
return single;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_uvc);
// 启动服务...
Intent intent = new Intent(this, MediaStream.class);
startService(intent);
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(final MediaStream ms) throws Exception {
final TextView pushingStateText = findViewById(R.id.pushing_state);
final TextView pushingBtn = findViewById(R.id.pushing);
ms.observePushingState(UVCActivity.this, new Observer<MediaStream.PushingState>() {
@Override
public void onChanged(@Nullable MediaStream.PushingState pushingState) {
if (pushingState.screenPushing) {
pushingStateText.setText("屏幕推送");
} else {
pushingStateText.setText("推送");
if (pushingState.state > 0) {
pushingBtn.setText("停止");
} else {
pushingBtn.setText("推送");
}
}
pushingStateText.append(":\t" + pushingState.msg);
if (pushingState.state > 0) {
pushingStateText.append(pushingState.url);
}
}
});
TextureView textureView = findViewById(R.id.texture_view);
textureView.setSurfaceTextureListener(new SurfaceTextureListenerWrapper() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
ms.setSurfaceTexture(surfaceTexture);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
ms.setSurfaceTexture(null);
return true;
}
});
if (ActivityCompat.checkSelfPermission(UVCActivity.this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(UVCActivity.this, android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(UVCActivity.this, new String[]{android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA_PERMISSION);
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(UVCActivity.this, "创建服务出错!", Toast.LENGTH_SHORT).show();
}
});
}
// 权限获取到了.
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_CAMERA_PERMISSION: {
if (grantResults.length > 1
&& grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(MediaStream mediaStream) throws Exception {
mediaStream.notifyPermissionGranted();
}
});
} else {
// 没有获取到权限,退出....
Intent intent = new Intent(this, MediaStream.class);
stopService(intent);
finish();
}
break;
}
}
}
public void onPush(View view) {
// 异步获取到MediaStream对象.
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(final MediaStream mediaStream) throws Exception {
// 判断当前的推送状态.
MediaStream.PushingState state = mediaStream.getPushingState();
if (state != null && state.state > 0) { // 当前正在推送,那终止推送和预览
mediaStream.stopStream();
mediaStream.closeCameraPreview();
}else{
// switch 0表示后置,1表示前置,2表示UVC摄像头
RxHelper.single(mediaStream.switchCamera(2), null).subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) throws Exception {
String id = PreferenceManager.getDefaultSharedPreferences(UVCActivity.this).getString("uvc-id", null);
if (id == null) {
double v = Math.random() * 1000;
id = "uvc_" + (int) v;
PreferenceManager.getDefaultSharedPreferences(UVCActivity.this).edit().putString("uvc-id", id).apply();
}
mediaStream.startStream("cloud.easydarwin.org", "554", id);
}
}, new Consumer<Throwable>() {
@Override
public void accept(final Throwable t) throws Exception {
t.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(UVCActivity.this, "UVC摄像头启动失败.." + t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
});
}
}
});
}
public void onRecord(View view) { // 开始或结束录像.
final TextView txt = (TextView) view;
getMediaStream().subscribe(new Consumer<MediaStream>() {
@Override
public void accept(MediaStream mediaStream) throws Exception {
if (mediaStream.isRecording()){ // 如果正在录像,那停止.
mediaStream.stopRecord();
txt.setText("录像");
}else { // 没在录像,开始录像...
// 表示最大录像时长为30秒,30秒后如果没有停止,会生成一个新文件.依次类推...
// 文件格式为test_uvc_0.mp4,test_uvc_1.mp4,test_uvc_2.mp4,test_uvc_3.mp4
String path = getExternalFilesDir(Environment.DIRECTORY_MOVIES) + "/test_uvc.mp4";
mediaStream.startRecord(path, 30000);
final TextView pushingStateText = findViewById(R.id.pushing_state);
pushingStateText.append("\n录像地址:" + path);
txt.setText("停止");
}
}
});
}
public void onQuit(View view) { // 退出
finish();
// 终止服务...
Intent intent = new Intent(this, MediaStream.class);
stopService(intent);
}
public void onBackground(View view) { // 后台
finish();
}
}

@ -21,9 +21,4 @@ dependencies {
implementation('androidx.lifecycle:lifecycle-reactivestreams:2.6.1')
implementation('androidx.lifecycle:lifecycle-common:2.6.1')
implementation('androidx.lifecycle:lifecycle-livedata:2.6.1')
implementation(name: 'libuvccamera-release', ext: 'aar') {
exclude module: 'support-v4'
exclude module: 'appcompat-v7'
}
}

@ -1,38 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.easydarwin.easypusher">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-feature
android:name="android.hardware.usb.host"
android:required="true" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<application>
<service
android:name="org.easydarwin.push.PushScreenService"
android:enabled="true" />
<service
android:name="org.easydarwin.push.UVCCameraService"
android:enabled="true" />
<service
android:name="org.easydarwin.push.MediaStream"
android:enabled="true" />
</application>
</manifest>

@ -1,263 +0,0 @@
package org.easydarwin.push;
import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import org.easydarwin.easypusher.BuildConfig;
import org.easydarwin.muxer.EasyMuxer;
import org.easydarwin.sw.JNIUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar;
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar;
/**
* Created by apple on 2017/5/13.
*/
public class HWConsumer extends Thread implements VideoConsumer {
private static final String TAG = "Pusher";
private final MediaStream.CodecInfo info;
public EasyMuxer mMuxer;
private final Context mContext;
private final Pusher mPusher;
private int mHeight;
private int mWidth;
private MediaCodec mMediaCodec;
private ByteBuffer[] inputBuffers;
private ByteBuffer[] outputBuffers;
private volatile boolean mVideoStarted;
private MediaFormat newFormat;
public HWConsumer(Context context, Pusher pusher, MediaStream.CodecInfo info) {
mContext = context;
mPusher = pusher;
this.info = info;
}
@Override
public void onVideoStart(int width, int height) throws IOException {
newFormat = null;
this.mWidth = width;
this.mHeight = height;
startMediaCodec();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + 1) {
inputBuffers = outputBuffers = null;
} else {
inputBuffers = mMediaCodec.getInputBuffers();
outputBuffers = mMediaCodec.getOutputBuffers();
}
start();
mVideoStarted = true;
}
final int millisPerframe = 1000 / 20;
long lastPush = 0;
@Override
public int onVideo(byte[] data, int format) {
if (!mVideoStarted) return 0;
try {
if (lastPush == 0) {
lastPush = System.currentTimeMillis();
}
long time = System.currentTimeMillis() - lastPush;
if (time >= 0) {
time = millisPerframe - time;
if (time > 0) Thread.sleep(time / 2);
}
if (info.mColorFormat == COLOR_FormatYUV420SemiPlanar) {
JNIUtil.yuvConvert(data, mWidth, mHeight, 6);
} else if (info.mColorFormat == COLOR_TI_FormatYUV420PackedSemiPlanar) {
JNIUtil.yuvConvert(data, mWidth, mHeight, 6);
} else if (info.mColorFormat == COLOR_FormatYUV420Planar) {
JNIUtil.yuvConvert(data, mWidth, mHeight, 5);
} else {
JNIUtil.yuvConvert(data, mWidth, mHeight, 5);
}
int bufferIndex = mMediaCodec.dequeueInputBuffer(0);
if (bufferIndex >= 0) {
ByteBuffer buffer = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
buffer = mMediaCodec.getInputBuffer(bufferIndex);
} else {
buffer = inputBuffers[bufferIndex];
}
buffer.clear();
buffer.put(data);
buffer.clear();
mMediaCodec.queueInputBuffer(bufferIndex, 0, data.length, System.nanoTime() / 1000, MediaCodec.BUFFER_FLAG_KEY_FRAME);
}
if (time > 0) Thread.sleep(time / 2);
lastPush = System.currentTimeMillis();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return 0;
}
@Override
public void run() {
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = 0;
byte[] mPpsSps = new byte[0];
byte[] h264 = new byte[mWidth * mHeight];
do {
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 10000);
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
// not expected for an encoder
outputBuffers = mMediaCodec.getOutputBuffers();
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
synchronized (HWConsumer.this) {
newFormat = mMediaCodec.getOutputFormat();
EasyMuxer muxer = mMuxer;
if (muxer != null) {
// should happen before receiving buffers, and should only happen once
muxer.addTrack(newFormat, true);
}
}
} else if (outputBufferIndex < 0) {
// let's ignore it
} else {
ByteBuffer outputBuffer;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
} else {
outputBuffer = outputBuffers[outputBufferIndex];
}
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
EasyMuxer muxer = mMuxer;
if (muxer != null) {
muxer.pumpStream(outputBuffer, bufferInfo, true);
}
boolean sync = false;
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// sps
sync = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
if (!sync) {
byte[] temp = new byte[bufferInfo.size];
outputBuffer.get(temp);
mPpsSps = temp;
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
continue;
} else {
mPpsSps = new byte[0];
}
}
sync |= (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
int len = mPpsSps.length + bufferInfo.size;
if (len > h264.length) {
h264 = new byte[len];
}
if (sync) {
System.arraycopy(mPpsSps, 0, h264, 0, mPpsSps.length);
outputBuffer.get(h264, mPpsSps.length, bufferInfo.size);
mPusher.push(h264, 0, mPpsSps.length + bufferInfo.size, bufferInfo.presentationTimeUs / 1000, 1);
if (BuildConfig.DEBUG)
Log.i(TAG, String.format("push i video stamp:%d", bufferInfo.presentationTimeUs / 1000));
} else {
outputBuffer.get(h264, 0, bufferInfo.size);
mPusher.push(h264, 0, bufferInfo.size, bufferInfo.presentationTimeUs / 1000, 1);
if (BuildConfig.DEBUG)
Log.i(TAG, String.format("push video stamp:%d", bufferInfo.presentationTimeUs / 1000));
}
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
}
}
while (mVideoStarted);
}
@Override
public void onVideoStop() {
do {
newFormat = null;
mVideoStarted = false;
try {
join();
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (isAlive());
if (mMediaCodec != null) {
stopMediaCodec();
mMediaCodec = null;
}
}
@Override
public synchronized void setMuxer(EasyMuxer muxer) {
if (muxer != null) {
if (newFormat != null)
muxer.addTrack(newFormat, true);
}
mMuxer = muxer;
}
/**
*
*/
private void startMediaCodec() throws IOException {
/*
SD (Low quality) SD (High quality) HD 720p
1 HD 1080p
1
Video resolution 320 x 240 px 720 x 480 px 1280 x 720 px 1920 x 1080 px
Video frame rate 20 fps 30 fps 30 fps 30 fps
Video bitrate 384 Kbps 2 Mbps 4 Mbps 10 Mbps
*/
int framerate = 20;
// if (width == 640 || height == 640) {
// bitrate = 2000000;
// } else if (width == 1280 || height == 1280) {
// bitrate = 4000000;
// } else {
// bitrate = 2 * width * height;
// }
int bitrate = (int) (mWidth * mHeight * 20 * 2 * 0.05f);
if (mWidth >= 1920 || mHeight >= 1920) bitrate *= 0.3;
else if (mWidth >= 1280 || mHeight >= 1280) bitrate *= 0.4;
else if (mWidth >= 720 || mHeight >= 720) bitrate *= 0.6;
mMediaCodec = MediaCodec.createByCodecName(info.mName);
MediaFormat mediaFormat = MediaFormat.createVideoFormat(info.mime, mWidth, mHeight);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, info.mColorFormat);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start();
Bundle params = new Bundle();
params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mMediaCodec.setParameters(params);
}
}
/**
*
*/
private void stopMediaCodec() {
mMediaCodec.stop();
mMediaCodec.release();
}
}

@ -1,371 +0,0 @@
package org.easydarwin.push;
import android.annotation.TargetApi;
import android.app.Application;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface;
import android.view.WindowManager;
import org.easydarwin.easypusher.BuildConfig;
import org.easydarwin.easypusher.R;
import java.io.IOException;
import java.nio.ByteBuffer;
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
public class PushScreenService extends Service {
private static final String TAG = "RService";
public static final String ACTION_CLOSE_PUSHING_SCREEN = "ACTION_CLOSE_PUSHING_SCREEN";
private String mVideoPath;
private MediaProjectionManager mMpmngr;
private MediaProjection mMpj;
private VirtualDisplay mVirtualDisplay;
private int windowWidth;
private int windowHeight;
private int screenDensity;
private Surface mSurface;
private MediaCodec mMediaCodec;
private WindowManager wm;
MediaStream.CodecInfo info = new MediaStream.CodecInfo();
private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
private Thread mPushThread;
private byte[] mPpsSps;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Application app = (Application) context.getApplicationContext();
MediaStream.stopPushScreen(app);
}
};
private final Pusher mEasyPusher = new EasyPusher();
private String ip;
private String port;
private String id;
private MediaStream.PushingScreenLiveData liveData;
public class MyBinder extends Binder
{
public PushScreenService getService(){
return PushScreenService.this;
}
}
MyBinder binder = new MyBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onCreate() {
super.onCreate();
mMpmngr = (MediaProjectionManager) getApplicationContext().getSystemService(MEDIA_PROJECTION_SERVICE);
createEnvironment();
registerReceiver(mReceiver,new IntentFilter(ACTION_CLOSE_PUSHING_SCREEN));
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void configureMedia() throws IOException {
MediaStream.initEncoder(this, info);
if (TextUtils.isEmpty(info.mName) && info.mColorFormat == 0){
throw new IOException("media codec init error");
}
MediaFormat mediaFormat = MediaFormat.createVideoFormat(info.mime, windowWidth, windowHeight);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 1200000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
mediaFormat.setInteger(MediaFormat.KEY_CAPTURE_RATE, 25);
mediaFormat.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000);
mMediaCodec = MediaCodec.createByCodecName(info.mName);
mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mSurface = mMediaCodec.createInputSurface();
mMediaCodec.start();
}
private void createEnvironment() {
mVideoPath = Environment.getExternalStorageDirectory().getPath() + "/";
wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
windowWidth = wm.getDefaultDisplay().getWidth();
windowHeight = wm.getDefaultDisplay().getHeight();
DisplayMetrics displayMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(displayMetrics);
screenDensity = displayMetrics.densityDpi;
while (windowWidth > 480){
windowWidth /= 2;
windowHeight /=2;
}
windowWidth /= 16;
windowWidth *= 16;
windowHeight /= 16;
windowHeight *= 16;
}
private void startPush() {
// liveData.postValue(new MediaStream.PushingState(0, "未开始", true));
mPushThread = new Thread(){
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
startForeground(111, new Notification.Builder(PushScreenService.this).setContentTitle(getString(R.string.screen_pushing))
.setSmallIcon(R.drawable.ic_pusher_screen_pushing)
.addAction(new Notification.Action(R.drawable.ic_close_pushing_screen, "关闭",
PendingIntent.getBroadcast(getApplicationContext(), 10000, new Intent(ACTION_CLOSE_PUSHING_SCREEN), FLAG_CANCEL_CURRENT))).build());
final String url = String.format("rtsp://%s:%s/%s.sdp", ip, port, id);
InitCallback _callback = new InitCallback() {
@Override
public void onCallback(int code) {
String msg = "";
switch (code) {
case EasyPusher.OnInitPusherCallback.CODE.EASY_ACTIVATE_INVALID_KEY:
msg = ("无效Key");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_ACTIVATE_SUCCESS:
msg = ("未开始");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_PUSH_STATE_CONNECTING:
msg = ("连接中");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_PUSH_STATE_CONNECTED:
msg = ("连接成功");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_PUSH_STATE_CONNECT_FAILED:
msg = ("连接失败");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_PUSH_STATE_CONNECT_ABORT:
msg = ("连接异常中断");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_PUSH_STATE_PUSHING:
msg = ("推流中");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_PUSH_STATE_DISCONNECTED:
msg = ("断开连接");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_ACTIVATE_PLATFORM_ERR:
msg = ("平台不匹配");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_ACTIVATE_COMPANY_ID_LEN_ERR:
msg = ("授权使用商不匹配");
break;
case EasyPusher.OnInitPusherCallback.CODE.EASY_ACTIVATE_PROCESS_NAME_LEN_ERR:
msg = ("进程名称长度不匹配");
break;
}
liveData.postValue(new MediaStream.PushingState(url, code, msg, true));
}
};
// startStream(ip, port, id, _callback);
mEasyPusher.initPush( getApplicationContext(), _callback);
MediaStream.PushingState.sCodec = (info.hevcEncode ? "hevc":"avc");
mEasyPusher.setMediaInfo(info.hevcEncode ? Pusher.Codec.EASY_SDK_VIDEO_CODEC_H265:Pusher.Codec.EASY_SDK_VIDEO_CODEC_H264, 25, Pusher.Codec.EASY_SDK_AUDIO_CODEC_AAC, 1, 8000, 16);
mEasyPusher.start(ip, port, String.format("%s.sdp", id), Pusher.TransType.EASY_RTP_OVER_TCP);
try {
byte[] h264 = new byte[102400];
while (mPushThread != null) {
int index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 10000);
Log.d(TAG, "dequeue output buffer index=" + index);
if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {//请求超时
try {
// wait 10ms
Thread.sleep(10);
} catch (InterruptedException e) {
}
} else if (index >= 0) {//有效输出
ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(index);
outputBuffer.position(mBufferInfo.offset);
outputBuffer.limit(mBufferInfo.offset + mBufferInfo.size);
boolean sync = false;
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// sps
sync = (mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
if (!sync) {
byte[] temp = new byte[mBufferInfo.size];
outputBuffer.get(temp);
mPpsSps = temp;
mMediaCodec.releaseOutputBuffer(index, false);
continue;
} else {
mPpsSps = new byte[0];
}
}
sync |= (mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
int len = mPpsSps.length + mBufferInfo.size;
if (len > h264.length) {
h264 = new byte[len];
}
if (sync) {
System.arraycopy(mPpsSps, 0, h264, 0, mPpsSps.length);
outputBuffer.get(h264, mPpsSps.length, mBufferInfo.size);
mEasyPusher.push(h264, 0, mPpsSps.length + mBufferInfo.size, mBufferInfo.presentationTimeUs / 1000, 1);
if (BuildConfig.DEBUG)
Log.i(TAG, String.format("push i video stamp:%d", mBufferInfo.presentationTimeUs / 1000));
} else {
outputBuffer.get(h264, 0, mBufferInfo.size);
mEasyPusher.push(h264, 0, mBufferInfo.size, mBufferInfo.presentationTimeUs / 1000, 1);
if (BuildConfig.DEBUG)
Log.i(TAG, String.format("push video stamp:%d", mBufferInfo.presentationTimeUs / 1000));
}
mMediaCodec.releaseOutputBuffer(index, false);
}
}
stopForeground(true);
}finally {
mEasyPusher.stop();
liveData.postValue(new MediaStream.PushingState("", 0, "未开始", true));
}
}
};
mPushThread.start();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void stopPush(){
Thread t = mPushThread;
if (t != null){
mPushThread = null;
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
void startVirtualDisplay(int resultCode, Intent resultData, String ip, String port, String id, final MediaStream.PushingScreenLiveData liveData) {
try {
configureMedia();
} catch (IOException e) {
e.printStackTrace();
liveData.postValue(new MediaStream.PushingState("",-1, "编码器初始化错误", true));
return;
}
if (mMpj == null) {
mMpj = mMpmngr.getMediaProjection(resultCode, resultData);
}
if (mMpj == null) {
liveData.postValue(new MediaStream.PushingState("",-1, "未知错误", true));
return;
}
mVirtualDisplay = mMpj.createVirtualDisplay("record_screen", windowWidth, windowHeight, screenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR|DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC|DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, mSurface, null, null);
this.ip = ip;
this.port = port;
this.id = id;
this.liveData = liveData;
startPush();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void encodeToVideoTrack(int index) {
ByteBuffer encodedData = mMediaCodec.getOutputBuffer(index);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {//是编码需要的特定数据,不是媒体数据
// The codec config data was pulled out and fed to the muxer when we got
// the INFO_OUTPUT_FORMAT_CHANGED status.
// Ignore it.
Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
mBufferInfo.size = 0;
}
if (mBufferInfo.size == 0) {
Log.d(TAG, "info.size == 0, drop it.");
encodedData = null;
} else {
Log.d(TAG, "got buffer, info: size=" + mBufferInfo.size
+ ", presentationTimeUs=" + mBufferInfo.presentationTimeUs
+ ", offset=" + mBufferInfo.offset);
}
if (encodedData != null) {
encodedData.position(mBufferInfo.offset);
encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
// mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);//写入
Log.i(TAG, "sent " + mBufferInfo.size + " bytes to muxer...");
}
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private void release() {
Log.i(TAG, " release() ");
if (mMediaCodec != null) {
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
if (mSurface != null){
mSurface.release();
}
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
mVirtualDisplay = null;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onDestroy() {
super.onDestroy();
stopPush();
release();
if (mMpj != null) {
mMpj.stop();
}
unregisterReceiver(mReceiver);
}
}

@ -1,200 +0,0 @@
package org.easydarwin.push;
import android.app.Service;
import androidx.lifecycle.LiveData;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.util.SparseArray;
import android.widget.Toast;
import com.serenegiant.usb.DeviceFilter;
import com.serenegiant.usb.IButtonCallback;
import com.serenegiant.usb.IStatusCallback;
import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.UVCCamera;
import org.easydarwin.easypusher.BuildConfig;
import org.easydarwin.easypusher.R;
import java.nio.ByteBuffer;
public class UVCCameraService extends Service {
public static class UVCCameraLivaData extends LiveData<UVCCamera>{
@Override
protected void postValue(UVCCamera value) {
super.postValue(value);
}
}
public static final UVCCameraLivaData liveData = new UVCCameraLivaData();
public static class MyUVCCamera extends UVCCamera {
boolean prev = false;
@Override
public synchronized void startPreview() {
if (prev ) return;
super.startPreview();
prev = true;
}
@Override
public synchronized void stopPreview() {
if (!prev )return;
super.stopPreview();
prev = false;
}
@Override
public synchronized void destroy() {
prev = false;
super.destroy();
}
}
private static final String TAG = "OutterCamera";
private USBMonitor mUSBMonitor;
private UVCCamera mUVCCamera;
private SparseArray<UVCCamera> cameras = new SparseArray<>();
public class MyBinder extends Binder {
public UVCCameraService getService() {
return UVCCameraService.this;
}
}
MyBinder binder = new MyBinder();
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public UVCCamera getCamera() {
return mUVCCamera;
}
private void releaseCamera() {
if (mUVCCamera != null) {
try {
mUVCCamera.close();
mUVCCamera.destroy();
mUVCCamera = null;
} catch (final Exception e) {
//
}
}
}
@Override
public void onCreate() {
super.onCreate();
mUSBMonitor = new USBMonitor(this, new USBMonitor.OnDeviceConnectListener() {
@Override
public void onAttach(final UsbDevice device) {
Log.v(TAG, "onAttach:" + device);
mUSBMonitor.requestPermission(device);
}
@Override
public void onConnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock, final boolean createNew) {
releaseCamera();
if (BuildConfig.DEBUG) Log.v(TAG, "onConnect:");
try {
final UVCCamera camera = new MyUVCCamera();
camera.open(ctrlBlock);
camera.setStatusCallback(new IStatusCallback() {
@Override
public void onStatus(final int statusClass, final int event, final int selector,
final int statusAttribute, final ByteBuffer data) {
Log.i(TAG, "onStatus(statusClass=" + statusClass
+ "; " +
"event=" + event + "; " +
"selector=" + selector + "; " +
"statusAttribute=" + statusAttribute + "; " +
"data=...)");
}
});
camera.setButtonCallback(new IButtonCallback() {
@Override
public void onButton(final int button, final int state) {
Log.i(TAG, "onButton(button=" + button + "; " + "state=" + state + ")");
}
});
// camera.setPreviewTexture(camera.getSurfaceTexture());
mUVCCamera = camera;
liveData.postValue(camera);
Toast.makeText(UVCCameraService.this, "UVCCamera connected!", Toast.LENGTH_SHORT).show();
if (device != null)
cameras.append(device.getDeviceId(), camera);
}catch (Exception ex){
ex.printStackTrace();
}
}
@Override
public void onDisconnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock) {
Log.v(TAG, "onDisconnect:");
// Toast.makeText(MainActivity.this, R.string.usb_camera_disconnected, Toast.LENGTH_SHORT).show();
// releaseCamera();
if (device != null) {
UVCCamera camera = cameras.get(device.getDeviceId());
if (mUVCCamera == camera) {
mUVCCamera = null;
Toast.makeText(UVCCameraService.this, "UVCCamera disconnected!", Toast.LENGTH_SHORT).show();
liveData.postValue(null);
}
cameras.remove(device.getDeviceId());
}else {
Toast.makeText(UVCCameraService.this, "UVCCamera disconnected!", Toast.LENGTH_SHORT).show();
mUVCCamera = null;
liveData.postValue(null);
}
// if (mUSBMonitor != null) {
// mUSBMonitor.destroy();
// }
//
// mUSBMonitor = new USBMonitor(OutterCameraService.this, this);
// mUSBMonitor.setDeviceFilter(DeviceFilter.getDeviceFilters(OutterCameraService.this, R.xml.device_filter));
// mUSBMonitor.register();
}
@Override
public void onCancel(UsbDevice usbDevice) {
releaseCamera();
}
@Override
public void onDettach(final UsbDevice device) {
Log.v(TAG, "onDettach:");
releaseCamera();
// AppContext.getInstance().bus.post(new UVCCameraDisconnect());
}
});
mUSBMonitor.setDeviceFilter(DeviceFilter.getDeviceFilters(this, R.xml.device_filter));
mUSBMonitor.register();
}
@Override
public void onDestroy() {
releaseCamera();
if (mUSBMonitor != null) {
mUSBMonitor.unregister();
}
super.onDestroy();
}
}

@ -1,5 +1,4 @@
include ':app' //, ':libuvccamera-release'
//include ':app-compose'
include ':app'
include ':library-push'
include ':library-ijkplayer'
include ':library-serialPort'

Loading…
Cancel
Save