1
zhanghua
2024-09-26 c775c6953d9759e70f08acbfa8f6d7490aaae3d1
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
package com.netsdk.demo.customize;
 
import com.netsdk.demo.customize.analyseTaskDemo.AnalyseTaskUtils;
import com.netsdk.demo.util.CaseMenu;
import com.netsdk.demo.util.EventTaskHandler;
import com.netsdk.lib.LastError;
import com.netsdk.lib.NetSDKLib;
import com.netsdk.lib.PlaySDKLib;
import com.netsdk.lib.ToolKits;
import com.netsdk.lib.callback.impl.DefaultDisconnectCallback;
import com.netsdk.lib.callback.impl.DefaultHaveReconnectCallBack;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
 
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.function.DoubleToIntFunction;
 
/**
 * @author : 47040
 * @since : Created in 2020/12/10 19:30
 */
public class PlayBackWithDataOnly {
 
    ////////////////// SDK 初始化/清理资源 /////////////////////
    //////////////////////////////////////////////////////////
 
    // The constant net sdk
    public static final NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;
 
    // The constant config sdk.
    public static NetSDKLib configsdk = NetSDKLib.CONFIG_INSTANCE;
 
    // The constant play sdk
    public static final PlaySDKLib playsdk = PlaySDKLib.PLAYSDK_INSTANCE;
 
    private static boolean bInit = false;    // 判断是否初始化
    private static boolean bLogOpen = false; // 判断log是否打开
 
    // 回调函数需要是静态的,防止被系统回收
    private static NetSDKLib.fDisConnect disConnectCB = DefaultDisconnectCallback.getINSTANCE();     // 断线回调
    private static NetSDKLib.fHaveReConnect haveReConnectCB = DefaultHaveReconnectCallBack.getINSTANCE();  // 重连回调
 
    /**
     * Init boolean. sdk 初始化
     *
     * @return the boolean
     */
    public static boolean Init() {
        bInit = netsdk.CLIENT_Init(disConnectCB, null);
        if (!bInit) {
            System.out.println("Initialize SDK failed");
            return false;
        }
 
        enableLog();  // 配置日志
 
        // 设置断线重连回调接口, 此操作为可选操作,但建议用户进行设置
        netsdk.CLIENT_SetAutoReconnect(haveReConnectCB, null);
 
        //设置登录超时时间和尝试次数,可选
        int waitTime = 3000;                        //登录请求响应超时时间设置为3S
        int tryTimes = 1;                           //登录时尝试建立链接 1 次
        netsdk.CLIENT_SetConnectTime(waitTime, tryTimes);
        // 设置更多网络参数, NET_PARAM 的nWaittime , nConnectTryNum 成员与 CLIENT_SetConnectTime
        // 接口设置的登录设备超时时间和尝试次数意义相同,可选
        NetSDKLib.NET_PARAM netParam = new NetSDKLib.NET_PARAM();
        netParam.nConnectTime = 10000;              // 登录时尝试建立链接的超时时间
        netParam.nGetConnInfoTime = 3000;           // 设置子连接的超时时间
        netsdk.CLIENT_SetNetworkParam(netParam);
        return true;
    }
 
    /**
     * 打开 sdk log
     */
    private static void enableLog() {
        NetSDKLib.LOG_SET_PRINT_INFO setLog = new NetSDKLib.LOG_SET_PRINT_INFO();
        File path = new File("sdklog/");
        if (!path.exists()) path.mkdir();
 
        // 这里的log保存地址依据实际情况自己调整
        String logPath = path.getAbsoluteFile().getParent() + "\\sdklog\\" + "sdklog" + AnalyseTaskUtils.getDate() + ".log";
        setLog.nPrintStrategy = 0;
        setLog.bSetFilePath = 1;
        System.arraycopy(logPath.getBytes(), 0, setLog.szLogFilePath, 0, logPath.getBytes().length);
        System.out.println(logPath);
        setLog.bSetPrintStrategy = 1;
        bLogOpen = netsdk.CLIENT_LogOpen(setLog);
        if (!bLogOpen) System.err.println("Failed to open NetSDK log");
    }
 
    /**
     * Cleanup. 清除 sdk 环境
     */
    public static void cleanup() {
        if (bLogOpen) {
            netsdk.CLIENT_LogClose();
        }
        if (bInit) {
            netsdk.CLIENT_Cleanup();
        }
    }
 
    /**
     * 清理并退出
     */
    public static void cleanAndExit() {
        netsdk.CLIENT_Cleanup();
        System.exit(0);
    }
 
    ////////////////////////////////////// 登录相关 ///////////////////////////////
    //////////////////////////////////////////////////////////////////////////////
 
