写在前面:
对于海康的技术支持我不想多做评价,电话打不通邮件不回,一些找寻解决方案的灵感多来自于海康的论坛和官网给的demo程序。
不过问题解决后感觉 靠自己,不放弃 是多么重要。
声明:此处的解决方法是结合海康论坛将官网demo里的有效代码搬移过来,于茫茫代码中找到自己所需也是挺烦燥的一件事,仅仅为别人节省一点时间也是好的,废话不多说啦~
得到摄像头数据流思路:
通过两个回调函数(这个是sdk里没有的)完成,在NET_DVR_SetRealDataCallBack(m_lRealHandle, g_RealDataCallBack_V30,(DWORD)m_CameraNo)处设置回调函数进行数据包的解析,当一帧图像的数据包完全到达时,会调用PlayM4_SetDecCallBack(LPort, decFunction)进行解码,这里decFunction就是解码的回调函数,在这个函数里完成YV12的数据转成你所需要的格式然后以队列或其他随便什么形式传递出去。
ps:1、所谓回调函数就是一种用函数指针调用的函数,多数情况下调用是系统或是其他函数调用的
2、这里的回调函数参数都是sdk规定好的(NET_DVR_SetRealDataCallBack函数声明里)
3、这里回调函数像是sdk函数在进行流处理时留给我们的一个接口,流通过参数传递进来,你只要想法把数据通过某个形势传递出去就可以在程序其他部分进行处理了~
g_RealDataCallBack_V30
函数体如下,在demo代码的基础上做了一小丢丢修改(重点的修改已经加粗字体),这个回调函数是在数据包到来时进行解码的,可以看到有数据包头等等,我只在其中加入了在多个摄像头情况下标识摄像头是哪一个的一点东西:
DWORD dwWinIndex = pUser;//从回调函数的参数 pUser中得到摄像头的index
g_CamOperate[dwWinIndex].m_port=LPort;//g_CamOperate是我将有关摄像头初始化和读函数等封装的一个类,由于后面解码的回调函数(decFunction)是全局函数(是这样叫么?),所有摄像头都通过此函数进行解码,所以怎么在解码的回调函数中标识出不同的摄像头数据很重要,而端口号在每次解码中会由参数传递到解码函数中,所以这里进行赋值就到类的属性中就是为了后面区分不同摄像头数据的~
- void CALLBACK g_RealDataCallBack_V30(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, DWORD pUser)//数据流回调
- {
- BOOL iRet = FALSE;
- DWORD dwWinIndex = pUser;
- //get player port to protect the process safe
- LPort=g_CamOperate[dwWinIndex].m_port;
- switch (dwDataType)
- {
- case NET_DVR_SYSHEAD://coming the stream header, open stream
- //soft decode
- if (LPort=-1)
- {
- if (!PlayM4_GetPort(&LPort))
- {
- return;
- }
- }
- g_CamOperate[dwWinIndex].m_port=LPort;
- if (dwBufSize > 0)
- {
- //set as stream mode, real-time stream under preview
- if (!PlayM4_SetStreamOpenMode(LPort, STREAME_REALTIME))
- {
- TRACE("Set StreamMode Error!");
- return;
- }
- //start player
- if (!PlayM4_OpenStream(LPort, pBuffer, dwBufSize, 1024*1024))
- {
- TRACE("PlayM4_OpenStream err");
- return;
- }
- if(!PlayM4_SetDecCallBack(LPort, decFunction))
- {
- TRACE("PlayM4_SetDecCallBack err");
- return;
- }
- if (!PlayM4_SetDisplayBuf(LPort, 6))
- {
- TRACE("PlayM4_SetDisplayBuf err");
- return;
- }
- //start play, set play window
- if (!PlayM4_Play(LPort, NULL))
- {
- TRACE("PlayM4_Play err");
- break;
- }
- //set frame buffer number
- }
- break;
- case NET_DVR_STREAMDATA:
- //start soft decode
- if (dwBufSize > 0 && LPort != -1)
- {
- if(DecSignal)
- {
- DecSignal=FALSE;
- }
- if (!PlayM4_InputData(LPort, pBuffer, dwBufSize))
- {
- TRACE("PlayM4_InputData err");
- }
- }
- break;
- //case NET_DVR_REALPLAYNETCLOSE:
- case EXCEPTION_PREVIEW:
- break;
- default:
- break;
- }
- return ;
- }
decFunction:
这里decFunction完成将解码出来的数据转移出去的功能,参数中nPort是标识摄像头端口的,pBuf是流数据,剩下就是流的属性之类的~DRAWSELF是标识得到rgb数据还是灰度数据的。yv12toYUV完成将yv12转成YVU数据,编码的事情不多说了,随便找找遍地都是~
g_CamOperate[pWndIndex].InsertFrame(pImg);//类的一个方法,完成将解码所得到的数据insert到队列里,对应的有GetFrame方法,这样进行数据处理可以在需要的地方通过g_CamOperate进行调用啦~
- void CALLBACK decFunction(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2)//解码回调
- {
- //得到解码后的图像数据
- //BOOL bRet = FALSE;
- #if DRAWSELF
- IplImage* pImgYCrCb = cvCreateImage(cvSize(pFrameInfo->nWidth,pFrameInfo->nHeight), 8, 3);//得到图像的Y分量
- yv12toYUV(pImgYCrCb->imageData, pBuf, pFrameInfo->nWidth,pFrameInfo->nHeight,pImgYCrCb->widthStep);//得到全部RGB图像
- IplImage* pImg = cvCreateImage(cvSize(pFrameInfo->nWidth,pFrameInfo->nHeight), 8, 3);
- cvCvtColor(pImgYCrCb,pImg,CV_YCrCb2RGB);
- #else
- IplImage* pImg = cvCreateImage(cvSize(pFrameInfo->nWidth,pFrameInfo->nHeight), 8, 1);
- memcpy(pImg->imageData,pBuf,pFrameInfo->nWidth*pFrameInfo->nHeight);
- #endif
- int pWndIndex=0;
- //找到对应的摄像头和端口
- for (int i=0; i<MAX_OUTPUTS; i++)
- {
- if (g_CamOperate[i].m_port == nPort)
- {
- pWndIndex = i;
- break;
- }
- }
- DecSignal=TRUE;
- g_CamOperate[pWndIndex].InsertFrame(pImg);
- #if DRAWSELF
- cvReleaseImage(&pImgYCrCb);
- #endif
- //bRet = 0;
- }