做过海康视频卡的朋友请进!关于视频录像的问题?

时间:2020-12-04 03:22:36
DS40xxSDK.dll中存在以下连个方法
DLLEXPORT_API int __stdcall StartVideoCapture(HANDLE hChannelHandle);
DLLEXPORT_API int __stdcall StopVideoCapture(HANDLE hChannelHandle);

我在C#中定义了以下方法
/// <summary>
/// 启动主通道编码数据流捕获
/// 说 明: 启动主通道编码数据流捕获。用户程序可以使用直接读取方式,使用
/// StreamDirectReadCallback回调函数直接对数据流进行处理;也可以与H卡一样,通过消息读取方式,等SDK向用户程序发送在 RegisterMessageNotifyHandle中注册的消息,用户程序使用ReadStreamData来读取数据流。
/// </summary>
/// <param name="hChannelHandle">通道句柄</param>
/// <returns>成功返回0;失败返回错误号</returns>
[DllImport("DS40xxSDK.dll")]
public static extern int StartVideoCapture(IntPtr hChannelHandle);

/// <summary>
/// 停止主通道编码数据流捕获
/// </summary>
/// <param name="hChannelHandle">通道句柄</param>
/// <returns>成功返回0;失败返回错误号</returns>
[DllImport("DS40xxSDK.dll")]
public static extern int StopVideoCapture(IntPtr hChannelHandle);

/// <summary>
/// 编码数据流直接读取回调函数委托
/// </summary>
/// <param name="channelNumber">通道号</param>
/// <param name="DataBuf">缓冲区地址</param>
/// <param name="Length">缓冲区长度</param>
/// <param name="FrameType">缓冲区数据帧类型</param>
/// <param name="context">设备上下文</param>
/// <returns></returns>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int STREAM_DIRECT_READ_CALLBACK(uint channelNumber, IntPtr DataBuf, uint Length, int FrameType, IntPtr context);

/// <summary>
/// 编码数据流直接读取回调函数
/// </summary>
/// <param name="channelNumber">通道号</param>
/// <param name="DataBuf">缓冲区地址</param>
/// <param name="Length">缓冲区长度</param>
/// <param name="FrameType">缓冲区数据帧类型</param>
/// <param name="context">设备上下文</param>
/// <returns></returns>
public static int _StreamDirectReadCallBack(uint channelNumber, IntPtr DataBuf, uint Length, int FrameType, IntPtr context)
{

}
在这个回调函数中,我怎么才能把视频数据保存到文件?谢谢
解决问题,全分奉上!

19 个解决方案

#1


DataBuf这个是数据缓冲区的地址.把这块的数据读出来,写入到你自己的流中去.

#2


好强大,学习,收藏,帮顶

#3


可通过System.Runtime.InteropServices.Marshal.Copy(DataBuf, databuffers, 0, databuffers.Length);

#4


从而获取某个地址的数据.databuffers这是一个byte[],你可自己开辟.Length这个可用.

我没搞过视频卡.不过理论上我觉得应该是我上面这样.


  private void vc_VideoCaptured(object sender, VIDEOHDR hdr)
        {
            if (this.chanel == null || !this.chanel.Connected) return;
            try
            {
                byte[] data = new byte[hdr.dwBytesUsed];
                Marshal.Copy(hdr.lpData, data, 0, hdr.dwBytesUsed);
                this.chanel.SendVideo(data);
            }
            catch (System.Exception ex)
            {


            }
        }

#5


视频和音频一起写入?我试下!

#6


@greystar
感谢!

#7


