随笔 - 1506  文章 - 1  评论 - 7  阅读 - 58万

安防融合视频云服务EasyCVR集成海康EHome协议实现设备录像回看返回会话ID为-1是什么情况?

之前TSINGSEE青犀视频讲过互联网安防视频云服务EasyCVR能够集成海康EHome私有协议,当然我们在测试的时候碰到了一些问题,本文就来讲一下测试EasyCVR集成海康EHome协议实现设备录像回看时遇到的问题。

一、测试环境

  • 设备型号DS-2CD2325FD-XF2005
  • SDK 版本 HCISUPSDKV2.3.4.2_build20200316_Win64_ZH

二、问题描述

1、 按照官方文档介绍,开启监听服务后,启播设备录像将返回一个会话ID,应用层可以将这个会话ID与播放操作进行绑定。

69.png

实际使用过程中会话ID 一直为无效值-1.

2、设备录像回看需启用回放监听服务不能与直播预览共用

3、设备录像每次触发的回调不是pes,也就是接收的包起始不是0x000001

三、解决方案

1、应用层设备录像播放接口,添加操作队列,同时只能启播一个设备录像,将启播成功取流回调中的句柄与设备录像进行绑定。

2、设备录像回看需启用流媒体服务器(SMS)的回放监听并注册回调函数以接收设备连接请求,如果想实现多路设备录像回看,最好每一路设备录像启用一个回放监听服务。

3、应用层添加缓存,进行流分割,获取一个完整的ps包后再进行解复用流程

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
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
#include <stdio.h>
#include <Windows.h>
#include "HCISUPCMS.h"
#include "HCISUPStream.h"
#include "plaympeg4.h"
  
LONG lLoginID = -1;
LONG lLinkHandle = -1;
LONG lPlayHandle = -1;
FILE *Videofile = NULL;
  
//
//注册回调函数
BOOL CALLBACK RegisterCallBack(LONG lUserID, DWORD dwDataType, void *pOutBuffer, DWORD dwOutLen, void *pInBuffer, DWORD dwInLen, void *pUser)
{
    if (ENUM_DEV_ON == dwDataType)
    {
       NET_EHOME_DEV_REG_INFO *pDevInfo = (NET_EHOME_DEV_REG_INFO *)pOutBuffer;
  
       if (pDevInfo != NULL)
       {
          lLoginID = lUserID;
          printf("On-line, lUserID: %d, Device ID: %s\n", lLoginID, pDevInfo->byDeviceID);
       }
       //输入参数
       NET_EHOME_SERVER_INFO *pServerInfo = (NET_EHOME_SERVER_INFO *)pInBuffer;
       pServerInfo->dwTimeOutCount = 6; //心跳超时次数
       pServerInfo->dwKeepAliveSec = 15; //心跳间隔
    }
    else if (ENUM_DEV_OFF == dwDataType)
    {
    printf("Off-line, lUserID: %d\n", lUserID);
    NET_ECMS_ForceLogout(lUserID);      
    }
    else
    {
    }
  
    return TRUE;
}
  
//
//处理文件流数据
BOOL InputStreamData(BYTE byDataType, char* pBuffer, int iDataLen)
{  
    if(Videofile == NULL)
    {
        Videofile = fopen("Test.mp4","wb");
        printf("Save data to file: Test.mp4!");
    }
  
    if(Videofile!= NULL)
    {
        fwrite(pBuffer,iDataLen,1,Videofile);  //下载视频文件
    }
  
    //调用播放库解码并显示码流实现回放
    /*
    if(1 == byDataType)
    {
        if (!PlayM4_GetPort(&m_lPort))
        {
            return FALSE;
        }
        if (!PlayM4_SetStreamOpenMode(m_lPort, STREAME_REALTIME))
        {
            return FALSE;
        }
        //Enter first 40-byte header
        if(!PlayM4_OpenStream(m_lPort, (unsigned char *)pBuffer, (DWORD)iDataLen, 2*1024*1024))
        {
            return FALSE;
        }
        if(!PlayM4_Play(m_lPort, hWnd))
        {
            return FALSE;
        }
    }
    else
    {  
        for (int i=0; i<1000; i++)
        {
            BOOL bRet = PlayM4_InputData(m_lPort,(unsigned char *)pBuffer, (DWORD)iDataLen);
            if (!bRet)
            {
                if ( i >=999)
                {
                    printf("PlayM4_InputData failed, error code: %d!", PlayM4_GetLastError(m_lPort));
                }  
                Sleep(2);
            }
        }
    }
    */
    return TRUE;
}
  
