海康视频流获取技术总结

海康监控设备在国内相关领域占有很大的份额,至少目前我接触到的项目或公司都是使用海康设备进行监控的。所以本文主要针对海康设备来进行视频流的获取。

视频流

1 概念说明

硬盘录像机:海康产品的硬盘录像机分为DVR、NVR和CVR三种,DVR是模拟机和同轴缆信号的硬盘录像机,不需要配置IP;NVR是网络型录像机,必须配置IP;CVR是更高级的NVR或者DVR,数据集中存储的NVR(预研用硬盘录像机为CVR)。

2 技术实现

技术实现

海康监控设备视频流获取技术以上技术路线图所示,首先初始化SDK,其次设置连接超时时间、重连时间与异常消息回调函数;接着通过提供硬盘录像机IP、账号、密码、端口号等信息进行登录;然后通过设置播放句柄、通道、流类型、连接方式、是否阻塞等播放信息来进行播放,同时通过设置播放回调函数来获取实时视频流;之后通过软解码技术将视频流逐帧解码为YU12格式,然后编写算法将YU12格式数据转换为RGBA数据,从而完成整个过程的操作。

3 示例代码

该技术用到的海康SDK包括HCNetSDK.lib、HCNetSDK.dll、PlayCtrl.lib、PlayCtr.dll,并使用opencv进行视频的试试显示。具体示例代码如下所示:

#include "Windows.h"
#include "HCNetSDK.h"
#include <stdio.h>
#include <time.h>
#include "plaympeg4.h"
#include "opencv/cv.h"
#include "opencv2/photo.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"




void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG IUserID, LONG IHandle, void *pUser)
{
    char tempbuf[256] = {};
    switch (dwType)
    {

    case EXCEPTION_RECONNECT:
        printf("--------reconnect-------%d\n", time(NULL));
        break;
    default:
        break;
    }
}

bool YV12_to_RGB32(unsigned char* pYV12, unsigned char* pRGB32, int iWidth, int iHeight)
{
    if (!pYV12 || !pRGB32)
        return false;

    const long nYLen = long(iHeight * iWidth);
    const int nHfWidth = (iWidth >> 1);

    if (nYLen < 1 || nHfWidth < 1)
        return false;

    unsigned char* yData = pYV12;
    unsigned char* vData = pYV12 + iWidth*iHeight + (iHeight / 2)*(iWidth / 2);//&vData[nYLen >> 2];  
    unsigned char* uData = pYV12 + iWidth*iHeight;// &yData[nYLen];  
    if (!uData || !vData)
        return false;

    int rgb[4];
    int jCol, iRow;
    for (iRow = 0; iRow < iHeight; iRow++)
    {
        for (jCol = 0; jCol < iWidth; jCol++)
        {
            rgb[3] = 1;

            int Y = yData[iRow*iWidth + jCol];
            int U = uData[(iRow / 2)*(iWidth / 2) + (jCol / 2)];
            int V = vData[(iRow / 2)*(iWidth / 2) + (jCol / 2)];
            int R = Y + (U - 128) + (((U - 128) * 103) >> 8);
            int G = Y - (((V - 128) * 88) >> 8) - (((U - 128) * 183) >> 8);
            int B = Y + (V - 128) + (((V - 128) * 198) >> 8);

            // r分量值   
            R = R < 0 ? 0 : R;
            rgb[2] = R > 255 ? 255 : R;
            // g分量值  
            G = G < 0 ? 0 : G;
            rgb[1] = G > 255 ? 255 : G;
            // b分量值   
            B = B < 0 ? 0 : B;
            rgb[0] = B > 255 ? 255 : B;
            pRGB32[4 * (iRow*iWidth + jCol) + 0] = rgb[0];
            pRGB32[4 * (iRow*iWidth + jCol) + 1] = rgb[1];
            pRGB32[4 * (iRow*iWidth + jCol) + 2] = rgb[2];
            pRGB32[4 * (iRow*iWidth + jCol) + 3] = rgb[3];
        }
    }

    return true;
}

void CALLBACK g_DecCBFun(long nPort, char* pBuf, long nSize, FRAME_INFO* pFrameInfo, long nReserved1, long nReserved2)
{
    long lFrameType = pFrameInfo->nType;
    if (lFrameType == T_AUDIO16)
    {
        //printf("Audio nStap:%d\n", pFrameInfo->nStamp);
    }
    else if (lFrameType == T_YV12)
    {
    
    
        char* rgba = new char[pFrameInfo->nHeight * pFrameInfo->nWidth * 4];

        //  YV12_to_RGB24((unsigned char*)pBuf, (unsigned char*)rgb, pFrameInfo->nWidth, pFrameInfo->nHeight);
        YV12_to_RGB32((unsigned char*)pBuf, (unsigned char*)rgba, pFrameInfo->nWidth, pFrameInfo->nHeight);

        cv::Mat img(pFrameInfo->nHeight,pFrameInfo->nWidth,CV_8UC4,rgba);
        cv::resize(img, img, cv::Size(img.size().width*0.5, img.size().height*0.5));
        cv::imshow("video", img);
        cv::waitKey(1);
    
    }
}