    private NetSDKLib.NET_DEVICEINFO_Ex deviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex(); // 设备信息
 
    private NetSDKLib.LLong m_hLoginHandle = new NetSDKLib.LLong(0); // 登录句柄
 
    /**
     * login with high level 高安全级别登陆
     */
    public boolean loginWithHighLevel() {
 
        NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstlnParam =
                new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY() {{
                    szIP = m_strIpAddr.getBytes();
                    nPort = m_nPort;
                    szUserName = m_strUser.getBytes();
                    szPassword = m_strPassword.getBytes();
                }};   // 输入结构体参数
        NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam =
                new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();  // 输结构体参数
 
        // 写入sdk
        m_hLoginHandle = netsdk.CLIENT_LoginWithHighLevelSecurity(pstlnParam, pstOutParam);
 
        if (m_hLoginHandle.longValue() == 0) {
            System.err.printf("Login Device[%s] Port[%d]Failed. %s\n", m_strIpAddr, m_nPort,
                    ToolKits.getErrorCode());
            return false;
        } else {
            deviceInfo = pstOutParam.stuDeviceInfo;   // 获取设备信息
            System.out.println("Login Success");
            System.out.println("Device Address:" + m_strIpAddr);
            System.out.println("设备包含:" + deviceInfo.byChanNum + "个通道");
            return true;
        }
    }
 
    /**
     * logout 退出
     */
    public void logOut() {
        if (m_hLoginHandle.longValue() != 0) {
            netsdk.CLIENT_Logout(m_hLoginHandle);
            System.out.println("LogOut Success");
        }
    }
 
    //////////////////////////// 录像回放相关 /////////////////////////////
    //////////////////////////////////////////////////////////////////////
 
    private NetSDKLib.LLong m_hPlayHandle = new NetSDKLib.LLong(0);   // 回放句柄
 
    private final NetSDKLib.NET_TIME m_startTime = new NetSDKLib.NET_TIME(); // 开始时间
    private final NetSDKLib.NET_TIME m_stopTime = new NetSDKLib.NET_TIME();  // 结束时间
 
    private static final DownLoadPosCallBack m_PlayBackDownLoadPos = new DownLoadPosCallBack(); // 回放数据下载进度
 
    private static final DataCallBack m_dataCallBack = new DataCallBack(); // 回放数据回调
 
    private static final PlayDecCallBack m_playDecCallBack = new PlayDecCallBack();  // Play SDK 回放
 
    private static final ExecutorService dataParseService = Executors.newFixedThreadPool(5);
 
    private static final int channel = 10;   // NetSDK 对应的 设备通道
 
    private static final int port = 10;     // PlaySDK 解码端口
 
 
    // 回放进度回调
    public static class DownLoadPosCallBack implements NetSDKLib.fDownLoadPosCallBack {
        public void invoke(NetSDKLib.LLong lPlayHandle, int dwTotalSize, int dwDownLoadSize, Pointer dwUser) {
            if (0 != dwDownLoadSize) {
                // System.out.println("PlayBack DownLoadCallback: [ " + dwTotalSize + " ]" + " [ " + dwDownLoadSize + " ]");
                if (-1 == dwDownLoadSize) {
                    System.out.println("回放结束");
                }
            }
        }
    }
 
    private static final LinkedBlockingDeque<ByteArrayOutputStream> queue = new LinkedBlockingDeque<>();
 