//
//注册回放码流回调函数
BOOL CALLBACK fnPLAYBACK_DATA_CB(LONG lPlayBackLinkHandle, NET_EHOME_PLAYBACK_DATA_CB_INFO *pDataCBInfo, void *pUserData)
{
    if (NULL == pDataCBInfo)
    {
        return FALSE;
    }
    lPlayHandle = lPlayBackLinkHandle;
    InputStreamData(pDataCBInfo->dwType, (char*)pDataCBInfo->pData, pDataCBInfo->dwDataLen);
    return TRUE;
}
  
//
//注册回访请求的响应回调函数
BOOL CALLBACK fnPLAYBACK_NEWLINK_CB(LONG lPlayBackLinkHandle, NET_EHOME_PLAYBACK_NEWLINK_CB_INFO  *pNewLinkCBMsg, void *pUserData)
{
    lLinkHandle = lPlayBackLinkHandle;
    printf("Callback of playback listening, Device ID: %s\n", pNewLinkCBMsg->szDeviceID);
  
    //回放数据的回调参数
    NET_EHOME_PLAYBACK_DATA_CB_PARAM struDataCB = {0};
    struDataCB.fnPlayBackDataCB = fnPLAYBACK_DATA_CB;
    struDataCB.byStreamFormat = 0;//封装格式:0-PS格式
  
    if (!NET_ESTREAM_SetPlayBackDataCB(lPlayBackLinkHandle, &struDataCB))
    {
        printf("NET_ESTREAM_SetPlayBackDataCB failed, error code: %d\n", NET_ESTREAM_GetLastError());
        return FALSE;
    }
    printf("NET_ESTREAM_SetPlayBackDataCB!\n");
  
    return TRUE;
}
  
/
//设置异常回调
NET_ESTREAM_SetExceptionCallBack(0, 0, StreamExceptionCallback, NULL);
  
void CALLBACK StreamExceptionCallback(DWORD dwType, LONG iUserID, LONG iHandle, void* pUser)
{
    if(EHOME_PREVIEW_EXCEPTION == dwType)
    {
        //预览异常
        printf("Preview exception, handle=%d, Error:%d", iHandle, dwError);
    }
    else if (EHOME_PLAYBACK_EXCEPTION == dwType)
    {
        //回放异常
        printf("Playback exception, handle=%d, Error:%d", iHandle, dwError);
    }
    else if (EHOME_AUDIOTALK_EXCEPTION == dwType)
    {
        //语音对讲(转发)异常
        printf("Stream VoiceTalk exception, handle=%d, Error:%d", iHandle, dwError);
    }
}
  