LONG nPort = -1;

void CALLBACK g_RealDataCallBack(LONG IRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void* pUser)
{
    DWORD dRet = 0;
    BOOL inData = FALSE;

    switch (dwDataType)
    {
    case NET_DVR_SYSHEAD:
        if (!PlayM4_GetPort(&nPort))
        {
            break;
        }

        if (!PlayM4_OpenStream(nPort,pBuffer,dwBufSize,1024*1024))
        {
            dRet = PlayM4_GetLastError(nPort);
            break;
        }

        if (!PlayM4_SetDecCallBack(nPort,g_DecCBFun))
        {
            dRet = PlayM4_GetLastError(nPort);
            break;
        }

        if (!PlayM4_Play(nPort,NULL))
        {
            dRet = PlayM4_GetLastError(nPort);
            break;
        }
    case NET_DVR_STREAMDATA:
        if (dwBufSize > 0 && nPort != -1)
        {
            inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
            while (!inData)
            {
                Sleep(10);
                inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
                printf("PlayM4_InputData faild %d\n",PlayM4_GetLastError(nPort));

            }
        }
        break;
    default:
        inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
        while (!inData)
        {
            Sleep(10);
            inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
            OutputDebugString("PlayM4_InputData failed 2\n");
        }
        break;
    }
}

void main()
{
    //init sdk
    NET_DVR_Init();

    //set reconnect time
    NET_DVR_SetConnectTime(2000, 1);
    NET_DVR_SetReconnect(10000, true);



    //set recall func
    NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL);

    //get console handler


    //register device
    LONG IUserID;
    NET_DVR_USER_LOGIN_INFO struLoginInfo = { 0 };
    NET_DVR_DEVICEINFO_V40 struDeviceInfo = { 0 };

    strcpy_s((char*)struLoginInfo.sDeviceAddress, sizeof(struLoginInfo.sDeviceAddress), "192.168.3.62");
    strcpy_s((char*)struLoginInfo.sUserName,sizeof(struLoginInfo.sUserName), "admin");
    strcpy_s((char*)struLoginInfo.sPassword,sizeof(struLoginInfo.sPassword), "abcd-1234");
    struLoginInfo.wPort = 8000;
    struLoginInfo.bUseAsynLogin = 0;

    IUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfo);
    if (IUserID < 0)
    {
        printf("login failed, error code:%d\n", NET_DVR_GetLastError());
        NET_DVR_Cleanup();
        return;
    }

//  printf("channel num:%d\n", struDeviceInfo.struDeviceV30.byChanNum);
//  printf("start channel:%d\n", struDeviceInfo.struDeviceV30.byStartChan);
    NET_DVR_IPPARACFG_V40 IpAccessCfg;
    memset(&IpAccessCfg, 0, sizeof(IpAccessCfg));
    DWORD  dwReturned;

    if (!NET_DVR_GetDVRConfig(IUserID, NET_DVR_GET_IPPARACFG_V40, 0, &IpAccessCfg, sizeof(NET_DVR_IPPARACFG_V40), &dwReturned))
    {
        return;
    }
    LONG channel;
    for (int i = 0; i < MAX_IP_CHANNEL; ++i)
    {
        if (IpAccessCfg.struStreamMode[i].uGetStream.struChanInfo.byEnable)
        {
            channel = i + IpAccessCfg.dwStartDChan;
            break;
        }
    }



    //display
    LONG IRealPlayHandle;

    NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
    struPlayInfo.hPlayWnd = NULL;
    struPlayInfo.lChannel = channel;
    struPlayInfo.dwStreamType = 0;
    struPlayInfo.dwLinkMode = 0;
    struPlayInfo.bBlocked = 1;


    //
//  NET_DVR_CLIENTINFO ClientInfo;
//  ClientInfo.hPlayWnd = hWnd;
//  ClientInfo.lChannel = 0;
//  ClientInfo.lLinkMode = 0;
//  ClientInfo.sMultiCastIP = NULL;
//  //TRACE("Channel number:%d\n", ClientInfo.lChannel);
//  IRealPlayHandle = NET_DVR_RealPlay_V30(IUserID, &ClientInfo, NULL, NULL, TRUE);

    IRealPlayHandle = NET_DVR_RealPlay_V40(IUserID, &struPlayInfo, g_RealDataCallBack, NULL);
    if (IRealPlayHandle<0)
    {
        printf("play failed, error code:%d\n", NET_DVR_GetLastError());
        NET_DVR_Logout(IUserID);
        NET_DVR_Cleanup();
        return;
    }

    Sleep(10000000000000000);

    //close display
    NET_DVR_StopRealPlay(IRealPlayHandle);
    //logout
    NET_DVR_Logout(IUserID);
    //release
    NET_DVR_Cleanup();
    return;

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,639评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,277评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,221评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,474评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,570评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,816评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,957评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,718评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,176评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,511评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,646评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,322评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,934评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,755评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,987评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,358评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,514评论 2 348

推荐阅读更多精彩内容