    // 回放数据回调
    public static class DataCallBack implements NetSDKLib.fDataCallBack {
        public int invoke(NetSDKLib.LLong lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, Pointer dwUser) {
            ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
            try {
                memoryStream.write(pBuffer.getByteArray(0, dwBufSize));
                queue.offer(memoryStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return 1;
        }
    }
 
    public static class PlayDecCallBack implements PlaySDKLib.fDecCBFun {
 
        @Override
        public void invoke(int nPort, Pointer pBuf, int nSize, Pointer pFrameInfo, Pointer pUserData, int nReserved2) {
            // Todo
            dataParseService.submit(new Runnable() {
                @Override
                public void run() {
                    queryOSDTime(10);
                }
            });
        }
    }
 
    /**
     * 开启回放
     */
    public void StartPlayBack() {
 
        if (m_hLoginHandle.longValue() == 0) {
            System.err.println("Please Login First");
            return;
        }
 
        // 配置 PlaySDK
        playsdk.PLAY_OpenStream(port, null, 0, 4 * 1024 * 1024);
        playsdk.PLAY_SetDecCallBackEx(port, m_playDecCallBack, null);
        playsdk.PLAY_Play(port, null);
 
        // 设置回放时的码流类型
        IntByReference steamType = new IntByReference(0);           // 0-主辅码流,1-主码流,2-辅码流
        int emType = NetSDKLib.EM_USEDEV_MODE.NET_RECORD_STREAM_TYPE;
 
        boolean bret = netsdk.CLIENT_SetDeviceMode(m_hLoginHandle, emType, steamType.getPointer());
        if (!bret) {
            System.err.println("Set Stream Type Failed" + ToolKits.getErrorCode());
        }
 
        // 设置回放时的录像文件类型
        IntByReference emFileType = new IntByReference(0); // 所有录像 NET_RECORD_TYPE
        emType = NetSDKLib.EM_USEDEV_MODE.NET_RECORD_TYPE;
        bret = netsdk.CLIENT_SetDeviceMode(m_hLoginHandle, emType, emFileType.getPointer());
        if (!bret) {
            System.err.println("Set Record Type Failed " + ToolKits.getErrorCode());
        }
 
        m_startTime.setTime(2021, 12, 15, 0, 0, 0);    // 开始时间
        m_stopTime.setTime(2021, 12, 15, 0, 2, 30);    // 结束时间
 
        m_hPlayHandle = netsdk.CLIENT_PlayBackByTimeEx(m_hLoginHandle, channel, m_startTime, m_stopTime,
                null, m_PlayBackDownLoadPos, null, m_dataCallBack, null);
 
        if (m_hPlayHandle.longValue() == 0) {
            int error = netsdk.CLIENT_GetLastError();
            System.err.println("PlayBackByTimeEx Failed " + ToolKits.getErrorCode());
            switch (error) {
                case LastError.NET_NO_RECORD_FOUND:
                    System.out.println("查找不到录像");
                    break;
                default:
                    System.out.println("开启失败");
                    break;
            }
        } else {
            System.out.println("PlayBackByTimeEx Succeed.");
        }
 
        // 启动新线程 顺序向PlaySDK写入数据 线程在句柄清空后关闭
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (m_hPlayHandle.longValue() != 0) {
                    try {
                        ByteArrayOutputStream stream = queue.take();
                        final byte[] data = stream.toByteArray();
                        playsdk.PLAY_InputData(port, data, data.length);
                    } catch (InterruptedException e) {
                        System.err.println(e.getMessage());
                    }
                }
            }
        }).start();
    }
 
    /**
     * 停止回放
     */
    public void StopPlayBack() {
        if (m_hPlayHandle.longValue() == 0) {
            System.err.println("Please make sure the PlayBack Handle is valid");
            return;
        }
 
        if (!netsdk.CLIENT_StopPlayBack(m_hPlayHandle)) {
            System.err.println("StopPlayBack Failed");
            return;
        }
 
        // 关闭PlaySDK解码端口
        playsdk.PLAY_Stop(port);
        playsdk.PLAY_CloseStream(port);
 
        System.out.println("StopPlayBack Succeed.");
 
        m_hPlayHandle.setValue(0);
    }
 
    public void queryOSDTime(){
        queryOSDTime(10);
    }
 
    public static void queryOSDTime(int port) {
        PlaySDKLib.TimeInfo timeInfo = new PlaySDKLib.TimeInfo();
        timeInfo.write();
        boolean ret = playsdk.PLAY_QueryInfo(port, 1, timeInfo.getPointer(), timeInfo.size(), new IntByReference(0));
        if (!ret) {
            System.err.println("Get OSD Time Failed " + playsdk.PLAY_GetLastErrorEx(port));
            return;
        }
        timeInfo.read();
        System.out.println("OSD Time: " + timeInfo.toString());
 
    }
 
 
    public void run() {
        CaseMenu menu = new CaseMenu();
        menu.addItem(new CaseMenu.Item(this, "开始播放", "StartPlayBack"));
        menu.addItem(new CaseMenu.Item(this, "结束播放", "StopPlayBack"));
        menu.addItem(new CaseMenu.Item(this, "获取OSD时间", "queryOSDTime"));
        menu.run();
    }
 
 
    /////////////// 配置登陆地址,端口,用户名,密码  ////////////////////////
    private final String m_strIpAddr = "172.8.88.220";
    private final int m_nPort = 37777;
    private final String m_strUser = "admin";
    private final String m_strPassword = "admin123";
    //////////////////////////////////////////////////////////////////////
 
    public static void main(String[] args) {
        PlayBackWithDataOnly demo = new PlayBackWithDataOnly();
 
 
        PlayBackWithDataOnly.Init();
        if (demo.loginWithHighLevel()) {
            demo.run();
        }
        demo.logOut();
        PlayBackWithDataOnly.cleanAndExit();
    }
}