void main(){
     
    //SMS在开启监听服务后获取码流
  
    //初始化SMS库
    NET_ESTREAM_Init();
  
    //回放的监听参数
    NET_EHOME_PLAYBACK_LISTEN_PARAM struListen = {0};
    memcpy(struListen.struIPAdress.szIP,"10.16.2.123", sizeof("10.16.2.123"));
    struListen.struIPAdress.wPort = 8003; //SMS的监听端口号
    struListen.fnNewLinkCB = fnPLAYBACK_NEWLINK_CB; //回放请求的回调函数
    struListen.pUserData = NULL;
    struListen.byLinkMode = 0; //0-TCP, 1-UDP(保留)
  
    //开启监听服务
    LONG lHandle = NET_ESTREAM_StartListenPlayBack(&struListen);
    if(lHandle < -1)
    {
        printf("NET_ESTREAM_StartListenPlayBack failed, error code: %d\n", NET_ESTREAM_GetLastError());
        NET_ESTREAM_Fini();
        return;
    }
    printf("NET_ESTREAM_StartListenPlayBack!\n");
    //
  
     
    //注册和回放请求
  
    //初始化CMS库
    NET_ECMS_Init();
  
    //注册的监听参数
    NET_EHOME_CMS_LISTEN_PARAM struCMSListenPara = {0};
    memcpy(struCMSListenPara.struAddress.szIP, "0.0.0.0", sizeof("0.0.0.0"));
    struCMSListenPara.struAddress.wPort = 7660;
    struCMSListenPara.fnCB = RegisterCallBack;
  
    //开启监听服务并接收设备注册信息
    LONG lListen = NET_ECMS_StartListen(&struCMSListenPara);
    if(lListen < -1)
    {
        printf("NET_ECMS_StartListen failed, error code: %d\n", NET_ECMS_GetLastError());
        NET_ECMS_Fini();
        return;
    }
    printf("NET_ECMS_StartListen!\n");
  
    while(1)
    {
        Sleep(1000);  //以下操作需在注册完成后再进行
        if(lLoginID >= 0)
        {
            break;
        }
    }
  
    //查找视频文件
    NET_EHOME_REC_FILE_COND struFindCond = {0};
    struFindCond.dwChannel =1;  //通道号,从1开始
    struFindCond.dwRecType = 0xff; //所有类型
    struFindCond.dwStartIndex = 0;  //查找起始位置
    struFindCond.dwMaxFileCountPer = 5;  //单次搜索的最多文件数量
  
    //查找开始时间
    struFindCond.struStartTime.wYear = (WORD)2015;
    struFindCond.struStartTime.byMonth = (BYTE)5;
    struFindCond.struStartTime.byDay = (BYTE)18;
    struFindCond.struStartTime.byHour = (BYTE)10;
    struFindCond.struStartTime.byMinute = (BYTE)0;
    struFindCond.struStartTime.bySecond = (BYTE)0;
  
    //查找结束时间
    struFindCond.struStopTime.wYear = (WORD)2015;
    struFindCond.struStopTime.byMonth = (BYTE)5;
    struFindCond.struStopTime.byDay = (BYTE)18;
    struFindCond.struStopTime.byHour = (BYTE)12;
    struFindCond.struStopTime.byMinute = (BYTE)59;
    struFindCond.struStopTime.bySecond = (BYTE)59;
  
    LONG lSearchType = 0; //查找文件
    LONG lFileHandle = NET_ECMS_StartFindFile_V11(lLoginID, lSearchType, &struFindCond, sizeof(struFindCond)); //Start file search
    if (lFileHandle < 0)
    {       
        printf("NET_ECMS_StartFindFile_V11 failed, error code: %d\n", NET_ECMS_GetLastError());
        NET_ECMS_Fini();
        return;
    }
    printf("NET_ECMS_StartFindFile_V11!\n");
  
    LONG lRet = -1;
    char csTmp[256] = {0}; 
    char szFileName[MAX_FILE_NAME_LEN] = {0};
  
    NET_EHOME_REC_FILE struFileInfo = {0};
  
    //可在线程中搜索文件
    while(1)
    {
        lRet = NET_ECMS_FindNextFile_V11(lFileHandle, &struFileInfo, sizeof(struFileInfo));//逐个获取搜索结果
        if (lRet == ENUM_GET_NEXT_STATUS_SUCCESS)
        {
            if (struFileInfo.dwFileSize / 1024 == 0)
            {
                sprintf(csTmp,"%d",struFileInfo.dwFileSize);
            }
            else if (struFileInfo.dwFileSize / 1024 > 0 && struFileInfo.dwFileSize /(1024*1024) == 0)
            {
                sprintf(csTmp,"%dK",struFileInfo.dwFileSize/1024);
            }
            else
            {
                sprintf(csTmp,"%dM",struFileInfo.dwFileSize /1024/1024);
            }
  
            printf("Filename[%s], Filesize[%s], StarTime[%04d-%02d-%02d %02d:%02d:%02d], StopTime[%04d-%02d-%02d %02d:%02d:%02d] \n", \
                struFileInfo.szFileName, csTmp, struFileInfo.struStartTime.wYear, struFileInfo.struStartTime.byMonth, \
                struFileInfo.struStartTime.byDay, struFileInfo.struStartTime.byHour, struFileInfo.struStartTime.byMinute,\
                struFileInfo.struStartTime.bySecond, struFileInfo.struStopTime.byDay, struFileInfo.struStopTime.byHour, \
                struFileInfo.struStopTime.byMinute, struFileInfo.struStopTime.bySecond);//Searched file information
  
            memcpy(szFileName, struFileInfo.sFileName, MAX_FILE_NAME_LEN);
        }
        else
        {
            if (lRet == ENUM_GET_NETX_STATUS_NEED_WAIT)
            {
                Sleep(5);
                continue;
            }
  
            if ((lRet == ENUM_GET_NETX_STATUS_NO_FILE) || (lRet == ENUM_GET_NEXT_STATUS_FINISH))
            {
                printf("No more file!\n");
                break;
            }
            else if(lRet == ENUM_GET_NEXT_STATUS_NOT_SUPPORT)
            {
                printf("Device does not support!\n");
                break;
            }
            else
            {
                printf("Failed to find a file, for the server is busy or network failure!\n");
                break;
            }
        }
    }
  
    //回放请求的输入参数
    NET_EHOME_PLAYBACK_INFO_IN struPlayBackIn = {0};
    struPlayBackIn.dwSize = sizeof(struPlayBackIn);
    struPlayBackIn.dwChannel = 1; //通道号
    struPlayBackIn.byPlayBackMode = 0; //回放模式:0-按文件名回放,1-按时间回放(保留)
    struPlayBackIn.unionPlayBackMode.struPlayBackbyName.dwSeekType = 0; //0-按字节长度计算,1-按秒数计算
    struPlayBackIn.unionPlayBackMode.struPlayBackbyName.dwFileOffset = 0;
    //当dwSeekType为0时,偏移量按字节计算;当dwSeekType等于1时,偏移量按秒数计算
    struPlayBackIn.unionPlayBackMode.struPlayBackbyName.dwFileSpan = 0; //已下载的文件大小,如果大小为0,说明文件已下载完成
    memcpy(struPlayBackIn.unionPlayBackMode.struPlayBackbyName.szFileName, szFileName, MAX_FILE_NAME_LEN);//需要进行回放的文件名称
  
    memcpy(struPlayBackIn.struStreamSever.szIP, "10.16.2.123", sizeof("10.16.2.123")); //SMS的IP地址
    struPlayBackIn.struStreamSever.wPort = 8003; //SMS的端口号,需和监听端口号一致
  
    //回放请求的输出参数
    NET_EHOME_PLAYBACK_INFO_OUT struPlayBackOut = {0};
  
    //回放请求
    if(!NET_ECMS_StartPlayBack(lLoginID, &struPlayBackIn, &struPlayBackOut))
    {
        printf("NET_ECMS_StartPlayBack failed, error code: %d\n", NET_ECMS_GetLastError());
        NET_ECMS_Fini();
        return;
    }
    printf("NET_ECMS_StartPlayBack!\n");
  
    //码流传输请求的输入参数
    NET_EHOME_PUSHPLAYBACK_IN struPushPlayBackIn = {0};
    struPushPlayBackIn.dwSize = sizeof(struPushPlayBackIn);
    struPushPlayBackIn.lSessionID = struPlayBackOut.lSessionID; //回放请求的会话ID
  
    //码流传输请求的输出参数
    NET_EHOME_PUSHPLAYBACK_OUT struPushPlayBackOut = {0};
  
    //发送请求给设备并开始传输码流
    if(!NET_ECMS_StartPushPlayBack(lLoginID, &struPushPlayBackIn, &struPushPlayBackOut))
    {
        printf("NET_ECMS_StartPushPlayBack failed, error code: %d\n", NET_ECMS_GetLastError());
        NET_ECMS_Fini();
        return;
    }
    printf("NET_ECMS_StartPushPlayBack!\n");
  
    Sleep(50000);
  
     
    //登出
  
    //CMS停止监听服务
    if(!NET_ECMS_StopListen(lListen))
    {
        printf("NET_ECMS_StopListen failed, error code: %d\n", NET_ECMS_GetLastError());
    }
  
    //释放被CMS占用的资源
    NET_ECMS_Fini();
    //
  
    //SMS停止转发码流
    if(lPlayHandle >= 0)
    {
        if (!NET_ESTREAM_StopPlayBack(lPlayHandle))
        {
            printf("NET_ESTREAM_StopPlayBack failed, error code: %d\n", NET_ECMS_GetLastError());
        }
    }
  
    //SMS停止监听服务
    if(lLinkHandle >= 0)
    {
        if (!NET_ESTREAM_StopListenPlayBack(lLinkHandle))
        {
            printf("NET_ESTREAM_StopListenPlayBack failed, error code: %d\n", NET_ECMS_GetLastError());
        }
    }
    //释放被SMS占用的资源
    NET_ESTREAM_Fini();
  
    //释放文件资源
    if(Videofile != NULL)
    {
        fclose(Videofile);
        Videofile = NULL;
    }
  
    printf("Exit!\n");
}

  

 
posted on   EasyNVR  阅读(500)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 提示词工程——AI应用必不可少的技术
· 地球OL攻略 —— 某应届生求职总结
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

点击右上角即可分享
微信分享提示