public static int _StreamDirectReadCallBack(uint channelNumber, IntPtr DataBuf, uint Length, int FrameType, IntPtr context)
{



这个函数参数中context在C++中的定义是void *context,标注为"设备上下文",我在C#定义为IntPtr没问题吧?
另外,请教"设备上下文"是什么意思?在C#中应做何理解?

谢谢!

#8


写入是没有问题,不过,这是个回调函数,在启动录像后,将不停的向某个文件中写入数据,我每次录像的
文件只有6KB,代码如下,
public static int _StreamDirectReadCallBack(uint channelNumber, IntPtr DataBuf, uint Length, FrameType_t FrameType, IntPtr context)
        {
            byte[][] FileHeader = new byte[64][];
            FileHeader[channelNumber] = new byte[Length];
            if (FrameType > 0)
            {
                if (FrameType == FrameType_t.PktSysHeader)
                {
                    
                    Marshal.Copy(DataBuf, FileHeader[channelNumber], 0, (int)Length);
                }
            }

            string fileName = "D:\\" + DateTime.Now.ToString("yyyyMMdd") + ".264";

            FileStream fileStream;

            if (!System.IO.File.Exists(fileName))
            {
                System.IO.File.Create(fileName);
            }

            fileStream = System.IO.File.OpenWrite(fileName);
            
            fileStream.Write(FileHeader[channelNumber], 0, (int)Length);
            fileStream.Close();
            return 0;
        }

怎么可以在上次文件里继续写入数据?

#9


你写文件时要使用追加的方式,
public FileStream (
string path,
FileMode mode,
FileAccess access,
FileShare share
)

#10


filemode.Append

#11


fileStream = System.IO.File.Open(fileName,FileMode.Append);


很是感谢!

我再仔细看看他们给的C++示例!

#12




//typedef int (*STREAM_DIRECT_READ_CALLBACK)(ULONG channelNumber,void *DataBuf,DWORD Length,int FrameType,void *context);
int __cdecl StreamDirectReadCallback(ULONG channelNum,void *DataBuf,DWORD Length,int frameType,void *context)
{

BOOL breakable;

int nframetype =0;
CHKVisionDlg *hkdlg = (CHKVisionDlg *)context;

if(frameType >0)
{
if (frameType == PktSysHeader)
{
memcpy(FileHeader[channelNum],DataBuf,Length);
FileHeaderLen = Length;
}
else if (frameType == PktSubSysHeader)
{
memcpy(FileHeaderQcif[channelNum],DataBuf,Length);
FileHeaderLen = Length;
}
if (frameType == PktMotionDetection)
{
int result[4];

if(bOverlayMode)
{
hkdlg->m_VideoWin.DrawVectEx(channelNum, (char *)DataBuf);
}
else
{
hkdlg->m_bMoving[channelNum] = TRUE;
memcpy(motionData[channelNum],(char*)DataBuf,Length);
}

MotionAnalyzer(ChannelHandle[channelNum],(char*)DataBuf,15,result);
if((result[0] + result[1] + result[2] + result[3]))
{
if (channelNum ==0)
TRACE("!!!!!!!!!!!!!!!!CH=%d motion detect result is %d %d %d %d\n",channelNum,result[0],\
result[1],result[2],result[3]);
// Trigger函数的作用是把报警前的数据按setup里面的设置,把相应的警前帧写入文件
if(!hkdlg->baftermotion[channelNum])
alarmFile[channelNum].Trigger(gFileHandle[channelNum]);
//每次有移动侦测帧上来,自动把ualreadywriteframes[channelNum]清为0 ,为写入警后帧作准备
hkdlg->ualreadywriteframes[channelNum]=0;
hkdlg->baftermotion[channelNum]=TRUE;
}
return 0;
}
if (frameType == PktIFrames || frameType ==PktSubIFrames)
breakable = TRUE;
else
breakable = FALSE;

}
gChannelTotalLength[channelNum].QuadPart +=Length;
gCurrentFileLen[channelNum] +=Length;
if ((gCurrentFileLen[channelNum] > gFileSize*1000*1000) && (breakable) )
{
char fileName[256];
CTime m_StartTime1=CTime::GetCurrentTime();
CString csStartTime=m_StartTime1.Format("%Y%m%d%H%M%S");


if (gFileHandle[channelNum])
{
_close(gFileHandle[channelNum]);
sprintf(fileName, "ch%02d_%s.264", channelNum,csStartTime);
gFileHandle[channelNum] = _open(fileName,_O_CREAT|_O_BINARY|_O_WRONLY|_O_TRUNC,_S_IREAD|_S_IWRITE);
_write(gFileHandle[channelNum],FileHeader[channelNum],FileHeaderLen);
}
if (gFileHandleQcif[channelNum])
{
_close(gFileHandleQcif[channelNum]);
sprintf(fileName, "ch%02d_%s_sub.264", channelNum,csStartTime);
gFileHandleQcif[channelNum] = _open(fileName,_O_CREAT|_O_BINARY|_O_WRONLY|_O_TRUNC,_S_IREAD|_S_IWRITE);
_write(gFileHandleQcif[channelNum],FileHeaderQcif[channelNum],FileHeaderLen);
}
gCurrentFileLen[channelNum] -=gFileSize*1000*1000;
}
if(bAlarmFileSave)
{
if(frameType == PktSysHeader)
_write(gFileHandle[channelNum],DataBuf,Length);
else
{
//统计移动侦测帧上来之后的音视频数据,统计它们 ,并把它们写入文件,每一次有 新的移动侦测数据上来,
//ualreadywriteframes都会先清0,然后统计写入文件的音视频数据
hkdlg->ualreadywriteframes[channelNum] += hkdlg->TypeToFrames((FrameType_t)frameType);
if (hkdlg->ualreadywriteframes[channelNum] <6*(25+25)) //录制移动侦测帧上来之后 6秒钟的音视频数据,也就是警后数据
_write(gFileHandle[channelNum],DataBuf,Length);
else
{
//  如果移动侦测上来之后,写入文件的音视频数据已经足够,那么把接下来上来的音视频数据压缓冲,
//  压入缓冲的数据有两种处理方式:1 如果缓冲满了,自动被删除;2 作为下次报警上来的警前帧写入文件; 
alarmFile[channelNum].FramePush(gFileHandle[channelNum],(unsigned char *)DataBuf,Length,(FrameType_t)frameType,breakable);
hkdlg->baftermotion[channelNum] = FALSE;
}
}
}
else
{
if(frameType ==PktAudioFrames)
{
_write(gFileHandle[channelNum],DataBuf,Length);
_write(gFileHandleQcif[channelNum],DataBuf,Length);

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

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

}else 
{
_write(gFileHandle[channelNum],DataBuf,Length);

}
}
return 0;
}




以上这段代码

#13


视频采集卡,我也开发过,是天敏的
up

#14


这个录像让我郁闷了一天!
谁能给点这个回调函数内数据处理的代码?

#15


解决了!!!结贴

#17


你们都是高手啊!学习学习

#18


引用 4 楼 greystar 的回复:
从而获取某个地址的数据.databuffers这是一个byte[],你可自己开辟.Length这个可用.

我没搞过视频卡.不过理论上我觉得应该是我上面这样.


  private void vc_VideoCaptured(object sender, VIDEOHDR hdr)
  {
  if (this.chanel == null || !this.chanel.Conn……

这位仁兄,能帮我看看我说我这个视频采集卡的回调函数吗?我不太明白怎么将视频显示在界面窗口上?
BOOL TwRegisterVideoStreamCallback(TwVideoStreamCallback VideoStreamCallback, void *Context)
功能:注册视频数据流回调函数
参数:[in]VideoStreamReadCallback,指定的回调函数指针
  [in]Context,指定的回调函数上下文指针
返回:成功返回TRUE,否则返回FALSE,调用TwGetLastError获取错误码

#19


引用楼主 goga21cn 的回复:
DS40xxSDK.dll中存在以下连个方法
DLLEXPORT_API int __stdcall StartVideoCapture(HANDLE hChannelHandle);
DLLEXPORT_API int __stdcall StopVideoCapture(HANDLE hChannelHandle);

我在C#中定义了以下方法
/// <summary>
/// ……

typedef int (__stdcall *TwVideoStreamCallback)(int nChannel, VOID *DataBuf, int width, int height, BOOL bField2, void *Context, __int64 pts)
功能:提供视频原始数据流给用户
参数:[in]nChannel,通道索引号
[in]DataBuf,数据缓存的指针
[in]width,视频宽度
[in]height,视频高度
[in]bField2,是否是偶场数据
[in]Context,上下文指针
[in]pts,帧时间戳
返回:成功返回TRUE,否则返回FALSE
怎么来写程序,我买的板卡没有实例,不知道怎么开发?

#1


DataBuf这个是数据缓冲区的地址.把这块的数据读出来,写入到你自己的流中去.

#2


好强大,学习,收藏,帮顶

#3


可通过System.Runtime.InteropServices.Marshal.Copy(DataBuf, databuffers, 0, databuffers.Length);

#4


从而获取某个地址的数据.databuffers这是一个byte[],你可自己开辟.Length这个可用.

我没搞过视频卡.不过理论上我觉得应该是我上面这样.


  private void vc_VideoCaptured(object sender, VIDEOHDR hdr)
        {
            if (this.chanel == null || !this.chanel.Connected) return;
            try
            {
                byte[] data = new byte[hdr.dwBytesUsed];
                Marshal.Copy(hdr.lpData, data, 0, hdr.dwBytesUsed);
                this.chanel.SendVideo(data);
            }
            catch (System.Exception ex)
            {


            }
        }

#5


视频和音频一起写入?我试下!

#6


@greystar
感谢!

#7


public static int _StreamDirectReadCallBack(uint channelNumber, IntPtr DataBuf, uint Length, int FrameType, IntPtr context)
{



这个函数参数中context在C++中的定义是void *context,标注为"设备上下文",我在C#定义为IntPtr没问题吧?
另外,请教"设备上下文"是什么意思?在C#中应做何理解?

谢谢!

#8


写入是没有问题,不过,这是个回调函数,在启动录像后,将不停的向某个文件中写入数据,我每次录像的
文件只有6KB,代码如下,
public static int _StreamDirectReadCallBack(uint channelNumber, IntPtr DataBuf, uint Length, FrameType_t FrameType, IntPtr context)
        {
            byte[][] FileHeader = new byte[64][];
            FileHeader[channelNumber] = new byte[Length];
            if (FrameType > 0)
            {
                if (FrameType == FrameType_t.PktSysHeader)
                {
                    
                    Marshal.Copy(DataBuf, FileHeader[channelNumber], 0, (int)Length);
                }
            }

            string fileName = "D:\\" + DateTime.Now.ToString("yyyyMMdd") + ".264";

            FileStream fileStream;

            if (!System.IO.File.Exists(fileName))
            {
                System.IO.File.Create(fileName);
            }

            fileStream = System.IO.File.OpenWrite(fileName);
            
            fileStream.Write(FileHeader[channelNumber], 0, (int)Length);
            fileStream.Close();
            return 0;
        }

怎么可以在上次文件里继续写入数据?

#9


你写文件时要使用追加的方式,
public FileStream (
string path,
FileMode mode,
FileAccess access,
FileShare share
)

#10


filemode.Append

#11


fileStream = System.IO.File.Open(fileName,FileMode.Append);


很是感谢!

我再仔细看看他们给的C++示例!

#12




//typedef int (*STREAM_DIRECT_READ_CALLBACK)(ULONG channelNumber,void *DataBuf,DWORD Length,int FrameType,void *context);
int __cdecl StreamDirectReadCallback(ULONG channelNum,void *DataBuf,DWORD Length,int frameType,void *context)
{

BOOL breakable;

int nframetype =0;
CHKVisionDlg *hkdlg = (CHKVisionDlg *)context;

if(frameType >0)
{
if (frameType == PktSysHeader)
{
memcpy(FileHeader[channelNum],DataBuf,Length);
FileHeaderLen = Length;
}
else if (frameType == PktSubSysHeader)
{
memcpy(FileHeaderQcif[channelNum],DataBuf,Length);
FileHeaderLen = Length;
}
if (frameType == PktMotionDetection)
{
int result[4];

if(bOverlayMode)
{
hkdlg->m_VideoWin.DrawVectEx(channelNum, (char *)DataBuf);
}
else
{
hkdlg->m_bMoving[channelNum] = TRUE;
memcpy(motionData[channelNum],(char*)DataBuf,Length);
}

MotionAnalyzer(ChannelHandle[channelNum],(char*)DataBuf,15,result);
if((result[0] + result[1] + result[2] + result[3]))
{
if (channelNum ==0)
TRACE("!!!!!!!!!!!!!!!!CH=%d motion detect result is %d %d %d %d\n",channelNum,result[0],\
result[1],result[2],result[3]);
// Trigger函数的作用是把报警前的数据按setup里面的设置,把相应的警前帧写入文件
if(!hkdlg->baftermotion[channelNum])
alarmFile[channelNum].Trigger(gFileHandle[channelNum]);
//每次有移动侦测帧上来,自动把ualreadywriteframes[channelNum]清为0 ,为写入警后帧作准备
hkdlg->ualreadywriteframes[channelNum]=0;
hkdlg->baftermotion[channelNum]=TRUE;
}
return 0;
}
if (frameType == PktIFrames || frameType ==PktSubIFrames)
breakable = TRUE;
else
breakable = FALSE;

}
gChannelTotalLength[channelNum].QuadPart +=Length;
gCurrentFileLen[channelNum] +=Length;
if ((gCurrentFileLen[channelNum] > gFileSize*1000*1000) && (breakable) )
{
char fileName[256];
CTime m_StartTime1=CTime::GetCurrentTime();
CString csStartTime=m_StartTime1.Format("%Y%m%d%H%M%S");


if (gFileHandle[channelNum])
{
_close(gFileHandle[channelNum]);
sprintf(fileName, "ch%02d_%s.264", channelNum,csStartTime);
gFileHandle[channelNum] = _open(fileName,_O_CREAT|_O_BINARY|_O_WRONLY|_O_TRUNC,_S_IREAD|_S_IWRITE);
_write(gFileHandle[channelNum],FileHeader[channelNum],FileHeaderLen);
}
if (gFileHandleQcif[channelNum])
{
_close(gFileHandleQcif[channelNum]);
sprintf(fileName, "ch%02d_%s_sub.264", channelNum,csStartTime);
gFileHandleQcif[channelNum] = _open(fileName,_O_CREAT|_O_BINARY|_O_WRONLY|_O_TRUNC,_S_IREAD|_S_IWRITE);
_write(gFileHandleQcif[channelNum],FileHeaderQcif[channelNum],FileHeaderLen);
}
gCurrentFileLen[channelNum] -=gFileSize*1000*1000;
}
if(bAlarmFileSave)
{
if(frameType == PktSysHeader)
_write(gFileHandle[channelNum],DataBuf,Length);
else
{
//统计移动侦测帧上来之后的音视频数据,统计它们 ,并把它们写入文件,每一次有 新的移动侦测数据上来,
//ualreadywriteframes都会先清0,然后统计写入文件的音视频数据
hkdlg->ualreadywriteframes[channelNum] += hkdlg->TypeToFrames((FrameType_t)frameType);
if (hkdlg->ualreadywriteframes[channelNum] <6*(25+25)) //录制移动侦测帧上来之后 6秒钟的音视频数据,也就是警后数据
_write(gFileHandle[channelNum],DataBuf,Length);
else
{
//  如果移动侦测上来之后,写入文件的音视频数据已经足够,那么把接下来上来的音视频数据压缓冲,
//  压入缓冲的数据有两种处理方式:1 如果缓冲满了,自动被删除;2 作为下次报警上来的警前帧写入文件; 
alarmFile[channelNum].FramePush(gFileHandle[channelNum],(unsigned char *)DataBuf,Length,(FrameType_t)frameType,breakable);
hkdlg->baftermotion[channelNum] = FALSE;
}
}
}
else
{
if(frameType ==PktAudioFrames)
{
_write(gFileHandle[channelNum],DataBuf,Length);
_write(gFileHandleQcif[channelNum],DataBuf,Length);

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

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

}else 
{
_write(gFileHandle[channelNum],DataBuf,Length);

}
}
return 0;
}




以上这段代码

#13


视频采集卡,我也开发过,是天敏的
up

#14


这个录像让我郁闷了一天!
谁能给点这个回调函数内数据处理的代码?

#15


解决了!!!结贴

#16


#17


你们都是高手啊!学习学习

#18


引用 4 楼 greystar 的回复:
从而获取某个地址的数据.databuffers这是一个byte[],你可自己开辟.Length这个可用.

我没搞过视频卡.不过理论上我觉得应该是我上面这样.


  private void vc_VideoCaptured(object sender, VIDEOHDR hdr)
  {
  if (this.chanel == null || !this.chanel.Conn……

这位仁兄,能帮我看看我说我这个视频采集卡的回调函数吗?我不太明白怎么将视频显示在界面窗口上?
BOOL TwRegisterVideoStreamCallback(TwVideoStreamCallback VideoStreamCallback, void *Context)
功能:注册视频数据流回调函数
参数:[in]VideoStreamReadCallback,指定的回调函数指针
  [in]Context,指定的回调函数上下文指针
返回:成功返回TRUE,否则返回FALSE,调用TwGetLastError获取错误码

#19


引用楼主 goga21cn 的回复:
DS40xxSDK.dll中存在以下连个方法
DLLEXPORT_API int __stdcall StartVideoCapture(HANDLE hChannelHandle);
DLLEXPORT_API int __stdcall StopVideoCapture(HANDLE hChannelHandle);

我在C#中定义了以下方法
/// <summary>
/// ……

typedef int (__stdcall *TwVideoStreamCallback)(int nChannel, VOID *DataBuf, int width, int height, BOOL bField2, void *Context, __int64 pts)
功能:提供视频原始数据流给用户
参数:[in]nChannel,通道索引号
[in]DataBuf,数据缓存的指针
[in]width,视频宽度
[in]height,视频高度
[in]bField2,是否是偶场数据
[in]Context,上下文指针
[in]pts,帧时间戳
返回:成功返回TRUE,否则返回FALSE
怎么来写程序,我买的板卡没有实例,不知道怎么开发?

#20