Dana
6 天以前 597ab1d3451e000597ba3e12749f087f6b45a328
1.p2摄像头 跨进程服务,未测试
1个文件已添加
3个文件已修改
360 ■■■■■ 已修改文件
app/src/main/aidl/com/safeluck/floatwindow/IMediaAidlInterface.aidl 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/MainActivity.kt 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/P2UsbCameraVideoService.java 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraPushManager.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/aidl/com/safeluck/floatwindow/IMediaAidlInterface.aidl
@@ -15,7 +15,7 @@
//            void transferInfo(String info);
            void startMedia(in MediaArgu media);
            void stopMedia();
            void stopMedia(in MediaArgu media);
            void registerCallback(in IMyCallback cb);
            void unregisterCallback(in IMyCallback cb);
}
app/src/main/java/com/safeluck/floatwindow/MainActivity.kt
@@ -80,7 +80,11 @@
                        onStartUsbRecord = { startUsbRecord() },
                        onStopUsbRecord = { stopUsbRecord() },
                        onStartUsbPush = { startUsbPush() },
                        onStopUsbPush = { stopUsbPush() }
                        onStopUsbPush = { stopUsbPush() },
                        onStartP2Push = { startP2Push() },
                        onStopP2Push = { stopP2Push() },
                        onStartP2Record = { startP2Record() },
                        onStopP2Record = { stopP2Record() }
                    )
                }
            }
@@ -119,7 +123,12 @@
        }
        
        try {
            mediaAidlInterface?.stopMedia()
            val mediaArgu = MediaArgu().apply {
                isPush = false
                isUsedOutCamera = false // Android 内置摄像头
                usbCameraId = 0 // Android 相机,usbCameraId 为 0
            }
            mediaAidlInterface?.stopMedia(mediaArgu)
            Timber.d("Stopped Android camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping Android record")
@@ -136,6 +145,7 @@
            val mediaArgu = MediaArgu().apply {
                isPush = false
                isUsedOutCamera = true // USB 摄像头
                usbCameraId = 1 // P1 USB 摄像头
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
@@ -145,7 +155,7 @@
            
            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started USB camera record")
            Timber.d("Started USB camera record (P1)")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting USB record")
        }
@@ -158,8 +168,13 @@
        }
        
        try {
            mediaAidlInterface?.stopMedia()
            Timber.d("Stopped USB camera record")
            val mediaArgu = MediaArgu().apply {
                isPush = false
                isUsedOutCamera = true // USB 摄像头
                usbCameraId = 1 // P1 USB 摄像头
            }
            mediaAidlInterface?.stopMedia(mediaArgu)
            Timber.d("Stopped USB camera record (P1)")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping USB record")
        }
@@ -173,6 +188,7 @@
        
        try {
            val mediaArgu = MediaArgu().apply {
                usbCameraId = 1 // P1 USB 摄像头
                isPush = true
                isUsedOutCamera = true // USB 摄像头
                codeRate = 0
@@ -185,7 +201,7 @@
            
            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started USB camera push")
            Timber.d("Started USB camera push (P1)")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting USB push")
        }
@@ -198,10 +214,106 @@
        }
        
        try {
            mediaAidlInterface?.stopMedia()
            Timber.d("Stopped USB camera push")
            val mediaArgu = MediaArgu().apply {
                usbCameraId = 1 // P1 USB 摄像头
                isPush = true
                isUsedOutCamera = true // USB 摄像头
            }
            mediaAidlInterface?.stopMedia(mediaArgu)
            Timber.d("Stopped USB camera push (P1)")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping USB push")
        }
    }
    private fun startP2Push() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot start P2 push")
            return
        }
        try {
            val mediaArgu = MediaArgu().apply {
                usbCameraId = 2 // P2 USB 摄像头
                isPush = true
                isUsedOutCamera = true // USB 摄像头
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
                url = "rtmp://192.168.16.143/live/livestream" // TODO: 需要设置实际的推流地址
                userName = ""
                pwd = ""
            }
            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started P2 USB camera push")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting P2 push")
        }
    }
    private fun stopP2Push() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot stop P2 push")
            return
        }
        try {
            val mediaArgu = MediaArgu().apply {
                usbCameraId = 2 // P2 USB 摄像头
                isPush = true
                isUsedOutCamera = true // USB 摄像头
            }
            mediaAidlInterface?.stopMedia(mediaArgu)
            Timber.d("Stopped P2 USB camera push")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping P2 push")
        }
    }
    private fun startP2Record() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot start P2 record")
            return
        }
        try {
            val mediaArgu = MediaArgu().apply {
                isPush = false
                isUsedOutCamera = true // USB 摄像头
                usbCameraId = 2 // P2 USB 摄像头
                codeRate = 0
                frameRate = 0
                m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
                recordTime = 0
                tfCardFlag = 0 // 内部存储
            }
            mediaAidlInterface?.registerCallback(callback)
            mediaAidlInterface?.startMedia(mediaArgu)
            Timber.d("Started P2 USB camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error starting P2 record")
        }
    }
    private fun stopP2Record() {
        if (mediaAidlInterface == null) {
            Timber.w("Service not bound, cannot stop P2 record")
            return
        }
        try {
            val mediaArgu = MediaArgu().apply {
                isPush = false
                isUsedOutCamera = true // USB 摄像头
                usbCameraId = 2 // P2 USB 摄像头
            }
            mediaAidlInterface?.stopMedia(mediaArgu)
            Timber.d("Stopped P2 USB camera record")
        } catch (e: RemoteException) {
            Timber.e(e, "Error stopping P2 record")
        }
    }
    
