package com.anyun.h264;
|
|
import android.app.Service;
|
import android.content.Intent;
|
import android.os.IBinder;
|
import android.os.RemoteException;
|
import timber.log.Timber;
|
|
import com.anyun.h264.model.ResourceInfo;
|
import com.anyun.h264.model.WatermarkInfo;
|
|
import org.json.JSONException;
|
import org.json.JSONObject;
|
|
import java.io.File;
|
import java.text.ParseException;
|
import java.text.SimpleDateFormat;
|
import java.util.ArrayList;
|
import java.util.Date;
|
import java.util.List;
|
import java.util.Locale;
|
|
/**
|
* H264编码服务
|
* 提供AIDL接口供客户端调用,用于控制H264编码和查询资源列表
|
*/
|
public class H264EncodeService extends Service {
|
private static final String TAG = "H264EncodeService";
|
|
private H264Encoder h264Encoder;
|
private H264FileTransmitter h264FileTransmitter; // H264文件传输器
|
private String outputFileDirectory; // H264文件输出目录
|
private WatermarkInfo currentWatermarkInfo; // 当前水印信息
|
|
// 默认编码参数
|
private static final int DEFAULT_WIDTH = 640;
|
private static final int DEFAULT_HEIGHT = 480;
|
private static final int DEFAULT_FRAME_RATE = 25;
|
private static final int DEFAULT_BITRATE = 2000000; // 2Mbps
|
|
// 默认摄像头参数
|
private static final int[] DEFAULT_CAMERA_ID_RANGE = {1, 2};
|
private static final int[] DEFAULT_RESOLUTION = {640, 480};
|
|
// AIDL接口实现
|
private final IH264EncodeService.Stub binder = new IH264EncodeService.Stub() {
|
@Override
|
public int controlEncode(int action, String jsonConfig) throws RemoteException {
|
return H264EncodeService.this.controlEncode(action, jsonConfig);
|
}
|
|
@Override
|
public List<ResourceInfo> getResourceList(String startTime, String endTime) throws RemoteException {
|
return H264EncodeService.this.getResourceList(startTime, endTime);
|
}
|
|
@Override
|
public void setWatermarkInfo(String watermarkInfo) throws RemoteException {
|
H264EncodeService.this.setWatermarkInfo(watermarkInfo);
|
}
|
};
|
|
@Override
|
public void onCreate() {
|
super.onCreate();
|
Timber.d("H264EncodeService created");
|
|
// 初始化输出文件目录(使用应用外部存储目录)
|
outputFileDirectory = getExternalFilesDir(null).getAbsolutePath();
|
Timber.d("Output file directory: %s", outputFileDirectory);
|
}
|
|
@Override
|
public IBinder onBind(Intent intent) {
|
Timber.d("Service bound");
|
return binder;
|
}
|
|
@Override
|
public boolean onUnbind(Intent intent) {
|
Timber.d("Service unbound");
|
// 不自动停止编码器,让它在服务中保持运行
|
return super.onUnbind(intent);
|
}
|
|
@Override
|
public void onDestroy() {
|
super.onDestroy();
|
Timber.d("Service destroyed");
|
|
// 停止并释放编码器和文件传输器
|
stopEncoder();
|
stopFileTransmitter();
|
}
|
|
/**
|
* 编码配置类
|
*/
|
private static class EncodeConfig {
|
String ip;
|
int port;
|
int width;
|
int height;
|
int framerate;
|
String simPhone;
|
|
// 从JSON解析配置
|
static EncodeConfig fromJson(String jsonConfig) throws JSONException {
|
EncodeConfig config = new EncodeConfig();
|
if (jsonConfig == null || jsonConfig.trim().isEmpty()) {
|
// 使用默认值
|
config.width = DEFAULT_WIDTH;
|
config.height = DEFAULT_HEIGHT;
|
config.framerate = DEFAULT_FRAME_RATE;
|
config.ip = null;
|
config.port = 0;
|
config.simPhone = null;
|
return config;
|
}
|
|
JSONObject json = new JSONObject(jsonConfig);
|
config.width = json.optInt("width", DEFAULT_WIDTH);
|
config.height = json.optInt("height", DEFAULT_HEIGHT);
|
config.framerate = json.optInt("framerate", DEFAULT_FRAME_RATE);
|
config.ip = json.optString("ip", null);
|
config.port = json.optInt("port", 0);
|
config.simPhone = json.optString("simPhone", null);
|
|
return config;
|
}
|
}
|
|
/**
|
* 文件传输配置类
|
*/
|
private static class FileTransmitConfig {
|
String ip;
|
int port;
|
int framerate;
|
String simPhone;
|
String filePath; // H264文件路径
|
int protocolType; // 协议类型:1-UDP,2-TCP
|
|
// 从JSON解析配置
|
static FileTransmitConfig fromJson(String jsonConfig) throws JSONException {
|
FileTransmitConfig config = new FileTransmitConfig();
|
if (jsonConfig == null || jsonConfig.trim().isEmpty()) {
|
throw new JSONException("File transmit config cannot be empty");
|
}
|
|
JSONObject json = new JSONObject(jsonConfig);
|
config.ip = json.optString("ip", null);
|
config.port = json.optInt("port", 0);
|
config.framerate = json.optInt("framerate", DEFAULT_FRAME_RATE);
|
config.simPhone = json.optString("simPhone", "013120122580");
|
config.filePath = json.optString("filePath", null);
|
// 协议类型:默认TCP(2),1-UDP,2-TCP
|
config.protocolType = json.optInt("protocolType", JT1076ProtocolHelper.PROTOCOL_TYPE_TCP);
|
|
return config;
|
}
|
}
|
|
/**
|
* 控制H264编码和文件传输
|
* @param action 操作类型:
|
* 0-开启h264文件写入,
|
* 1-停止h264编码并停止写入文件,
|
* 2-开启网络推送h264(不写入文件),
|
* 3-停止h264编码并停止网络推送,
|
* 4-开始传输H264文件(从文件读取并网络推送),
|
* 5-停止H264文件传输
|
* @param jsonConfig JSON格式的配置参数
|
* action 0/2: 包含:ip、port、width、height、framerate、simPhone
|
* action 4: 包含:ip、port、framerate、simPhone、filePath、protocolType(可选,1-UDP,2-TCP,默认TCP)
|
* action 1/3/5: 此参数可为空或null
|
* @return 0-成功,1-失败
|
*/
|
private synchronized int controlEncode(int action, String jsonConfig) {
|
Timber.d("controlEncode called with action: %d, jsonConfig: %s", action, jsonConfig);
|
|
try {
|
switch (action) {
|
case 0: // 开启h264文件写入
|
try {
|
EncodeConfig config0 = EncodeConfig.fromJson(jsonConfig);
|
return startFileEncode(config0);
|
} catch (JSONException e) {
|
Timber.e(e, "Failed to parse JSON config: %s", jsonConfig);
|
return 1;
|
}
|
|
case 1: // 停止h264编码并停止写入文件
|
return stopEncoder();
|
|
case 2: // 开启网络推送h264(不写入文件)
|
try {
|
EncodeConfig config2 = EncodeConfig.fromJson(jsonConfig);
|
return startNetworkEncode(config2);
|
} catch (JSONException e) {
|
Timber.e(e, "Failed to parse JSON config: %s", jsonConfig);
|
return 1;
|
}
|
|
case 3: // 停止h264编码并停止网络推送
|
return stopEncoder();
|
|
case 4: // 开始传输H264文件
|
try {
|
FileTransmitConfig config4 = FileTransmitConfig.fromJson(jsonConfig);
|
return startFileTransmit(config4);
|
} catch (JSONException e) {
|
Timber.e(e, "Failed to parse JSON config: %s", jsonConfig);
|
return 1;
|
}
|
|
case 5: // 停止H264文件传输
|
Timber.i("客户端请求停止视频文件上传");
|
return stopFileTransmitter();
|
|
default:
|
Timber.e("Unknown action: %d", action);
|
return 1; // 失败
|
}
|
} catch (Exception e) {
|
Timber.e(e, "Error in controlEncode");
|
return 1; // 失败
|
}
|
}
|
|
/**
|
* 启动文件编码模式(只写入文件,不进行网络推送)
|
*/
|
private int startFileEncode(EncodeConfig config) {
|
Timber.d("Starting file encode mode");
|
|
// 如果编码器已经在运行,先停止
|
if (h264Encoder != null) {
|
Timber.w("Encoder is already running, stopping it first");
|
stopEncoder();
|
}
|
|
try {
|
// 创建编码器
|
h264Encoder = new H264Encoder();
|
|
// 设置编码参数(使用配置中的参数)
|
int width = config != null ? config.width : DEFAULT_WIDTH;
|
int height = config != null ? config.height : DEFAULT_HEIGHT;
|
int framerate = config != null ? config.framerate : DEFAULT_FRAME_RATE;
|
h264Encoder.setEncoderParams(width, height, framerate, DEFAULT_BITRATE);
|
|
long timeFile = System.currentTimeMillis()/1000;//Date是秒,所以为了跟下发的Date starttime一致,此处除以1000 秒
|
SimpleDateFormat bcdFormat = new SimpleDateFormat("yyMMddHHmmss");
|
String str = bcdFormat.format(timeFile);
|
Timber.i("文件名:%s", str);
|
// 设置输出文件
|
String fileName = "h264_" + timeFile+ ".h264";
|
File outputFile = new File(outputFileDirectory, fileName);
|
h264Encoder.setOutputFile(outputFile.getAbsolutePath());
|
h264Encoder.setEnableFileOutput(true); // 启用文件输出
|
|
// 禁用网络传输
|
h264Encoder.setEnableNetworkTransmission(false);
|
|
// 初始化并启动(使用配置中的分辨率)
|
int[] resolution = {width, height};
|
if (h264Encoder.initialize(DEFAULT_CAMERA_ID_RANGE, null, resolution, false)) {
|
// 应用已保存的水印信息(如果有)
|
if (currentWatermarkInfo != null) {
|
h264Encoder.setWatermarkInfo(currentWatermarkInfo);
|
Timber.d("Applied saved watermark info to encoder");
|
}
|
h264Encoder.start();
|
Timber.d("File encode started successfully, output file: %s, resolution: %dx%d, framerate: %d",
|
outputFile.getAbsolutePath(), width, height, framerate);
|
return 0; // 成功
|
} else {
|
Timber.e("Failed to initialize encoder");
|
h264Encoder = null;
|
return 1; // 失败
|
}
|
} catch (Exception e) {
|
Timber.e(e, "Failed to start file encode");
|
h264Encoder = null;
|
return 1; // 失败
|
}
|
}
|
|
/**
|
* 启动网络推送模式(只进行网络推送,不写入文件)
|
*/
|
private int startNetworkEncode(EncodeConfig config) {
|
Timber.d("Starting network encode mode");
|
|
// 如果编码器已经在运行,先停止
|
if (h264Encoder != null) {
|
Timber.w("Encoder is already running, stopping it first");
|
stopEncoder();
|
}
|
|
// 检查必需的配置参数
|
if (config == null || config.ip == null || config.ip.trim().isEmpty() || config.port <= 0) {
|
Timber.e("Network encode requires valid ip and port in config");
|
return 1; // 失败
|
}
|
|
try {
|
// 创建编码器
|
h264Encoder = new H264Encoder();
|
|
// 设置编码参数(使用配置中的参数)
|
|
|
// 设置编码参数(使用配置中的参数)
|
int width = DEFAULT_WIDTH;
|
int height = DEFAULT_HEIGHT;
|
int framerate = DEFAULT_FRAME_RATE;
|
h264Encoder.setEncoderParams(width, height, framerate, DEFAULT_BITRATE);
|
|
long timeFile = System.currentTimeMillis();
|
SimpleDateFormat bcdFormat = new SimpleDateFormat("yyMMddHHmmss");
|
String str = bcdFormat.format(timeFile);
|
Timber.i("startNetworkEncode 文件名:%s", str);
|
// 设置输出文件
|
String fileName = "h264_" + timeFile+ ".h264";
|
File outputFile = new File(outputFileDirectory, fileName);
|
h264Encoder.setOutputFile(outputFile.getAbsolutePath());
|
h264Encoder.setEnableFileOutput(true); // 启用文件输出
|
|
|
// 启用网络传输并设置服务器地址
|
h264Encoder.setEnableNetworkTransmission(true);
|
h264Encoder.setServerAddress(config.ip, config.port);
|
|
// 设置协议参数(使用配置中的simPhone,如果未提供则使用默认值)
|
String simPhone = config.simPhone != null && !config.simPhone.trim().isEmpty()
|
? config.simPhone : "013120122580";
|
h264Encoder.setProtocolParams(simPhone, (byte)1);
|
|
// 初始化并启动(使用配置中的分辨率)
|
int[] resolution = {width, height};
|
if (h264Encoder.initialize(DEFAULT_CAMERA_ID_RANGE, null, resolution, false)) {
|
// 应用已保存的水印信息(如果有)
|
if (currentWatermarkInfo != null) {
|
h264Encoder.setWatermarkInfo(currentWatermarkInfo);
|
Timber.d("Applied saved watermark info to encoder");
|
}
|
h264Encoder.start();
|
Timber.d("Network encode started successfully, server: %s:%d, resolution: %dx%d, framerate: %d",
|
config.ip, config.port, width, height, framerate);
|
return 0; // 成功
|
} else {
|
Timber.e("Failed to initialize encoder");
|
h264Encoder = null;
|
return 1; // 失败
|
}
|
} catch (Exception e) {
|
Timber.e(e, "Failed to start network encode");
|
h264Encoder = null;
|
return 1; // 失败
|
}
|
}
|
|
/**
|
* 停止编码器
|
*/
|
private int stopEncoder() {
|
Timber.d("Stopping encoder");
|
|
if (h264Encoder != null) {
|
try {
|
h264Encoder.stop();
|
h264Encoder.release();
|
h264Encoder = null;
|
Timber.d("Encoder stopped successfully");
|
return 0; // 成功
|
} catch (Exception e) {
|
Timber.e(e, "Error stopping encoder");
|
h264Encoder = null;
|
return 1; // 失败
|
}
|
} else {
|
Timber.w("Encoder is not running");
|
return 0; // 成功(没有运行的编码器,视为成功)
|
}
|
}
|
|
/**
|
* 启动文件传输模式(从H264文件读取并网络推送)
|
*/
|
private int startFileTransmit(FileTransmitConfig config) {
|
Timber.d("Starting file transmit mode");
|
|
// 如果文件传输器已经在运行,先停止
|
if (h264FileTransmitter != null) {
|
Timber.w("File transmitter is already running, stopping it first");
|
stopFileTransmitter();
|
}
|
|
// 检查必需的配置参数
|
if (config == null || config.ip == null || config.ip.trim().isEmpty() || config.port <= 0) {
|
Timber.e("File transmit requires valid ip and port in config");
|
return 1; // 失败
|
}
|
|
if (config.filePath == null || config.filePath.trim().isEmpty()) {
|
Timber.e("File transmit requires valid filePath in config");
|
return 1; // 失败
|
}
|
|
try {
|
// 检查文件是否存在
|
File file = new File(config.filePath);
|
if (!file.exists() || !file.isFile()) {
|
Timber.e("File does not exist: %s", config.filePath);
|
return 1; // 失败
|
}
|
|
// 创建文件传输器
|
h264FileTransmitter = new H264FileTransmitter();
|
|
// 设置服务器地址
|
h264FileTransmitter.setServerAddress(config.ip, config.port);
|
|
// 设置协议类型
|
h264FileTransmitter.setProtocolType(1); //1-tcp
|
|
// 设置协议参数(SIM卡号和逻辑通道号)
|
String simPhone = config.simPhone != null && !config.simPhone.trim().isEmpty()
|
? config.simPhone : "013120122580";
|
h264FileTransmitter.setProtocolParams(simPhone, (byte)1);
|
|
// 设置帧率(用于计算时间戳间隔)
|
int framerate = config.framerate > 0 ? config.framerate : DEFAULT_FRAME_RATE;
|
h264FileTransmitter.setFrameRate(framerate);
|
|
// 设置进度回调(可选,用于日志输出)
|
h264FileTransmitter.setOnTransmitProgressCallback(new H264FileTransmitter.OnTransmitProgressCallback() {
|
@Override
|
public void onProgress(int currentFrame, int totalFrames) {
|
Timber.d("File transmit progress: frame %d%s", currentFrame,
|
totalFrames > 0 ? " of " + totalFrames : "");
|
}
|
|
@Override
|
public void onComplete() {
|
Timber.d("File transmit completed");
|
stopFileTransmitter();
|
}
|
|
@Override
|
public void onError(String error) {
|
Timber.e("File transmit error: %s", error);
|
}
|
});
|
|
// 初始化Socket连接
|
if (!h264FileTransmitter.initialize()) {
|
Timber.e("Failed to initialize file transmitter socket");
|
h264FileTransmitter = null;
|
return 1; // 失败
|
}
|
|
// 开始传输文件
|
h264FileTransmitter.transmitFile(config.filePath);
|
|
Timber.d("File transmit started successfully, file: %s, server: %s:%d, protocol: %s, framerate: %d",
|
config.filePath, config.ip, config.port,
|
config.protocolType == JT1076ProtocolHelper.PROTOCOL_TYPE_UDP ? "UDP" : "TCP", framerate);
|
return 0; // 成功
|
|
} catch (Exception e) {
|
Timber.e(e, "Failed to start file transmit");
|
if (h264FileTransmitter != null) {
|
try {
|
h264FileTransmitter.stop();
|
} catch (Exception ex) {
|
Timber.e(ex, "Error stopping file transmitter after failure");
|
}
|
h264FileTransmitter = null;
|
}
|
return 1; // 失败
|
}
|
}
|
|
/**
|
* 停止文件传输器
|
*/
|
private int stopFileTransmitter() {
|
Timber.d("Stopping file transmitter");
|
|
if (h264FileTransmitter != null) {
|
try {
|
h264FileTransmitter.stop();
|
h264FileTransmitter = null;
|
Timber.d("File transmitter stopped successfully");
|
return 0; // 成功
|
} catch (Exception e) {
|
Timber.e(e, "Error stopping file transmitter");
|
h264FileTransmitter = null;
|
return 1; // 失败
|
}
|
} else {
|
Timber.w("File transmitter is not running");
|
return 0; // 成功(没有运行的文件传输器,视为成功)
|
}
|
}
|
|
/**
|
* 获取资源列表(根据JT/T 1076-2016表23定义)
|
* @param startTime 开始时间(格式:YYMMDDHHmmss)
|
* @param endTime 结束时间(格式:YYMMDDHHmmss)
|
* @return 资源列表
|
*/
|
private List<ResourceInfo> getResourceList(String startTime, String endTime) {
|
Timber.d("getResourceList called, startTime: %s, endTime: %s", startTime, endTime);
|
|
List<ResourceInfo> resourceList = new ArrayList<>();
|
|
try {
|
// 扫描输出目录中的H264文件
|
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"));
|
if (files == null || files.length == 0) {
|
Timber.d("No H264 files found 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;
|
}
|
|
// 遍历文件,查找在时间范围内的文件
|
for (File file : files) {
|
ResourceInfo resourceInfo = createResourceInfoFromFile(file, startDate, endDate);
|
if (resourceInfo != null) {
|
resourceList.add(resourceInfo);
|
}
|
}
|
|
Timber.d("Found %d resources in time range", resourceList.size());
|
return resourceList;
|
|
} catch (Exception e) {
|
Timber.e(e, "Error getting resource list");
|
return resourceList;
|
}
|
}
|
|
/**
|
* 设置水印信息
|
* @param watermarkInfoJson 水印信息JSON字符串,包含:车牌(plateNumber)、学员(student)、教练(coach)、
|
* 经度(longitude)、纬度(latitude)、驾校(drivingSchool)、车速(speed)
|
* 示例:{"plateNumber":"京A12345","student":"张三","coach":"李四",
|
* "longitude":116.397128,"latitude":39.916527,"drivingSchool":"XX驾校","speed":60.5}
|
*/
|
private void setWatermarkInfo(String watermarkInfoJson) {
|
Timber.d("setWatermarkInfo called, watermarkInfoJson: %s", watermarkInfoJson);
|
|
try {
|
if (watermarkInfoJson == null || watermarkInfoJson.trim().isEmpty()) {
|
Timber.w("Watermark info JSON is null or empty, clearing watermark");
|
currentWatermarkInfo = null;
|
// 如果编码器正在运行,清除水印
|
if (h264Encoder != null) {
|
h264Encoder.setWatermarkInfo(null);
|
}
|
return;
|
}
|
|
// 解析JSON
|
JSONObject json = new JSONObject(watermarkInfoJson);
|
WatermarkInfo watermarkInfo = new WatermarkInfo();
|
|
// 解析各个字段(使用 optString/optDouble 避免字段不存在时抛出异常)
|
watermarkInfo.setPlateNumber(json.optString("plateNumber", null));
|
watermarkInfo.setStudent(json.optString("student", null));
|
watermarkInfo.setCoach(json.optString("coach", null));
|
|
// 经度和纬度可能是数字或字符串
|
if (json.has("longitude")) {
|
Object lonObj = json.get("longitude");
|
if (lonObj instanceof Number) {
|
watermarkInfo.setLongitude(((Number) lonObj).doubleValue());
|
} else if (lonObj instanceof String) {
|
try {
|
watermarkInfo.setLongitude(Double.parseDouble((String) lonObj));
|
} catch (NumberFormatException e) {
|
Timber.w("Invalid longitude format: %s", lonObj);
|
}
|
}
|
}
|
|
if (json.has("latitude")) {
|
Object latObj = json.get("latitude");
|
if (latObj instanceof Number) {
|
watermarkInfo.setLatitude(((Number) latObj).doubleValue());
|
} else if (latObj instanceof String) {
|
try {
|
watermarkInfo.setLatitude(Double.parseDouble((String) latObj));
|
} catch (NumberFormatException e) {
|
Timber.w("Invalid latitude format: %s", latObj);
|
}
|
}
|
}
|
|
watermarkInfo.setDrivingSchool(json.optString("drivingSchool", null));
|
|
// 车速可能是数字或字符串
|
if (json.has("speed")) {
|
Object speedObj = json.get("speed");
|
if (speedObj instanceof Number) {
|
watermarkInfo.setSpeed(((Number) speedObj).doubleValue());
|
} else if (speedObj instanceof String) {
|
try {
|
watermarkInfo.setSpeed(Double.parseDouble((String) speedObj));
|
} catch (NumberFormatException e) {
|
Timber.w("Invalid speed format: %s", speedObj);
|
}
|
}
|
}
|
|
// 保存水印信息
|
currentWatermarkInfo = watermarkInfo;
|
Timber.i("Watermark info parsed successfully: %s", watermarkInfo);
|
|
// 如果编码器正在运行,立即应用水印
|
if (h264Encoder != null) {
|
h264Encoder.setWatermarkInfo(watermarkInfo);
|
Timber.d("Watermark applied to encoder");
|
} else {
|
Timber.d("Encoder not running, watermark will be applied when encoder starts");
|
}
|
|
} catch (JSONException e) {
|
Timber.e(e, "Failed to parse watermark info JSON: %s", watermarkInfoJson);
|
currentWatermarkInfo = null;
|
} catch (Exception e) {
|
Timber.e(e, "Unexpected error setting watermark info");
|
currentWatermarkInfo = null;
|
}
|
}
|
|
/**
|
* 从文件创建资源信息(如果文件在时间范围内)
|
*/
|
private ResourceInfo createResourceInfoFromFile(File file, Date startDate, Date endDate) {
|
try {
|
// 从文件名中提取时间戳(格式:h264_1234567890123.h264)
|
String fileName = file.getName();
|
Date startTimeFromFileName = null;
|
|
if (fileName.startsWith("h264_") && fileName.endsWith(".h264")) {
|
try {
|
// 提取文件名中的时间戳(h264_ 和 .h264 之间的部分)
|
String timestampStr = fileName.substring(5, fileName.length() - 5); // 去掉 "h264_" 和 ".h264"
|
long timestamp = Long.parseLong(timestampStr);
|
startTimeFromFileName = new Date(timestamp);
|
} catch (NumberFormatException e) {
|
Timber.w("Failed to parse timestamp from filename: %s", fileName);
|
}
|
}
|
|
// 如果无法从文件名解析时间戳,则使用文件修改时间作为开始时间
|
if (startTimeFromFileName == null) {
|
startTimeFromFileName = new Date(file.lastModified());
|
}
|
|
// 结束时间使用文件修改时间
|
Date endTimeFromFile = new Date(file.lastModified());
|
|
// 检查文件时间是否在指定范围内
|
// 开始时间应该 >= startDate,结束时间应该 <= endDate
|
// 如果文件的开始时间在范围内,或者结束时间在范围内,或者文件时间范围包含查询范围,则包含该文件
|
if (startTimeFromFileName.after(endDate) || endTimeFromFile.before(startDate)) {
|
return null; // 不在时间范围内
|
}
|
|
// 创建资源信息对象
|
ResourceInfo resourceInfo = new ResourceInfo();
|
|
// 逻辑通道号(默认值,实际应从配置获取)
|
resourceInfo.setLogicalChannelNumber((byte) 1);
|
|
// 开始时间:从文件名中的时间戳
|
SimpleDateFormat bcdFormat = new SimpleDateFormat("yyMMddHHmmss", Locale.CHINA);
|
resourceInfo.setStartTime(bcdFormat.format(startTimeFromFileName));
|
|
// 结束时间:使用文件修改时间
|
resourceInfo.setEndTime(bcdFormat.format(endTimeFromFile));
|
|
// 报警标志(默认值,实际应从文件元数据获取)
|
resourceInfo.setAlarmFlag(0L);
|
|
// 音视频资源类型:2-视频
|
resourceInfo.setResourceType((byte) 2);
|
|
// 码流类型:1-主码流
|
resourceInfo.setStreamType((byte) 1);
|
|
// 存储器类型:1-主存储器
|
resourceInfo.setStorageType((byte) 1);
|
|
// 文件大小
|
resourceInfo.setFileSize(file.length());
|
|
return resourceInfo;
|
|
} catch (Exception e) {
|
Timber.e(e, "Error creating resource info from file: %s", file.getName());
|
return null;
|
}
|
}
|
|
/**
|
* 解析BCD时间字符串
|
* @param timeStr 时间字符串(格式:YYMMDDHHmmss)
|
* @return Date对象,如果解析失败返回null
|
*/
|
private Date parseTime(String timeStr) {
|
if (timeStr == null || timeStr.length() != 12) {
|
return null;
|
}
|
|
try {
|
SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss", Locale.CHINA);
|
return format.parse(timeStr);
|
} catch (ParseException e) {
|
Timber.e(e, "Failed to parse time: %s", timeStr);
|
return null;
|
}
|
}
|
}
|