Dana
2025-12-03 25ac86fe6a8d26dddd41ba7b4a276e6309b1afad
1.sdcard/android/data/包名/files下的h264文件,超过五天删除掉
1个文件已修改
1个文件已添加
156 ■■■■■ 已修改文件
app/src/main/java/com/anyun/h264/H264EncodeService.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
多进程方案使用说明.md 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/anyun/h264/H264EncodeService.java
@@ -36,6 +36,7 @@
    private H264FileTransmitter h264FileTransmitter; // H264文件传输器
    private String outputFileDirectory; // H264文件输出目录
    private WatermarkInfo currentWatermarkInfo; // å½“前水印信息
    private static final int H264_FILE_RETENTION_DAYS = 5; // å¯æ ¹æ®éœ€æ±‚调整为3或5天
    
    // å¤šè¿›ç¨‹æ”¯æŒï¼šç¬¬äºŒä¸ªæ‘„像头的服务连接
    private IH264EncodeService camera2Service;
@@ -78,6 +79,9 @@
        // åˆå§‹åŒ–输出文件目录(使用应用外部存储目录)
        outputFileDirectory = getExternalFilesDir(null).getAbsolutePath();
        Timber.d("Output file directory: %s", outputFileDirectory);
        // æ¸…理过期的H264文件
        cleanupExpiredH264Files(H264_FILE_RETENTION_DAYS);
    }
    
    @Override
@@ -793,6 +797,41 @@
            currentWatermarkInfo = null;
        }
    }
    /**
     * åˆ é™¤è¶…过保留期的H264文件
     */
    private void cleanupExpiredH264Files(int retentionDays) {
        if (outputFileDirectory == null) {
            Timber.w("cleanupExpiredH264Files: outputFileDirectory is null");
            return;
        }
        File dir = new File(outputFileDirectory);
        if (!dir.exists() || !dir.isDirectory()) {
            Timber.w("cleanupExpiredH264Files: directory invalid -> %s", outputFileDirectory);
            return;
        }
        long retentionMillis = TimeUnit.DAYS.toMillis(Math.max(1, retentionDays));
        long cutoffTime = System.currentTimeMillis() - retentionMillis;
        File[] files = dir.listFiles((d, name) -> name.toLowerCase(Locale.CHINA).endsWith(".h264"));
        if (files == null || files.length == 0) {
            return;
        }
        for (File file : files) {
            if (file.lastModified() < cutoffTime) {
                boolean deleted = file.delete();
                if (deleted) {
                    Timber.i("Deleted expired H264 file: %s", file.getAbsolutePath());
                } else {
                    Timber.w("Failed to delete expired H264 file: %s", file.getAbsolutePath());
                }
            }
        }
    }
    
    /**
     * ä»Žæ–‡ä»¶åˆ›å»ºèµ„源信息(如果文件在时间范围内)
¶à½ø³Ì·½°¸Ê¹ÓÃ˵Ã÷.md
New file
@@ -0,0 +1,117 @@
# å¤šè¿›ç¨‹æ–¹æ¡ˆä½¿ç”¨è¯´æ˜Ž
## æ¦‚è¿°
本方案通过多进程实现两个USB摄像头同时工作。每个摄像头运行在独立的进程中,每个进程拥有独立的H264Encoder实例,从而避免了底层C库的单实例限制。
## æž¶æž„说明
- **H264EncodeService**:主进程服务,处理第一个摄像头(cameraId=1)
- **H264EncodeService2**:独立进程服务(`:camera2`),处理第二个摄像头(cameraId=2)
## ä½¿ç”¨æ–¹æ³•
### å¯åŠ¨ç¬¬ä¸€ä¸ªæ‘„åƒå¤´ï¼ˆcameraId=1)
```json
{
  "width": 640,
  "height": 480,
  "framerate": 25,
  "cameraId": 1
}
```
或者不指定cameraId(默认为1):
```json
{
  "width": 640,
  "height": 480,
  "framerate": 25
}
```
### å¯åŠ¨ç¬¬äºŒä¸ªæ‘„åƒå¤´ï¼ˆcameraId=2)
```json
{
  "width": 640,
  "height": 480,
  "framerate": 25,
  "cameraId": 2
}
```
### ç½‘络推送示例
**第一个摄像头:**
```json
{
  "ip": "192.168.1.100",
  "port": 8888,
  "width": 640,
  "height": 480,
  "framerate": 25,
  "simPhone": "013120122580",
  "cameraId": 1
}
```
**第二个摄像头:**
```json
{
  "ip": "192.168.1.100",
  "port": 8889,
  "width": 640,
  "height": 480,
  "framerate": 25,
  "simPhone": "013120122580",
  "cameraId": 2
}
```
### åœæ­¢æŒ‡å®šæ‘„像头
停止第一个摄像头:
```json
{
  "cameraId": 1
}
```
停止第二个摄像头:
```json
{
  "cameraId": 2
}
```
如果不指定cameraId,默认停止第一个摄像头。
## å…³é”®ç‰¹æ€§
1. **进程隔离**:两个摄像头运行在完全独立的进程中,互不干扰
2. **自动路由**:主服务根据cameraId自动路由到对应的进程
3. **独立配置**:每个摄像头可以独立配置分辨率、帧率、网络参数等
4. **独立文件**:第二个摄像头的文件会添加`camera2`标识,便于区分
5. **独立通道**:第二个摄像头使用`logicalChannelId=2`,第一个使用`logicalChannelId=1`
## æ–‡ä»¶å‘½å
- ç¬¬ä¸€ä¸ªæ‘„像头:`h264_1234567890123.h264`
- ç¬¬äºŒä¸ªæ‘„像头:`h264_camera2_1234567890123.h264`
## æ³¨æ„äº‹é¡¹
1. ç¡®ä¿ä¸¤ä¸ªUSB摄像头都已正确连接
2. ç¬¬äºŒä¸ªæ‘„像头固定使用cameraId=2,第一个摄像头使用cameraId=1(或不指定)
3. ä¸¤ä¸ªè¿›ç¨‹å¯ä»¥åŒæ—¶è¿è¡Œï¼Œäº’不影响
4. æ¯ä¸ªè¿›ç¨‹éƒ½æœ‰ç‹¬ç«‹çš„H264Encoder实例,因此可以真正并发工作
## æŠ€æœ¯å®žçް
- ä¸»æœåŠ¡é€šè¿‡AIDL绑定到第二个进程的服务
- ç¬¬äºŒä¸ªè¿›ç¨‹é€šè¿‡`android:process=":camera2"`配置在独立进程中运行
- æ‰€æœ‰æ“ä½œéƒ½é€šè¿‡AIDL接口进行进程间通信