@@ -240,7 +352,11 @@
    onStartUsbRecord: () -> Unit,
    onStopUsbRecord: () -> Unit,
    onStartUsbPush: () -> Unit,
    onStopUsbPush: () -> Unit
    onStopUsbPush: () -> Unit,
    onStartP2Push: () -> Unit,
    onStopP2Push: () -> Unit,
    onStartP2Record: () -> Unit,
    onStopP2Record: () -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize()
@@ -339,22 +455,62 @@
            
            Divider(modifier = Modifier.padding(vertical = 8.dp))
            
            // 按钮 7: 开始 USB 推流
            // 按钮 7: 开始 USB 推流 (P1)
            Button(
                onClick = onStartUsbPush,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("7. 开始 USB 推流")
                Text("7. 开始 USB 推流 (P1)")
            }
            
            // 按钮 8: 结束 USB 推流
            // 按钮 8: 结束 USB 推流 (P1)
            Button(
                onClick = onStopUsbPush,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("8. 结束 USB 推流")
                Text("8. 结束 USB 推流 (P1)")
            }
            Divider(modifier = Modifier.padding(vertical = 8.dp))
            // 按钮 9: 开始 P2 USB 推流
            Button(
                onClick = onStartP2Push,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("9. 开始 P2 USB 推流")
            }
            // 按钮 10: 结束 P2 USB 推流
            Button(
                onClick = onStopP2Push,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("10. 结束 P2 USB 推流")
            }
            Divider(modifier = Modifier.padding(vertical = 8.dp))
            // 按钮 11: 开始 P2 USB 录像
            Button(
                onClick = onStartP2Record,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("11. 开始 P2 USB 录像")
            }
            // 按钮 12: 结束 P2 USB 录像
            Button(
                onClick = onStopP2Record,
                modifier = Modifier.fillMaxWidth(),
                enabled = isServiceBound
            ) {
                Text("12. 结束 P2 USB 录像")
            }
            
            Spacer(modifier = Modifier.height(12.dp))
app/src/main/java/com/safeluck/floatwindow/P2UsbCameraVideoService.java
New file
@@ -0,0 +1,172 @@
package com.safeluck.floatwindow;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import com.safeluck.floatwindow.manager.UsbCameraPushManager;
import com.safeluck.floatwindow.manager.UsbCameraRecordManager;
import timber.log.Timber;
/**
 * 运行在独立进程(:p2)的 USB 摄像头视频服务,用于同时支持两路 USB 摄像头工作。
 * 约定:当 MediaArgu.usbCameraId == 2 时,调用方应使用该服务进行推流/录像。
 */
