您的位置:首页 > 编程语言 > C#

C# 视频监控系列(9):服务器端——数据捕获(抓图 + 录像)

2009-03-16 18:31 375 查看
前言

录像功能是监控系统中最重要的功能之一,除了本文的功能实现外,还需要你自己考虑合适的存储策略:存储大小、时间段、存储盘符等。

注意

本系列文章限于学习交流,注重过程,由于涉及公司,所以不提供源代码下载,非常抱歉!!但是请大家放心,核心、实现以及其他能够贴出来的代码我都会贴出来,并且争取尽所能的回答留言里的每一个问题,感谢大家关注,欢迎交流 :)

系列

1. C# 视频监控系列(1):准备

2. C# 视频监控系列(2):客户端——封装API

3. C# 视频监控系列(3):客户端——连接服务器

4. C# 视频监控系列(4):客户端——音频接收和抓图

5. C# 视频监控系列(5):客户端——给服务器端发送字符串和录像(数据捕获)

6. C# 视频监控系列(6):服务器端——封装API(上) [HikServer.dll]

7. C# 视频监控系列(7):服务器端——封装API(下) [DS40xxSDK.dll]

8. C# 视频监控系列(8):服务器端——预览和可被客户端连接

9. C# 视频监控系列(9):服务器端——数据捕获(抓图 + 录像)

推荐文章

1. 《控工安防监控知识论坛》提供海康DS4000M/DS4000H卡在安装驱动时常见问题解答等安防专业知识

正文

一、抓图

这个功能没有在VC++服务器端找到对应的代码,但是GOOGLE到了一段CSDN求助的代码:

int ret=GetJpegImage(aa,bb,cc,dd);

if(ret==0)

