1.MainActivity添加按钮 功能
2.内部相机可以录像
3. sdcard/anyun_VIDEO 目录下
4.没有音频
6个文件已添加
6个文件已修改
342 ■■■■■ 已修改文件
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/src/main/AndroidManifest.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/MainActivity.kt 324 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/MediaArgu.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/util/VideoFileUtils.java 3 ●●●● 补丁 | 查看 | 原始文档 | 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 | 历史
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\aidl1635290626695601630.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\aidl7435277295093533370.d D:\JetBrainsProjects\AndroidProject\anyunVideo\app\src\main\aidl\com\safeluck\floatwindow\IMyCallback.aidl
 */
package com.safeluck.floatwindow;
/**
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,335 @@
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.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 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()
        var isServiceBoundState by mutableStateOf(false)
        val serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                mediaAidlInterface = IMediaAidlInterface.Stub.asInterface(service)
                isServiceBoundState = true
                Timber.d("FloatingService connected")
            }
            override fun onServiceDisconnected(name: ComponentName?) {
                mediaAidlInterface = null
                isServiceBoundState = 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,
                        onBindService = {
                            if (!isServiceBoundState) {
                                val intent = Intent(this@MainActivity, FloatingService::class.java)
                                bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
                                Timber.d("Binding FloatingService")
                            }
                        },
                        onUnbindService = {
                            if (isServiceBoundState) {
                                try {
                                    mediaAidlInterface?.unregisterCallback(callback)
                                } catch (e: RemoteException) {
                                    Timber.e(e, "Error unregistering callback")
                                }
                                unbindService(serviceConnection)
                                isServiceBoundState = false
                                mediaAidlInterface = null
                                Timber.d("Unbinding FloatingService")
                            }
                        },
                        onStartAndroidRecord = { startAndroidRecord() },
                        onStopAndroidRecord = { stopAndroidRecord() },
                        onStartUsbRecord = { startUsbRecord() },
                        onStopUsbRecord = { stopUsbRecord() },
                        onStartUsbPush = { startUsbPush() },
                        onStopUsbPush = { stopUsbPush() }
                    )
                }
            }
        }
    }
    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")
        }
    }
    override fun onDestroy() {
        super.onDestroy()
        if (mediaAidlInterface != null) {
            try {
                mediaAidlInterface?.unregisterCallback(callback)
            } catch (e: RemoteException) {
                Timber.e(e, "Error unregistering callback")
            }
        }
    }
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
fun MainScreen(
    isServiceBound: Boolean,
    onBindService: () -> Unit,
    onUnbindService: () -> Unit,
    onStartAndroidRecord: () -> Unit,
    onStopAndroidRecord: () -> Unit,
    onStartUsbRecord: () -> Unit,
    onStopUsbRecord: () -> Unit,
    onStartUsbPush: () -> Unit,
    onStopUsbPush: () -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
    Text(
        text = "Hello $name!",
        modifier = modifier
            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)
        )
        // 按钮 1: 绑定服务
        Button(
            onClick = onBindService,
            modifier = Modifier.fillMaxWidth(),
            enabled = !isServiceBound
        ) {
            Text("1. 绑定 FloatingService")
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    AnyunVideoTheme {
        Greeting("Android")
        // 按钮 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 推流")
        }
    }
}
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/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/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