public class P2UsbCameraVideoService extends Service {
    private static final String TAG = "P2UsbCameraVideoService";
    private Context context;
    private UsbCameraPushManager usbCameraPushManager;
    private UsbCameraRecordManager usbCameraRecordManager;
    private ManagerType currentManagerType = ManagerType.NONE;
    private enum ManagerType {
        NONE,
        USB_PUSH,
        USB_RECORD
    }
    private final RemoteCallbackList<IMyCallback> mCallbacks = new RemoteCallbackList<>();
    private final IMediaAidlInterface.Stub mBinder = new IMediaAidlInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) {
            Timber.d("[%s] basicTypes called", TAG);
        }
        @Override
        public void startMedia(MediaArgu media) throws RemoteException {
            Timber.d("[%s] startMedia called via AIDL", TAG);
            P2UsbCameraVideoService.this.startMedia(media);
        }
        @Override
        public void stopMedia(MediaArgu media) throws RemoteException {
            Timber.d("[%s] stopMedia called via AIDL", TAG);
            P2UsbCameraVideoService.this.stopMedia(media);
        }
        @Override
        public void registerCallback(IMyCallback cb) throws RemoteException {
            if (cb != null) {
                mCallbacks.register(cb);
                Timber.d("[%s] Callback registered", TAG);
            }
        }
        @Override
        public void unregisterCallback(IMyCallback cb) throws RemoteException {
            if (cb != null) {
                mCallbacks.unregister(cb);
                Timber.d("[%s] Callback unregistered", TAG);
            }
        }
    };
    @Override
    public void onCreate() {
        super.onCreate();
        context = this;
        usbCameraPushManager = new UsbCameraPushManager(context);
        usbCameraPushManager.setCallback(this::notifyCallback);
        usbCameraRecordManager = new UsbCameraRecordManager(context);
        usbCameraRecordManager.setCallback(this::notifyCallback);
        Timber.d("[%s] onCreate", TAG);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }
    @Override
    public IBinder onBind(Intent intent) {
        Timber.d("[%s] onBind", TAG);
        return mBinder;
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        stopCurrentManager();
        mCallbacks.kill();
        Timber.d("[%s] onDestroy", TAG);
    }
    private void startMedia(MediaArgu media) {
        if (media == null) {
            notifyCallback(1, -1, "MediaArgu is null");
            return;
        }
        // 该服务只处理 USB 摄像头
        if (!media.isUsedOutCamera()) {
            notifyCallback(1, -4, "P2 service only supports USB camera");
            return;
        }
        // 先停止当前
        stopCurrentManager();
        if (media.isPush()) {
            currentManagerType = ManagerType.USB_PUSH;
            usbCameraPushManager.startPush(media);
        } else {
            currentManagerType = ManagerType.USB_RECORD;
            usbCameraRecordManager.startRecord(media);
        }
    }
    private void stopMedia(MediaArgu media) {
        stopCurrentManager();
    }
    private void stopCurrentManager() {
        switch (currentManagerType) {
            case USB_PUSH:
                if (usbCameraPushManager != null) {
                    usbCameraPushManager.stopPush();
                }
                break;
            case USB_RECORD:
                if (usbCameraRecordManager != null) {
                    usbCameraRecordManager.stopRecord();
                }
                break;
            case NONE:
                break;
        }
        currentManagerType = ManagerType.NONE;
    }
    private void notifyCallback(ResponseVO response) {
        if (response == null) return;
        int count = mCallbacks.beginBroadcast();
        for (int i = 0; i < count; i++) {
            try {
                mCallbacks.getBroadcastItem(i).onResult(response);
            } catch (RemoteException e) {
                Timber.e(e, "[%s] Error notifying callback", TAG);
            }
        }
        mCallbacks.finishBroadcast();
    }
    private void notifyCallback(int type, int errCode, String message) {
        ResponseVO response = new ResponseVO();
        response.setType(type);
        response.setErrCode(errCode);
        response.setMessage(message);
        notifyCallback(response);
    }
}
app/src/main/java/com/safeluck/floatwindow/manager/UsbCameraPushManager.java
@@ -591,9 +591,9 @@
            int usbId = (mediaArgu != null) ? mediaArgu.getUsbCameraId() : 0;
            int[] cameraIds;
            if (usbId == 2) {
                cameraIds = new int[]{2};
                cameraIds = new int[]{2,3};
            } else if (usbId == 1) {
                cameraIds = new int[]{0};
                cameraIds = new int[]{1,2};
            } else {
                cameraIds = new int[]{0, 2};
            }