{

CString str;

str.Format("ch%02d_%s.jpg",iLastSelect,csStartTime);

FILE *pFile=fopen(str.GetBuffer(0),"wb");//Buffer应该是个缓冲区

if(pFile)

{

fwrite(bb,cc,1,pFile); //存储图像

fclose(pFile);

另外一段代码:http://topic.csdn.net/t/20060721/09/4894821.html

C#:

byte[] imageBuf = new byte[704*576*2];

int size = 704*576*2;

HikVisionSDK.GetJpegImage(ChannelHandle, imageBuf, out size, 100);

using (MemoryStream ms = new MemoryStream(imageBuf))

{

Image image = Image.FromStream(ms, true);

image.Save("C:\\1.jpg");

}
注意GetJpegImage的参数说明!!并且请注意,由于这个示例,发现前面的(GetJpegImage/GetOriginalImage)API错误了,请你及时更新!!

public static extern int GetOriginalImage(IntPtr hChannelHandle, byte[] ImageBuf, out int Size);

public static extern int GetJpegImage(IntPtr hChannelHandle, byte[] ImageBuf, out int Size, uint nQuality);

保存为bmp的方法请自行尝试,应该是差不多的: )

二、录像

关于录像的文件总共有三个部分,分别是文件头、数据流和文件尾,这里先给出代码,然后再进行说明。

VC++:

CHKVisionDlg::OnStart()

for(int i = 0; i < GetTotalDSPs(); i++){

m_bDspPreset[i]=TRUE;

if(m_bDspPreset[i]){

char fileName[256];

sprintf(fileName, "d:\\stream%d_%d.264", i, gFileNum++/GetTotalDSPs());

gFileHandle[i] = _open(fileName, _O_CREAT | _O_BINARY | _O_WRONLY| _O_TRUNC, _S_IREAD | _S_IWRITE);

if(gFileHandle[i] == -1){

TRACE("channel %d file open error\n,i");

return;

}

gChannelFrames[i] = 0;

gChannelTotalLength[i] = 0;

gChannelFramesLost[i] = 0;

gChannelOverflow[i] = 0;

gCurrentFileLen[i] = 0;

_write(gFileHandle[i], FileHeader[i], FileHeaderLen);

// could not be start again untill stopped first

//m_bDspPreset[i] = FALSE;

gCaptureStartedNum++;

// let the threads have chance to run

//Sleep(500);

}else

gFileHandle[i] = -1;

}
StreamDirectReadCallback

int __cdecl StreamDirectReadCallback(ULONG channelNum,void *DataBuf,DWORD Length,int frameType,void *context)

{

//CHKVisionDlg * lpDlg = (CHKVisionDlg*)context;

//return lpDlg->ProcCallBack(channelNum, DataBuf, Length, frameType);

int i,status=0;

CString ctip;

int nframetype =0;

// if cap images we need clean the queue here

// if (!bCapture)

// return 0;

// no errors

if(frameType > 0) {

if(frameType == PktSysHeader){

// store the file header

memcpy(FileHeader[channelNum], DataBuf, Length);

FileHeaderLen = Length;

TRACE("channel %d get the file header !\n",channelNum);

}

if(frameType == PktIFrames || frameType ==PktSubIFrames){

status = 1;

}

else{

status = 0;

}

if(frameType == PktMotionDetection){

// m_VideoWin.DrawVect(channelNum, (char *)DataBuf, Length);

return 0;

}

if(frameType == PktOrigImage){

return 0;

}

}

if(Length == 0){

TRACE("no data ?\n");

return 0;

}

// if(frameType == PktIFrames){

// int iii=1;

// }

ULONG currentTime = timeGetTime();

gChannelTotalLength[channelNum] += Length;

gCurrentFileLen[channelNum] += Length;

if(currentTime > StartTime+1000){

CString str,str2;

str.Format("%d", (gChannelTotalLength[dcurrentwin] *8/(currentTime - StartTime)));

for(i=0;i<g_nChannelTotal;i++)

gChannelTotalLength[i] = 0;

StartTime= currentTime;

CHKVisionDlg *pMain = (CHKVisionDlg *)AfxGetMainWnd();

pMain->GetDlgItem(IDC_BPS)->SetWindowText((LPCTSTR)str);

}

// if (m_sframe && channelNum ==0)

// {

// if((frameType == PktSFrames && nframetype ==4 )||(frameType == PktSysHeader))

// {

// MP4_ServerWriteData(channelNum,(unsigned char *)DataBuf, Length,frameType,status);

// }

// }

// MP4_ServerWriteData(channelNum,(unsigned char *)DataBuf, Length,frameType,status);

if(frameType ==PktAudioFrames)

{

_write(gFileHandleQcif[channelNum],DataBuf,Length);

MP4_ServerWriteDataEx(channelNum,(unsigned char *)DataBuf, Length,frameType,status,1);

_write(gFileHandle[channelNum], DataBuf, Length);

MP4_ServerWriteDataEx(channelNum,(unsigned char *)DataBuf, Length,frameType,status,0);

}else if (frameType ==PktSubIFrames || frameType ==PktSubPFrames || frameType == PktSubBBPFrames || frameType == PktSubSysHeader)

{

_write(gFileHandleQcif[channelNum],DataBuf,Length);

MP4_ServerWriteDataEx(channelNum,(unsigned char *)DataBuf, Length,frameType,status,1);

}else

{

//_write(gFileHandle[channelNum], DataBuf, Length);

MP4_ServerWriteDataEx(channelNum,(unsigned char *)DataBuf, Length,frameType,status,0);

}

return 0;

}
CHKVisionDlg::OnStop()

for(int i = 0; i < GetTotalDSPs(); i++){

if(m_bDspPreset[i]){

ASSERT(gFileHandle[i] != -1);

// StopVideoCapture(ChannelHandle[i]);

//lseek(gFileHandle[i], 0, SEEK_SET);

//FRAMES_STATISTICS fs;

//GetFramesStatistics(ChannelHandle[i], &fs);

//ULONG frames = fs.AudioFrames + fs.VideoFrames;

//TRACE("channel %i has %x frames written\n", i, frames);

#define END_CODE 0x00000002

ULONG endCode = END_CODE;

_write(gFileHandle[i], &endCode, sizeof(ULONG));

_close(gFileHandle[i]);

///add v34

if (bEncodeCifAndQcif[i])

_close(gFileHandleQcif[i]);

gCaptureStartedNum--;

}

}
代码说明:

1. 从StartCap和StopCap的按钮事件可以看得出主要实现写文件头和文件尾的功能,注意_write函数。

2. 而上一章我们讲到了回调函数StreamDirectReadCallback,主要是将数据写到内存中,从代码能看出回调中是边写内存边写文件的代码,而且输出就是.264文件。由于回调从启动开始(允许被客户端访问),就一直不停的在调用这个回调,根据断点调试可以看得出当frameType == PktSysHeader时表示的就是文件头,并且只执行一次,这样在点击StartCap按钮时就直接将这个保存的文件头的数据写入文件了,用UE打开.264的文件可以发现前几个字符总是以4HKH开头的文件。

3. 注意gFileHandle是一个文件指针数组,文件被打开后回调中就一直往这个文件指针写数据!!

C#:

//用于存放头文件

byte[] FileHeader;

//文件头长度

int FileHeaderLen;

//是否开始捕获文件 0 未启用 1 启用

volatile int CaptureState;

/// <summary>

/// 开始录像

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void btnStart_Click(object sender, EventArgs e)

{

//写入头文件

using (FileStream fs = new FileStream("C:\\hik.264", FileMode.Create))

{

BinaryWriter bw = new BinaryWriter(fs);

bw.Write(FileHeader);

bw.Flush();

bw.Close();

}

CaptureState = 1;

}

uint endCode = 0x00000002;

/// <summary>

/// 停止录像

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void btnStop_Click(object sender, EventArgs e)

{

CaptureState = 0;

using (FileStream fs = new FileStream("C:\\hik.264", FileMode.Append))

{

BinaryWriter bw = new BinaryWriter(fs);

bw.Write(endCode);

bw.Close();

}

}

public int STREAM_DIRECT_READ_CALLBACK1(int channelNum, IntPtr DataBuf, int Length, FrameType_t frameType, IntPtr context)

{

//int status = 0;

//HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 0);

//return 0;

int status = 0;

if (frameType > 0)

{

if (frameType == FrameType_t.PktSysHeader)

{

FileHeader = new byte[Length];

Marshal.Copy(DataBuf, FileHeader, 0, Length);

FileHeaderLen = Length;

}

if (frameType == FrameType_t.PktIFrames || frameType == FrameType_t.PktSubIFrames)

status = 1;

else

status = 0;

if (frameType == FrameType_t.PktMotionDetection || frameType == FrameType_t.PktOrigImage)

return 0;

}

if (Length == 0)

{

//TRACE("no data ?\n");

return 0;

}

if (frameType == FrameType_t.PktAudioFrames)

{

WriterVideoCapture(Length, DataBuf);

//写文件

// _write(gFileHandleQcif[channelNum],DataBuf,Length);

//HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 1);

// _write(gFileHandle[channelNum], DataBuf, Length);

HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 0);

}

else if (frameType == FrameType_t.PktSubIFrames || frameType == FrameType_t.PktSubPFrames || frameType == FrameType_t.PktSubBBPFrames || frameType == FrameType_t.PktSubSysHeader)

{

// _write(gFileHandleQcif[channelNum],DataBuf,Length);

HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 1);

}

