| | |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.ArrayList; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | |
| | | private H264FileTransmitter h264FileTransmitter; // H264文件传输器 |
| | | private String outputFileDirectory; // H264文件输出目录 |
| | | private WatermarkInfo currentWatermarkInfo; // 当前水印信息 |
| | | private boolean currentUseTFCard = false; // 当前是否使用TF卡配置 |
| | | |
| | | // 默认编码参数 |
| | | private static final int DEFAULT_WIDTH = 640; |
| | |
| | | // 设置 Context(用于清理 TF 卡文件) |
| | | h264Encoder.setContext(this); |
| | | |
| | | // 保存当前useTFCard配置 |
| | | currentUseTFCard = config != null && config.useTFCard; |
| | | |
| | | // 设置编码参数(使用配置中的参数) |
| | | // 设置编码参数(使用配置中的参数) |
| | | int width = config != null && config.width > 0 ? config.width : DEFAULT_WIDTH; |
| | |
| | | |
| | | // 设置 Context(用于清理 TF 卡文件) |
| | | h264Encoder.setContext(this); |
| | | |
| | | // 保存当前useTFCard配置 |
| | | currentUseTFCard = config != null && config.useTFCard; |
| | | |
| | | // 设置编码参数(使用配置中的参数) |
| | | int width = config != null && config.width > 0 ? config.width : DEFAULT_WIDTH; |
| | |
| | | } |
| | | |
| | | try { |
| | | // 检查文件是否存在 |
| | | File file = new File(config.filePath); |
| | | // 解析待传输文件路径,若不存在则尝试到TF卡目录按日期查找 |
| | | String resolvedFilePath = config.filePath; |
| | | File file = new File(resolvedFilePath); |
| | | if (!file.exists() || !file.isFile()) { |
| | | Timber.e("File does not exist: %s (camera2)", config.filePath); |
| | | return 1; // 失败 |
| | | Timber.w("File does not exist, try TF card lookup (camera2): %s", resolvedFilePath); |
| | | |
| | | String fileName = file.getName(); |
| | | String timestampStr = null; |
| | | // camera2的文件名格式:h264_camera2_1234567890123.h264 |
| | | if (fileName.startsWith("h264_camera2_") && fileName.endsWith(".h264")) { |
| | | timestampStr = fileName.substring(14, fileName.length() - 5); // 去掉 "h264_camera2_" 和 ".h264" |
| | | } |
| | | |
| | | if (timestampStr == null || timestampStr.trim().isEmpty()) { |
| | | Timber.e("Cannot parse timestamp from file name (camera2): %s", fileName); |
| | | return 1; // 失败 |
| | | } |
| | | |
| | | try { |
| | | long timestamp = Long.parseLong(timestampStr); |
| | | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd", Locale.CHINA); |
| | | String dateDir = dateFormat.format(new Date(timestamp)); |
| | | |
| | | String storagePath = FileUtil.getStoragePath(this, true); |
| | | if (storagePath == null || storagePath.trim().isEmpty()) { |
| | | Timber.e("TF card storage path not available when searching file (camera2)"); |
| | | return 1; // 失败 |
| | | } |
| | | |
| | | File tfRoot = new File(storagePath, "h264"); |
| | | File candidate = new File(new File(tfRoot, dateDir), fileName); |
| | | if (candidate.exists() && candidate.isFile()) { |
| | | resolvedFilePath = candidate.getAbsolutePath(); |
| | | file = candidate; |
| | | Timber.i("Found file on TF card (camera2): %s", resolvedFilePath); |
| | | } else { |
| | | Timber.e("File not found on TF card path (camera2): %s", candidate.getAbsolutePath()); |
| | | return 1; // 失败 |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | Timber.e(e, "Failed to parse timestamp from file name (camera2): %s", fileName); |
| | | return 1; // 失败 |
| | | } |
| | | } |
| | | |
| | | // 创建文件传输器 |
| | |
| | | } |
| | | |
| | | // 开始传输文件 |
| | | h264FileTransmitter.transmitFile(config.filePath); |
| | | h264FileTransmitter.transmitFile(resolvedFilePath); |
| | | |
| | | Timber.d("File transmit started successfully (camera2), file: %s, server: %s:%d, protocol: %s, framerate: %d", |
| | | config.filePath, config.ip, config.port, |
| | | resolvedFilePath, config.ip, config.port, |
| | | config.protocolType == JT1076ProtocolHelper.PROTOCOL_TYPE_UDP ? "UDP" : "TCP", framerate); |
| | | return 0; // 成功 |
| | | |
| | |
| | | * 获取资源列表(根据JT/T 1076-2016表23定义) |
| | | */ |
| | | private List<ResourceInfo> getResourceList(String startTime, String endTime) { |
| | | Timber.d("getResourceList called (camera2), startTime: %s, endTime: %s", startTime, endTime); |
| | | Timber.d("getResourceList called (camera2), startTime: %s, endTime: %s, useTFCard: %b", startTime, endTime, currentUseTFCard); |
| | | |
| | | List<ResourceInfo> resourceList = new ArrayList<>(); |
| | | |
| | | try { |
| | | // 扫描输出目录中的H264文件(只查找camera2的文件) |
| | | File dir = new File(outputFileDirectory); |
| | | if (!dir.exists() || !dir.isDirectory()) { |
| | | Timber.w("Output directory does not exist: %s", outputFileDirectory); |
| | | return resourceList; |
| | | } |
| | | |
| | | File[] files = dir.listFiles((dir1, name) -> |
| | | name.toLowerCase().endsWith(".h264") && name.contains("camera2")); |
| | | if (files == null || files.length == 0) { |
| | | Timber.d("No H264 files found for camera2 in directory"); |
| | | return resourceList; |
| | | } |
| | | |
| | | // 解析时间范围 |
| | | Date startDate = parseTime(startTime); |
| | | Date endDate = parseTime(endTime); |
| | | |
| | | if (startDate == null || endDate == null) { |
| | | Timber.e("Invalid time format, startTime: %s, endTime: %s", startTime, endTime); |
| | | return resourceList; |
| | | } |
| | | |
| | | if (currentUseTFCard) { |
| | | // 使用TF卡:扫描TF卡上的h264文件夹,根据日期范围过滤 |
| | | String storagePath = FileUtil.getStoragePath(this, true); |
| | | if (storagePath == null || storagePath.trim().isEmpty()) { |
| | | Timber.w("TF card storage path not available, fallback to app directory"); |
| | | // 回退到应用目录 |
| | | return getResourceListFromDirectory(outputFileDirectory, startDate, endDate, true); |
| | | } |
| | | |
| | | File externalStorage = new File(storagePath); |
| | | if (!externalStorage.exists()) { |
| | | Timber.w("TF card storage directory does not exist: %s, fallback to app directory", storagePath); |
| | | // 回退到应用目录 |
| | | return getResourceListFromDirectory(outputFileDirectory, startDate, endDate, true); |
| | | } |
| | | |
| | | // TF卡上的h264文件夹路径:/sdcard/h264/ |
| | | File h264Dir = new File(externalStorage, "h264"); |
| | | if (!h264Dir.exists() || !h264Dir.isDirectory()) { |
| | | Timber.w("TF card h264 directory does not exist: %s", h264Dir.getAbsolutePath()); |
| | | return resourceList; |
| | | } |
| | | |
| | | // 获取日期范围内的所有日期文件夹 |
| | | List<String> dateDirs = getDateDirectoriesInRange(startDate, endDate); |
| | | Timber.d("Found %d date directories in range", dateDirs.size()); |
| | | |
| | | // 扫描每个日期文件夹下的h264文件(只查找camera2的文件) |
| | | for (String dateDir : dateDirs) { |
| | | File dateDirFile = new File(h264Dir, dateDir); |
| | | if (dateDirFile.exists() && dateDirFile.isDirectory()) { |
| | | List<ResourceInfo> dateResources = getResourceListFromDirectory( |
| | | dateDirFile.getAbsolutePath(), startDate, endDate, true); |
| | | resourceList.addAll(dateResources); |
| | | } |
| | | } |
| | | |
| | | Timber.d("Found %d resources for camera2 in TF card time range", resourceList.size()); |
| | | return resourceList; |
| | | } else { |
| | | // 不使用TF卡:扫描应用目录(只查找camera2的文件) |
| | | return getResourceListFromDirectory(outputFileDirectory, startDate, endDate, true); |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | Timber.e(e, "Error getting resource list (camera2)"); |
| | | return resourceList; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 从指定目录扫描H264文件并创建资源列表 |
| | | * @param directoryPath 目录路径 |
| | | * @param startDate 开始日期 |
| | | * @param endDate 结束日期 |
| | | * @param camera2Only 是否只查找camera2的文件 |
| | | * @return 资源列表 |
| | | */ |
| | | private List<ResourceInfo> getResourceListFromDirectory(String directoryPath, Date startDate, Date endDate, boolean camera2Only) { |
| | | List<ResourceInfo> resourceList = new ArrayList<>(); |
| | | |
| | | try { |
| | | File dir = new File(directoryPath); |
| | | if (!dir.exists() || !dir.isDirectory()) { |
| | | Timber.w("Directory does not exist: %s", directoryPath); |
| | | return resourceList; |
| | | } |
| | | |
| | | File[] files = dir.listFiles((dir1, name) -> { |
| | | boolean isH264 = name.toLowerCase().endsWith(".h264"); |
| | | if (camera2Only) { |
| | | return isH264 && name.contains("camera2"); |
| | | } else { |
| | | return isH264; |
| | | } |
| | | }); |
| | | if (files == null || files.length == 0) { |
| | | Timber.d("No H264 files found in directory: %s", directoryPath); |
| | | return resourceList; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | Timber.d("Found %d resources for camera2 in time range", resourceList.size()); |
| | | return resourceList; |
| | | |
| | | } catch (Exception e) { |
| | | Timber.e(e, "Error getting resource list (camera2)"); |
| | | Timber.e(e, "Error getting resource list from directory: %s", directoryPath); |
| | | return resourceList; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取日期范围内的所有日期文件夹名称列表(格式:yyyyMMdd) |
| | | * @param startDate 开始日期 |
| | | * @param endDate 结束日期 |
| | | * @return 日期文件夹名称列表 |
| | | */ |
| | | private List<String> getDateDirectoriesInRange(Date startDate, Date endDate) { |
| | | List<String> dateDirs = new ArrayList<>(); |
| | | |
| | | try { |
| | | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd", Locale.CHINA); |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.setTime(startDate); |
| | | calendar.set(Calendar.HOUR_OF_DAY, 0); |
| | | calendar.set(Calendar.MINUTE, 0); |
| | | calendar.set(Calendar.SECOND, 0); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | |
| | | Date currentDate = calendar.getTime(); |
| | | Date endDateOnly = new Date(endDate.getTime()); |
| | | Calendar endCalendar = Calendar.getInstance(); |
| | | endCalendar.setTime(endDateOnly); |
| | | endCalendar.set(Calendar.HOUR_OF_DAY, 23); |
| | | endCalendar.set(Calendar.MINUTE, 59); |
| | | endCalendar.set(Calendar.SECOND, 59); |
| | | endCalendar.set(Calendar.MILLISECOND, 999); |
| | | endDateOnly = endCalendar.getTime(); |
| | | |
| | | // 遍历从开始日期到结束日期的所有日期 |
| | | while (!currentDate.after(endDateOnly)) { |
| | | String dateDir = dateFormat.format(currentDate); |
| | | dateDirs.add(dateDir); |
| | | |
| | | // 增加一天 |
| | | calendar.add(Calendar.DAY_OF_MONTH, 1); |
| | | currentDate = calendar.getTime(); |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | Timber.e(e, "Error getting date directories in range"); |
| | | } |
| | | |
| | | return dateDirs; |
| | | } |
| | | |
| | | /** |
| | | * 设置水印信息 |
| | | */ |
| | | private void setWatermarkInfo(String watermarkInfoJson) { |