zhanghua
2024-10-09 b11443475114cacb529130a04d45c7c9981ff375
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.dahua.netsdk.lib.callback.impl;
 
import com.dahua.netsdk.lib.NetSDKLib;
import com.dahua.netsdk.lib.callback.BasicVideoStatHeatMapCallBack;
import com.dahua.netsdk.lib.callback.fVideoStatHeatMapCallBack;
import com.dahua.netsdk.lib.enumeration.EM_HEATMAP_TYPE;
import com.dahua.netsdk.module.HeatMapModule;
import com.dahua.netsdk.module.entity.HeatMapData;
import com.dahua.netsdk.module.entity.HeatMapGrayData;
import com.sun.jna.Pointer;
 
import java.util.concurrent.ConcurrentHashMap;
 
/**
 * @author 47081
 * @version 1.0
 * @description 热度图回调函数的默认实现, 回调函数建议写成单例模式, 不然容易导致sdk出现意想不到的错误
 * @date 2020/9/24
 */
public class DefaultVideoStatHeatMapCallBack extends BasicVideoStatHeatMapCallBack {
    private HeatMapModule module;
    /** 由于热度图数据是分多次发送,需要判断数据接收完整再处理 */
    private ConcurrentHashMap<Integer, HeatMapData> binDatas;
    /** 用于外部获取灰度图数据 */
    public static ConcurrentHashMap<Integer, HeatMapGrayData> grayDatas = new ConcurrentHashMap<Integer, HeatMapGrayData>();
 
    private static DefaultVideoStatHeatMapCallBack instance;
 
    private DefaultVideoStatHeatMapCallBack() {
        this.module = new HeatMapModule();
        this.binDatas = new ConcurrentHashMap<Integer, HeatMapData>();
    }
 
    public static fVideoStatHeatMapCallBack getInstance() {
        if (instance == null) {
            instance = new DefaultVideoStatHeatMapCallBack();
        }
        return instance;
    }
 
    /**
     * 解析数据
     *
     * @param attachHandle 订阅句柄
     * @param nToken       获取数据的句柄,即调用{@link NetSDKLib#CLIENT_GetVideoStatHeatMap(NetSDKLib.LLong, Pointer, Pointer, int)}
     *                     对应其出参的nToken字段{@link com.dahua.netsdk.lib.structure.NET_OUT_GET_VIDEOSTAT_HEATMAP#nToken}
     * @param type         热度图类型
     * @param binData      热度图字节数据
     */
    @Override
    public void parseData(long attachHandle, int nToken, EM_HEATMAP_TYPE type, byte[] binData) {
        System.out.println("receive heatmap data. attachHandle:" + attachHandle + ",nToken:" + nToken + ",type:"
                + type.getType() + "," + type.getDesc() + " data length:" + binData.length);
        // 协议描述:每个getHeatMap请求的结果分多个包来发送,每个包的大小在 “4~7此次数据总长度”中描述,每个包的 “8~11
        // 总行数(总轨迹条数)”相同,每个包的“12~15本次行数(本次轨迹条数)”表示本包的数据数量,最后一个包的“12~15本次行数(本次轨迹条数)”值为0,表示数据发送完毕。
        // 当总行数(或者总轨迹条数)和本次行数(或者本次轨迹条数)都为0时,表示没有数据。
        // 当总行数(或者总轨迹条数)不为0,而本次行数(或者本次轨迹条数)为0时表示,数据已经传完
        // 对binData进行过滤
        byte[] temp = new byte[4];
        System.arraycopy(binData, 7, temp, 0, 4);
        // 总行数
        int totalLine = byte2Int(temp);
        System.arraycopy(binData, 11, temp, 0, 4);
        // 本次行数
        int currentLine = byte2Int(temp);
        if (totalLine == 0 && currentLine == 0) {
            System.out.println("token:" + nToken + ",本次没有数据");
            return;
        }
        // 总行数和本次行数都不为0时,说明数据有效
        if (totalLine != 0 && currentLine != 0) {
            HeatMapData heatMapData = binDatas.get(nToken);
            // 获取宽高,宽16-17位,高18-19位
            int width = ((binData[16] & 0xFF)) | ((binData[17] & 0xFF) << 8);
            int height = ((binData[18] & 0xFF)) | ((binData[19] & 0xFF) << 8);
 
            if (heatMapData == null) {
                heatMapData = new HeatMapData(width, height, binData);
            } else {
                /** 目前设备端处理是一个完整数据包和一个结束包,以下代码基本不会被执行到, 先按协议进行多包数据拼接处理 */
                // 获取已保存数据的数据行数
                System.arraycopy(heatMapData.getData(), 11, temp, 0, 4);
                int savedLine = byte2Int(temp);
                // 合并后的本次行数
                int currentTotal = savedLine + currentLine;
                byte[] bytes = int2ByteArr(currentTotal);
                System.arraycopy(bytes, 0, heatMapData.getData(), 11, bytes.length);
                // 去掉数据头,数据头为32位
                byte[] data = new byte[binData.length - 32];
                System.arraycopy(binData, 32, data, 0, data.length);
                // 追加到完整数据包中
                heatMapData.addData(data);
            }
            // 将数据存到map中
            binDatas.put(nToken, heatMapData);
        }
        // 数据接收完成
        if (totalLine != 0 && currentLine == 0) {
 
            // 将数据取出
            HeatMapData heatMapData = binDatas.get(nToken);
 
            if (heatMapData != null) {
                System.out.println("width:" + heatMapData.getWidth() + ",height:" + heatMapData.getHeight()
                        + ",data length:" + heatMapData.getData().length);
                // 热度图数据转灰度图
                byte[] grayData = module.transferGray(heatMapData.getData(), heatMapData.getWidth(),
                        heatMapData.getHeight());
                // 销毁binData数据
                binDatas.remove(nToken);
                // 保存灰度图数据
                grayDatas.put(nToken, new HeatMapGrayData(heatMapData.getWidth(), heatMapData.getHeight(), grayData));
            }
        }
    }
 
    /**
     * 将byte[4]转成int
     *
     * @param src
     * @return
     */
    private int byte2Int(byte[] src) {
        int value;
        value = (int) ((src[0] & 0xFF) | ((src[1] & 0xFF) << 8) | ((src[2] & 0xFF) << 16) | ((src[3] & 0xFF) << 24));
        return value;
    }
 
    /**
     * int转成byte[],长度为4
     *
     * @param value
     * @return
     */
    private byte[] int2ByteArr(int value) {
        byte[] bytes = new byte[4];
        bytes[3] = (byte) (value >> 24);
        bytes[2] = (byte) (value >> 16);
        bytes[1] = (byte) (value >> 8);
        bytes[0] = (byte) (value);
        return bytes;
    }
}