else

{

WriterVideoCapture(Length, DataBuf);

HikServer.MP4_ServerWriteDataEx(channelNum, DataBuf, Length, (int)frameType, status, 0);

}

return 0;

}

/// <summary>

/// 将数据流写入视频文件

/// </summary>

/// <param name="length"></param>

/// <param name="dataBuf"></param>

private void WriterVideoCapture(int length, IntPtr dataBuf)

{

if (CaptureState == 1)

{

using (FileStream fs = new FileStream("C:\\hik.264", FileMode.Append))

{

BinaryWriter bw = new BinaryWriter(fs);

byte[] byteBuf = new byte[length];

Marshal.Copy(dataBuf, byteBuf, 0, length);

bw.Write(byteBuf);

bw.Flush();

bw.Close();

}

}

}
代码说明:

1. 回调函数STREAM_DIRECT_READ_CALLBACK1是在上篇文章的基础上修改的,也主要是参照的VC++的源代码改写的。

2. CaptureState变量主要用于STREAM_DIRECT_READ_CALLBACK1中控制是否写文件。

3. btnStart_Click与btnStop_Click分别代表界面上的开始录像和停止录像按钮。

4. 注意写文件的方式,开始录像用FileMode.Create,持续写入用FileMode.Append。

补充

1. 录像的时候务必考虑单录像文件的大小以及磁盘空间不够的问题,最好还能考虑下分时段监控等。

2. 注意保存文件头的变量FileHeader,如果分文件连续保存的话有可能出现第一个文件能播放,后面的都不能播放了,可能是文件头变量的数据类型问题,你可以换byte[] -> IntPtr保存试试看。

3. 自带的示例里面有播放器极其源码,打开播放器,直接将.264文件拖拽到里面就可以播放了;如果报错那么说明你的录像有问题!!

结束

虽然代码都给出来了,但是里面整个过程还是需要理解的,一定要配合VC++自带的例子进行调试编写。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