Dana
2025-12-01 964db3e1a2d6e2ab277da1ffcb27064bd2776848
1.添加一个设置水印的aidl接口
2.h264Encoder 设置usbcamera加水印
4个文件已修改
228 ■■■■■ 已修改文件
app/src/main/aidl/com/anyun/h264/IH264EncodeService.aidl 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/anyun/h264/H264EncodeService.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/anyun/h264/H264Encoder.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/anyun/h264/service/H264EncodeServiceClient.java 27 ●●●●● 补丁 | 查看 | 原始文档 | 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;
        }
    }
}