8个文件已添加
30个文件已修改
596 ■■■■■ 已修改文件
app/build.gradle.kts 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build/generated/aidl_source_output_dir/debug/out/com/safeluck/floatwindow/IMediaAidlInterface.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build/generated/aidl_source_output_dir/debug/out/com/safeluck/floatwindow/IMyCallback.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build/intermediates/desugar_graph/debug/dexBuilderDebug/out/currentProject/dirs_bucket_9/graph.bin 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.values.at 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at 补丁 | 查看 | 原始文档 | blame | 历史
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/AndroidManifest.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/MainActivity.kt 368 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/MediaArgu.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/AndroidCameraRecordManager.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java 105 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/util/VideoFileUtils.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
key/Verify.txt 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
key/bjkey.jks 补丁 | 查看 | 原始文档 | blame | 历史
key/key.jks 补丁 | 查看 | 原始文档 | blame | 历史
key/key.jks.ori 补丁 | 查看 | 原始文档 | blame | 历史
key/keyrk3288.jks 补丁 | 查看 | 原始文档 | blame | 历史
key/keysc200.jks 补丁 | 查看 | 原始文档 | blame | 历史
key/keysc626.jks 补丁 | 查看 | 原始文档 | blame | 历史
keystore.properties 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
usbcameralib/build.gradle 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build.gradle.kts
@@ -1,3 +1,6 @@
import java.io.FileInputStream
import java.util.Properties
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
@@ -7,7 +10,9 @@
android {
    namespace = "com.safeluck.floatwindow"
    compileSdk = 35
    val keystorePropertiesFile = rootProject.file("keystore.properties")
    val keystoreProperties =  Properties()
    keystoreProperties.load(FileInputStream(keystorePropertiesFile))
    defaultConfig {
        applicationId = "com.safeluck.floatwindow"
        minSdk = 24
@@ -16,9 +21,27 @@
        versionName = "1.0"
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        ndk {
            abiFilters += listOf("arm64-v8a", "armeabi-v7a")
        }
    }
    buildFeatures{
        aidl =true
    }
    signingConfigs {
        create("release") {
            keyPassword = keystoreProperties.getProperty("keyPassword")
            keyAlias = keystoreProperties.getProperty("keyAlias")
            storeFile = file(keystoreProperties.getProperty("storeFile") ?: "")
            storePassword = keystoreProperties.getProperty("storePassword")
        }
        getByName("debug") {
            keyPassword = keystoreProperties.getProperty("keyPassword")
            keyAlias = keystoreProperties.getProperty("keyAlias")
            storeFile = file(keystoreProperties.getProperty("storeFile") ?: "")
            storePassword = keystoreProperties.getProperty("storePassword")
        }
    }
    buildTypes {
        release {
@@ -27,6 +50,10 @@
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
            signingConfig = signingConfigs.getByName("release")
        }
        debug {
            signingConfig = signingConfigs.getByName("debug")
        }
    }
    compileOptions {
@@ -39,6 +66,15 @@
    buildFeatures {
        compose = true
    }
    packaging {
        jniLibs {
            useLegacyPackaging = false
        }
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}
dependencies {
app/build/generated/aidl_source_output_dir/debug/out/com/safeluck/floatwindow/IMediaAidlInterface.java
@@ -1,6 +1,6 @@
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Using: D:\Program\ Files\Android\Sdk\build-tools\35.0.0\aidl.exe -pD:\Program\ Files\Android\Sdk\platforms\android-35\framework.aidl -oD:\JetBrainsProjects\AndroidProject\anyunVideo\app\build\generated\aidl_source_output_dir\debug\out -ID:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\main\aidl -ID:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\debug\aidl -ID:\data\.gradle\caches\8.10.2\transforms\53a750d70626c759bd7a6dbaf50185ee\transformed\core-1.12.0\aidl -ID:\data\.gradle\caches\8.10.2\transforms\dc945394860d4e1c7d02ff0c8d3e2e6f\transformed\versionedparcelable-1.1.1\aidl -dC:\Users\Dana\AppData\Local\Temp\aidl14282392422309074909.d D:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\main\aidl\com\safeluck\floatwindow\IMediaAidlInterface.aidl
 * Using: D:\Program\ Files\Android\Sdk\build-tools\35.0.0\aidl.exe -pD:\Program\ Files\Android\Sdk\platforms\android-35\framework.aidl -oD:\JetBrainsProjects\AndroidProject\anyunVideo\app\build\generated\aidl_source_output_dir\debug\out -ID:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\main\aidl -ID:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\debug\aidl -ID:\data\.gradle\caches\8.10.2\transforms\53a750d70626c759bd7a6dbaf50185ee\transformed\core-1.12.0\aidl -ID:\data\.gradle\caches\8.10.2\transforms\dc945394860d4e1c7d02ff0c8d3e2e6f\transformed\versionedparcelable-1.1.1\aidl -dC:\Users\Dana\AppData\Local\Temp\aidl10441886692587460682.d D:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\main\aidl\com\safeluck\floatwindow\IMediaAidlInterface.aidl
 */
package com.safeluck.floatwindow;
// Declare any non-default types here with import statements
app/build/generated/aidl_source_output_dir/debug/out/com/safeluck/floatwindow/IMyCallback.java
@@ -1,6 +1,6 @@
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Using: D:\Program\ Files\Android\Sdk\build-tools\35.0.0\aidl.exe -pD:\Program\ Files\Android\Sdk\platforms\android-35\framework.aidl -oD:\JetBrainsProjects\AndroidProject\anyunVideo\app\build\generated\aidl_source_output_dir\debug\out -ID:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\main\aidl -ID:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\debug\aidl -ID:\data\.gradle\caches\8.10.2\transforms\53a750d70626c759bd7a6dbaf50185ee\transformed\core-1.12.0\aidl -ID:\data\.gradle\caches\8.10.2\transforms\dc945394860d4e1c7d02ff0c8d3e2e6f\transformed\versionedparcelable-1.1.1\aidl -dC:\Users\Dana\AppData\Local\Temp\aidl6375257616930721574.d D:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\main\aidl\com\safeluck\floatwindow\IMyCallback.aidl
 * Using: D:\Program\ Files\Android\Sdk\build-tools\35.0.0\aidl.exe -pD:\Program\ Files\Android\Sdk\platforms\android-35\framework.aidl -oD:\JetBrainsProjects\AndroidProject\anyunVideo\app\build\generated\aidl_source_output_dir\debug\out -ID:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\main\aidl -ID:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\debug\aidl -ID:\data\.gradle\caches\8.10.2\transforms\53a750d70626c759bd7a6dbaf50185ee\transformed\core-1.12.0\aidl -ID:\data\.gradle\caches\8.10.2\transforms\dc945394860d4e1c7d02ff0c8d3e2e6f\transformed\versionedparcelable-1.1.1\aidl -dC:\Users\Dana\AppData\Local\Temp\aidl6260459078460591487.d D:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\main\aidl\com\safeluck\floatwindow\IMyCallback.aidl
 */
package com.safeluck.floatwindow;
/**
app/build/intermediates/desugar_graph/debug/dexBuilderDebug/out/currentProject/dirs_bucket_9/graph.bin
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.values.at
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab
@@ -1,2 +1,2 @@
4
5
0
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at
Binary files differ
app/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i
Binary files differ
app/src/main/AndroidManifest.xml
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    android:sharedUserId="android.uid.system"
    xmlns:tools="http://schemas.android.com/tools"
    >
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <application
        android:allowBackup="true"
        android:name=".H264Application"
app/src/main/java/com/safeluck/floatwindow/MainActivity.kt
@@ -1,47 +1,363 @@
package com.safeluck.floatwindow
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.safeluck.floatwindow.ui.theme.AnyunVideoTheme
import timber.log.Timber
class MainActivity : ComponentActivity() {
    private var mediaAidlInterface: IMediaAidlInterface? = null
    private var serviceConnection: ServiceConnection? = null
    private var isServiceBound = false
    private val isServiceBoundState = mutableStateOf(false)
    private val callback = object : IMyCallback.Stub() {
        @Throws(RemoteException::class)
        override fun onResult(re: ResponseVO?) {
            re?.let {
                Timber.d("Callback received: type=${it.type}, errCode=${it.errCode}, message=${it.message}")
            }
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                mediaAidlInterface = IMediaAidlInterface.Stub.asInterface(service)
                isServiceBound = true
                isServiceBoundState.value = true
                Timber.d("FloatingService connected")
            }
            override fun onServiceDisconnected(name: ComponentName?) {
                mediaAidlInterface = null
                isServiceBound = false
                isServiceBoundState.value = false
                Timber.d("FloatingService disconnected")
            }
        }
        setContent {
            AnyunVideoTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        name = "Android",
                        modifier = Modifier.padding(innerPadding)
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    MainScreen(
                        isServiceBound = isServiceBoundState.value,
                        onBindService = {
                            if (!isServiceBoundState.value && serviceConnection != null) {
                                val intent = Intent(this@MainActivity, FloatingService::class.java)
                                bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE)
                                Timber.d("Binding FloatingService")
                            }
                        },
                        onUnbindService = {
                            unbindServiceInternal()
                        },
                        onStartAndroidRecord = { startAndroidRecord() },
                        onStopAndroidRecord = { stopAndroidRecord() },
                        onStartUsbRecord = { startUsbRecord() },
                        onStopUsbRecord = { stopUsbRecord() },
                        onStartUsbPush = { startUsbPush() },
                        onStopUsbPush = { stopUsbPush() }
                    )
                }
            }
        }
    }
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    AnyunVideoTheme {
        Greeting("Android")
    private fun startAndroidRecord() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot start Android record")
            return
        }
        try {
            val mediaArgu = MediaArgu().apply {
                isPush = false
                isUsedOutCamera = false // Android 内置摄像头
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
                recordTime = 0
                tfCardFlag = 0 // 内部存储
            }
            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started Android camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting Android record")
        }
    }
}
    private fun stopAndroidRecord() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot stop Android record")
            return
        }
        try {
            mediaAidlInterface?.stopMedia()
            Timber.d("Stopped Android camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping Android record")
        }
    }
    private fun startUsbRecord() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot start USB record")
            return
        }
        try {
            val mediaArgu = MediaArgu().apply {
                isPush = false
                isUsedOutCamera = true // USB 摄像头
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
                recordTime = 0
                tfCardFlag = 0 // 内部存储
            }
            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started USB camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting USB record")
        }
    }
    private fun stopUsbRecord() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot stop USB record")
            return
        }
        try {
            mediaAidlInterface?.stopMedia()
            Timber.d("Stopped USB camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping USB record")
        }
    }
    private fun startUsbPush() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot start USB push")
            return
        }
        try {
            val mediaArgu = MediaArgu().apply {
                isPush = true
                isUsedOutCamera = true // USB 摄像头
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
                url = "rtmp://your-push-url" // TODO: 需要设置实际的推流地址
                userName = ""
                pwd = ""
            }
            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started USB camera push")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting USB push")
        }
    }
    private fun stopUsbPush() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot stop USB push")
            return
        }
        try {
            mediaAidlInterface?.stopMedia()
            Timber.d("Stopped USB camera push")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping USB push")
        }
    }
    private fun unbindServiceInternal() {
        if (isServiceBound && serviceConnection != null) {
            try {
                mediaAidlInterface?.unregisterCallback(callback)
            } catch (e: RemoteException) {
                Timber.e(e, "Error unregistering callback")
            }
            try {
                unbindService(serviceConnection!!)
                Timber.d("Unbinding FloatingService")
            } catch (e: Exception) {
                Timber.e(e, "Error unbinding service")
            }
            isServiceBound = false
            isServiceBoundState.value = false
            mediaAidlInterface = null
        }
    }
    override fun onDestroy() {
        super.onDestroy()
        unbindServiceInternal()
    }
}
@Composable
fun MainScreen(
    isServiceBound: Boolean,
    onBindService: () -> Unit,
    onUnbindService: () -> Unit,
    onStartAndroidRecord: () -> Unit,
    onStopAndroidRecord: () -> Unit,
    onStartUsbRecord: () -> Unit,
    onStopUsbRecord: () -> Unit,
    onStartUsbPush: () -> Unit,
    onStopUsbPush: () -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        // 固定顶部:标题和状态
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(
                text = "FloatingService 控制",
                style = MaterialTheme.typography.headlineMedium,
                modifier = Modifier.padding(bottom = 8.dp)
            )
            Text(
                text = if (isServiceBound) "服务状态: 已绑定" else "服务状态: 未绑定",
                style = MaterialTheme.typography.bodyMedium,
                color = if (isServiceBound) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error,
                modifier = Modifier.padding(bottom = 16.dp)
            )
        }
        Divider()
        // 可滚动的按钮列表
        Column(
            modifier = Modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
                .padding(horizontal = 16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            Spacer(modifier = Modifier.height(12.dp))
            // 按钮 1: 绑定服务
            Button(
                onClick = onBindService,
                modifier = Modifier.fillMaxWidth(),
                enabled = !isServiceBound
            ) {
                Text("1. 绑定 FloatingService")
            }
            // 按钮 2: 解绑服务
            Button(
                onClick = onUnbindService,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("2. 解绑 FloatingService")
            }
            Divider(modifier = Modifier.padding(vertical = 8.dp))
            // 按钮 3: 开始 Android 相机录像
            Button(
                onClick = onStartAndroidRecord,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("3. 开始 Android 相机录像")
            }
            // 按钮 4: 结束 Android 相机录像
            Button(
                onClick = onStopAndroidRecord,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("4. 结束 Android 相机录像")
            }
            Divider(modifier = Modifier.padding(vertical = 8.dp))
            // 按钮 5: 开始 USB 相机录像
            Button(
                onClick = onStartUsbRecord,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("5. 开始 USB 相机录像")
            }
            // 按钮 6: 结束 USB 相机录像
            Button(
                onClick = onStopUsbRecord,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("6. 结束 USB 相机录像")
            }
            Divider(modifier = Modifier.padding(vertical = 8.dp))
            // 按钮 7: 开始 USB 推流
            Button(
                onClick = onStartUsbPush,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("7. 开始 USB 推流")
            }
            // 按钮 8: 结束 USB 推流
            Button(
                onClick = onStopUsbPush,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("8. 结束 USB 推流")
            }
            Spacer(modifier = Modifier.height(12.dp))
        }
    }
}
app/src/main/java/com/safeluck/floatwindow/MediaArgu.java
@@ -107,6 +107,7 @@
        this.pwd = pwd;
    }
  public  static class ScreenSolution implements Parcelable {
        int width;
        int height;
app/src/main/java/com/safeluck/floatwindow/manager/AndroidCameraRecordManager.java
@@ -186,15 +186,31 @@
            }
            
            // 设置MediaRecorder
            // 注意:MediaRecorder的设置顺序非常重要,必须严格按照以下顺序:
            mediaRecorder = new MediaRecorder();
            // 1. 设置数据源(必须在setOutputFormat之前)
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
            // 2. 设置输出格式(必须在setOutputFile和编码器之前)
            mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            // 3. 设置输出文件(必须在编码器之前)
            mediaRecorder.setOutputFile(currentVideoFile.getAbsolutePath());
            // 4. 设置编码器(必须在setOutputFormat之后)
            mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            // 5. 设置编码参数(必须在编码器之后)
            mediaRecorder.setAudioEncodingBitRate(64000); // 64kbps
            mediaRecorder.setAudioSamplingRate(44100); // 44.1kHz
            mediaRecorder.setVideoEncodingBitRate(width * height * 3);
            mediaRecorder.setVideoFrameRate(20);
            mediaRecorder.setVideoSize(width, height);
            mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            
            // 6. 准备MediaRecorder
            mediaRecorder.prepare();
            
            // 创建Surface
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraRecordManager.java
@@ -70,6 +70,9 @@
    private File currentVideoFile;
    private long currentFileStartTime;
    
    // 录制开始时间(纳秒),用于时间戳同步
    private volatile long recordingStartTimeNs = 0;
    /**
     * 录像回调接口
     */
@@ -165,7 +168,7 @@
            usbCamera.setenv();
            // 使用prepareCamera方法,camera_id范围[0,9]
            int[] cameraIds = {0, 9};
            int[] cameraIds = {0, 2};
            String cameraName = null; // 不指定特定名称
            // 如果返回非0,代表打开失败,则先stopCamera再重试,最多3次
@@ -366,12 +369,18 @@
                    Timber.d("音频录制已启动");
                }
                
                // 注意:不要在这里主动检查音频编码器输出格式
                // 因为 MediaCodec 的 getOutputFormat() 在编码器启动后可能返回 null
                // 应该等待 INFO_OUTPUT_FORMAT_CHANGED 事件
                // 启动音频编码线程
                audioThread = new AudioThread();
                audioThread.start();
                
                Timber.d("开始录像,分辨率: %dx%d", width, height);
                
                // 记录开始时间(纳秒,用于精确时间戳)
                recordingStartTimeNs = System.nanoTime();
                long frameCount = 0;
                long lastFileChangeTime = System.currentTimeMillis();
                
@@ -400,6 +409,9 @@
                            break;
                        }
                        
                        // 重置开始时间
                        recordingStartTimeNs = System.nanoTime();
                        // 重新启动音频录制
                        if (audioRecord != null) {
                            audioRecord.startRecording();
@@ -426,11 +438,13 @@
                    }
                    
                    // 获取YUV数据 (参数0表示录像)
                    usbCamera.rgba(0, buffer);
                    usbCamera.rgba(2, buffer);
                    
                    // 编码并写入文件
                    if (videoEncoder != null && mediaMuxer != null) {
                        encodeFrame(buffer, frameCount, width, height);
                        // 计算实际经过的时间(微秒)
                        long elapsedTimeUs = (System.nanoTime() - recordingStartTimeNs) / 1000;
                        encodeFrame(buffer, elapsedTimeUs, width, height);
                        frameCount++;
                    }
                    
@@ -461,8 +475,12 @@
        
        /**
         * 编码一帧数据
         * @param yuvData YUV数据
         * @param presentationTimeUs 时间戳(微秒),基于实际开始时间
         * @param width 宽度
         * @param height 高度
         */
        private void encodeFrame(byte[] yuvData, long frameIndex, int width, int height) {
        private void encodeFrame(byte[] yuvData, long presentationTimeUs, int width, int height) {
            try {
                // 获取输入缓冲区
                int inputBufferIndex = videoEncoder.dequeueInputBuffer(10000);
@@ -472,7 +490,7 @@
                        inputBuffer.clear();
                        inputBuffer.put(yuvData);
                        
                        long presentationTimeUs = (frameIndex * 1000000) / FRAME_RATE;
                        // 使用实际经过的时间作为时间戳
                        videoEncoder.queueInputBuffer(inputBufferIndex, 0, yuvData.length, 
                                presentationTimeUs, 0);
                    }
@@ -482,18 +500,35 @@
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputBufferIndex = videoEncoder.dequeueOutputBuffer(bufferInfo, 0);
                
                while (outputBufferIndex >= 0) {
                while (outputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
                    if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        // 输出格式改变,添加视频轨道
                        MediaFormat newFormat = videoEncoder.getOutputFormat();
                        videoTrackIndex = mediaMuxer.addTrack(newFormat);
                        checkAndStartMuxer();
                        synchronized (UsbCameraRecordManager.this) {
                            // 检查 muxer 是否已启动,如果已启动则无法添加轨道
                            if (muxerStarted) {
                                Timber.e("Muxer already started, cannot add video track");
                                break;
                            }
                            if (videoTrackIndex < 0) {
                                videoTrackIndex = mediaMuxer.addTrack(newFormat);
                                Timber.d("视频轨道已添加: %d", videoTrackIndex);
                                checkAndStartMuxer();
                            }
                        }
                        outputBufferIndex = videoEncoder.dequeueOutputBuffer(bufferInfo, 0);
                        continue;
                    } else if (outputBufferIndex >= 0) {
                        ByteBuffer outputBuffer = videoEncoder.getOutputBuffer(outputBufferIndex);
                        // 只有在 muxer 已启动且轨道索引有效时才写入数据
                        if (outputBuffer != null && muxerStarted && videoTrackIndex >= 0) {
                            outputBuffer.position(bufferInfo.offset);
                            outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
                            mediaMuxer.writeSampleData(videoTrackIndex, outputBuffer, bufferInfo);
                            try {
                                mediaMuxer.writeSampleData(videoTrackIndex, outputBuffer, bufferInfo);
                            } catch (Exception e) {
                                Timber.e(e, "Error writing video sample data");
                            }
                        }
                        videoEncoder.releaseOutputBuffer(outputBufferIndex, false);
                    }
@@ -512,9 +547,15 @@
     */
    private synchronized void checkAndStartMuxer() {
        if (!muxerStarted && videoTrackIndex >= 0 && audioTrackIndex >= 0) {
            mediaMuxer.start();
            muxerStarted = true;
            try {
                mediaMuxer.start();
                muxerStarted = true;
                Timber.d("Muxer started, video track: %d, audio track: %d", videoTrackIndex, audioTrackIndex);
            } catch (Exception e) {
                Timber.e(e, "Failed to start muxer");
            }
        } else {
            Timber.d("Muxer not started yet, video track: %d, audio track: %d", videoTrackIndex, audioTrackIndex);
        }
    }
    
@@ -535,7 +576,7 @@
            
            try {
                byte[] audioBuffer = new byte[audioBufferSize];
                long audioFrameCount = 0;
                long totalSamplesRead = 0; // 总采样数
                
                while (isRecording && audioRecord != null && audioEncoder != null) {
                    // 读取音频数据
@@ -546,8 +587,8 @@
                    }
                    
                    // 编码音频数据
                    encodeAudio(audioBuffer, readSize, audioFrameCount);
                    audioFrameCount += readSize / 2; // 16位采样,每个采样2字节
                    encodeAudio(audioBuffer, readSize, totalSamplesRead);
                    totalSamplesRead += readSize / 2; // 16位采样,每个采样2字节
                }
                
            } catch (Exception e) {
@@ -559,10 +600,13 @@
        
        /**
         * 编码音频数据
         * @param audioData 音频数据
         * @param size 数据大小(字节)
         * @param totalSamples 总采样数(从开始到现在的累计采样数)
         */
        private void encodeAudio(byte[] audioData, int size, long frameCount) {
        private void encodeAudio(byte[] audioData, int size, long totalSamples) {
            try {
                // 获取输入缓冲区
                // 获取输入缓冲区(即使 muxer 未启动也要编码,以便尽快获得输出格式)
                int inputBufferIndex = audioEncoder.dequeueInputBuffer(10000);
                if (inputBufferIndex >= 0) {
                    ByteBuffer inputBuffer = audioEncoder.getInputBuffer(inputBufferIndex);
@@ -570,7 +614,9 @@
                        inputBuffer.clear();
                        inputBuffer.put(audioData, 0, size);
                        
                        long presentationTimeUs = (frameCount * 1000000) / SAMPLE_RATE;
                        // 使用采样数计算时间戳(微秒)
                        // totalSamples 是采样数,SAMPLE_RATE 是每秒采样数
                        long presentationTimeUs = (totalSamples * 1000000) / SAMPLE_RATE;
                        audioEncoder.queueInputBuffer(inputBufferIndex, 0, size, 
                                presentationTimeUs, 0);
                    }
@@ -580,18 +626,35 @@
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int outputBufferIndex = audioEncoder.dequeueOutputBuffer(bufferInfo, 0);
                
                while (outputBufferIndex >= 0) {
                while (outputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
                    if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        // 输出格式改变,添加音频轨道
                        MediaFormat newFormat = audioEncoder.getOutputFormat();
                        audioTrackIndex = mediaMuxer.addTrack(newFormat);
                        checkAndStartMuxer();
                        synchronized (UsbCameraRecordManager.this) {
                            // 检查 muxer 是否已启动,如果已启动则无法添加轨道
                            if (muxerStarted) {
                                Timber.e("Muxer already started, cannot add audio track");
                                break;
                            }
                            if (audioTrackIndex < 0) {
                                audioTrackIndex = mediaMuxer.addTrack(newFormat);
                                Timber.d("音频轨道已添加: %d", audioTrackIndex);
                                checkAndStartMuxer();
                            }
                        }
                        outputBufferIndex = audioEncoder.dequeueOutputBuffer(bufferInfo, 0);
                        continue;
                    } else if (outputBufferIndex >= 0) {
                        ByteBuffer outputBuffer = audioEncoder.getOutputBuffer(outputBufferIndex);
                        // 只有在 muxer 已启动且轨道索引有效时才写入数据
                        if (outputBuffer != null && muxerStarted && audioTrackIndex >= 0) {
                            outputBuffer.position(bufferInfo.offset);
                            outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
                            mediaMuxer.writeSampleData(audioTrackIndex, outputBuffer, bufferInfo);
                            try {
                                mediaMuxer.writeSampleData(audioTrackIndex, outputBuffer, bufferInfo);
                            } catch (Exception e) {
                                Timber.e(e, "Error writing audio sample data");
                            }
                        }
                        audioEncoder.releaseOutputBuffer(outputBufferIndex, false);
                    }
app/src/main/java/com/safeluck/floatwindow/util/VideoFileUtils.java
@@ -1,6 +1,7 @@
package com.safeluck.floatwindow.util;
import android.content.Context;
import android.os.Environment;
import timber.log.Timber;
@@ -36,7 +37,7 @@
            baseDir = new File(storagePath);
        } else {
            // 内部存储
            baseDir = context.getFilesDir();
            baseDir = Environment.getExternalStorageDirectory();
        }
        
        // 创建 AnYun_VIDEO 目录
key/Verify.txt
New file
@@ -0,0 +1,41 @@
    private static PrivateKey getKey(String key, char[] password) throws Exception {
        byte[] cabuf = new BASE64Decoder().decodeBuffer(key);
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(new ByteArrayInputStream(cabuf), password);
        Enumeration<String> aliases = keyStore.aliases();
        if (!aliases.hasMoreElements()) {
            throw new RuntimeException("no alias found");
        }
        String alias = aliases.nextElement();
        PrivateKey privateKey = (RSAPrivateCrtKeyImpl) keyStore.getKey(alias, password);
        return privateKey;
    }
    public static String sign(String data, PrivateKey key) throws Exception {
//        MessageDigest md = MessageDigest.getInstance("SHA-256");
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(data.getBytes("utf-8"));
        byte[] hash = md.digest();
//        Cipher cipher = Cipher.getInstance("RSA");
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] encrypted = cipher.doFinal(hash);
        return HexBin.encode(encrypted);
    }
    public static String sign(String data, String keyString ,String pwd ) throws Exception {
        char[] password = pwd.toCharArray();
        return sign(data,getKey(keyString,password));
    }
    public static void main(String[] args) {
        try{
            String sign = sign("xuanborobot123456nullnullnull","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","27U777");
            System.out.println(sign);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
key/bjkey.jks
Binary files differ
key/key.jks
Binary files differ
key/key.jks.ori
Binary files differ
key/keyrk3288.jks
Binary files differ
key/keysc200.jks
Binary files differ
key/keysc626.jks
Binary files differ
keystore.properties
New file
@@ -0,0 +1,4 @@
storePassword = 123456
keyPassword = 123456
keyAlias = key0
storeFile = ../key/key.jks
usbcameralib/build.gradle
@@ -16,6 +16,8 @@
            cmake {
                arguments "-DANDROID_ARM_NEON=TRUE"
                cppFlags ""
                // 确保使用 c++_shared STL
                arguments "-DANDROID_STL=c++_shared"
            }
        }
        ndk {
@@ -43,7 +45,9 @@
        pickFirst 'lib/armeabi-v7a/libturbojpeg.so'
        pickFirst 'lib/arm64-v8a/libjpeg.so'
        pickFirst 'lib/arm64-v8a/libturbojpeg.so'
        // 确保 libc++_shared.so 被包含
        pickFirst 'lib/armeabi-v7a/libc++_shared.so'
        pickFirst 'lib/arm64-v8a/libc++_shared.so'
    }
}