| app/src/main/aidl/com/anyun/h264/IH264EncodeService.aidl | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/H264EncodeService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/H264Encoder.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/service/H264EncodeServiceClient.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
app/src/main/aidl/com/anyun/h264/IH264EncodeService.aidl
@@ -33,5 +33,11 @@ * @return 资源列表(根据JT/T 1076-2016表23定义) */ List<ResourceInfo> getResourceList(String startTime, String endTime); /** * 设置水印信息 * @param watermarkInfo 水印信息字符串 */ void setWatermarkInfo(String watermarkInfo); } app/src/main/java/com/anyun/h264/H264EncodeService.java
@@ -7,6 +7,7 @@ import timber.log.Timber; import com.anyun.h264.model.ResourceInfo; import com.anyun.h264.model.WatermarkInfo; import org.json.JSONException; import org.json.JSONObject; @@ -29,6 +30,7 @@ private H264Encoder h264Encoder; private H264FileTransmitter h264FileTransmitter; // H264文件传输器 private String outputFileDirectory; // H264文件输出目录 private WatermarkInfo currentWatermarkInfo; // 当前水印信息 // 默认编码参数 private static final int DEFAULT_WIDTH = 640; @@ -50,6 +52,11 @@ @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); } }; @@ -259,6 +266,11 @@ // 初始化并启动(使用配置中的分辨率) 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); @@ -321,6 +333,11 @@ // 初始化并启动(使用配置中的分辨率) 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); @@ -534,6 +551,100 @@ } /** * 设置水印信息 * @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) { app/src/main/java/com/anyun/h264/H264Encoder.java
@@ -4,11 +4,14 @@ import android.media.MediaCodecInfo; import android.media.MediaFormat; import com.anyun.libusbcamera.UsbCamera; import com.anyun.libusbcamera.WatermarkParam; import com.anyun.h264.model.WatermarkInfo; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; @@ -148,6 +151,87 @@ } /** * 设置水印信息 * @param watermarkInfo 水印信息对象 */ public void setWatermarkInfo(WatermarkInfo watermarkInfo) { if (watermarkInfo == null) { Timber.w("WatermarkInfo is null, disabling watermark"); usbCamera.enableWatermark(false, null); return; } try { // 构建水印文本列表(分行显示,每行一个信息项) ArrayList<WatermarkParam> watermarkParams = new ArrayList<>(); // 从左上角开始,每行间隔25像素 int yOffset = 30; int xOffset = 10; // 车牌号 if (watermarkInfo.getPlateNumber() != null && !watermarkInfo.getPlateNumber().isEmpty()) { watermarkParams.add(new WatermarkParam(xOffset, yOffset, "车牌:" + watermarkInfo.getPlateNumber())); yOffset += 25; } // 学员姓名 if (watermarkInfo.getStudent() != null && !watermarkInfo.getStudent().isEmpty()) { watermarkParams.add(new WatermarkParam(xOffset, yOffset, "学员:" + watermarkInfo.getStudent())); yOffset += 25; } // 教练姓名 if (watermarkInfo.getCoach() != null && !watermarkInfo.getCoach().isEmpty()) { watermarkParams.add(new WatermarkParam(xOffset, yOffset, "教练:" + watermarkInfo.getCoach())); yOffset += 25; } // 位置信息(纬度,经度) if (watermarkInfo.getLongitude() != null && watermarkInfo.getLatitude() != null) { watermarkParams.add(new WatermarkParam(xOffset, yOffset, String.format("位置:%.6f,%.6f", watermarkInfo.getLatitude(), watermarkInfo.getLongitude()))); yOffset += 25; } // 驾校名称 if (watermarkInfo.getDrivingSchool() != null && !watermarkInfo.getDrivingSchool().isEmpty()) { watermarkParams.add(new WatermarkParam(xOffset, yOffset, "驾校:" + watermarkInfo.getDrivingSchool())); yOffset += 25; } // 车速 if (watermarkInfo.getSpeed() != null) { watermarkParams.add(new WatermarkParam(xOffset, yOffset, String.format("车速:%.1fkm/h", watermarkInfo.getSpeed()))); } if (!watermarkParams.isEmpty()) { // 启用水印,使用默认字体路径(如果系统有字体文件) // 颜色:0-REVERSE(反色),1-BLACK,2-WHITE,3-RED,4-GREEN,5-BLUE // 字体大小、倍数可以根据需要调整 String fontPath = "/system/fonts/DroidSans.ttf"; // 默认字体路径,如果不存在可以传null usbCamera.enableWatermark(true, fontPath); // 设置水印:颜色(2=白色),字体大小(24),倍数(1),文本列表 usbCamera.setWatermark(2, 24, 1, watermarkParams); Timber.d("Watermark set successfully: %s", watermarkInfo); } else { Timber.w("No watermark text to display, disabling watermark"); usbCamera.enableWatermark(false, null); } } catch (Exception e) { Timber.e(e, "Failed to set watermark"); usbCamera.enableWatermark(false, null); } } /** * 初始化摄像头和编码器 */ public boolean initialize(int[] cameraIdRange, String cameraName, int[] resolution, boolean ayCamera) { app/src/main/java/com/anyun/h264/service/H264EncodeServiceClient.java
@@ -40,6 +40,10 @@ * // 获取资源列表 * List<ResourceInfo> resources = client.getResourceList("240101000000", "240101235959"); * * // 设置水印信息 * String watermarkJson = "{\"plateNumber\":\"京A12345\",\"student\":\"张三\",\"coach\":\"李四\",\"longitude\":116.397128,\"latitude\":39.916527,\"drivingSchool\":\"XX驾校\",\"speed\":60.5}"; * client.setWatermarkInfo(watermarkJson); * * // 解绑服务 * client.unbindService(); * </pre> @@ -202,5 +206,28 @@ return null; } } /** * 设置水印信息 * @param watermarkInfo JSON格式的水印信息字符串,包含:plateNumber(车牌)、student(学员)、coach(教练)、longitude(经度)、latitude(纬度)、drivingSchool(驾校)、speed(车速) * 示例:{"plateNumber":"京A12345","student":"张三","coach":"李四","longitude":116.397128,"latitude":39.916527,"drivingSchool":"XX驾校","speed":60.5} * 如果传入null或空字符串,将清除水印 * @return true-成功,false-失败 */ public boolean setWatermarkInfo(String watermarkInfo) { if (!isServiceBound()) { Log.e(TAG, "Service is not bound"); return false; } try { service.setWatermarkInfo(watermarkInfo); Log.d(TAG, "setWatermarkInfo called with: " + watermarkInfo); return true; } catch (RemoteException e) { Log.e(TAG, "Error calling setWatermarkInfo", e); return false; } } }