由于种种原因,感觉还是调用别人的sdk效果比自己写的要好的多,机缘巧合之下,我用雄迈的sdk进行了二次开发。网上基本没有教程,对c++真的不熟悉,很是吃力。
雄迈的NetSdk的ui界面使用的是MFC开发的,因此我也用了MFC,开发环境:vc2017
下面这个链接下载雄迈的sdk
下载的是NetSdk
目录
使用MFC
准备工作
MFC界面搭建
C++代码
存在问题
使用MFC
在vc安装器里面加装MFC,大概2G左右
创建MFC的项目文件,起名字就随便了。
准备工作
在开始之前,在下载的sdk里面把以下文件放到项目根目录
H264Play.dll; H264Play.h; H264Play.lib
NetSdk.dll; NetSdk.lib; NetSdk.h;
StreamRead.dll
嗨,运行看他报啥错,你就往里面加啥就行了。
MFC界面搭建
简单测试,简单界面
C++代码
// ShowDialog.cpp: 实现文件
//
#include "pch.h"
#include "MFC_XM.h"
#include "ShowDialog.h"
#include "afxdialogex.h"
#include "netsdk.h"
#include <stdio.h>
#include <iostream>
#include <string>
#include "Resource.h"
#include "H264Play.h"
#include <direct.h>
#include <IO.H>
using namespace std;
// ShowDialog 对话框
IMPLEMENT_DYNAMIC(ShowDialog, CDialogEx)
ShowDialog::ShowDialog(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_SHOW_DIALOG, pParent)
{ //播放端口
_PlaySDKHandle = -1;
//实时播放句柄NetSdk媒体句柄
watchHandle = -1;
//下载选项
m_bRecord = false;
//登录句柄
LoginHandle = 0;
}
ShowDialog::~ShowDialog()
{
}
void ShowDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(ShowDialog, CDialogEx)
ON_BN_CLICKED(IDC_BUTTON_OPENCAM, &ShowDialog::OnBnClickedButtonOpencam)
ON_BN_CLICKED(IDC_BUTTON_DOWN, &ShowDialog::OnBnClickedButtonDown)
END_MESSAGE_MAP()
//实时数据回调函数.设置实时监视数据回调,给用户提供设备流出的数据。
int __stdcall RealDataCallBack(long lRealHandle,
long dwDataType, unsigned char *pBuffer,
long lbufsize, USEROBJECT dwUser)
{
ShowDialog *pDataChnl = (ShowDialog*)dwUser;
H264_PLAY_InputData(pDataChnl->GetPlaySDKHandle(), pBuffer, lbufsize);
return 1;
}
//
int __stdcall RealDataCallBack_V2(long lRealHandle, const PACKET_INFO_EX *pFrame, USEROBJECT dwUser)
{
ShowDialog *pDataChnl = (ShowDialog*)dwUser;
STDH264_PACKET_INFO stdH264Info;
memset(&stdH264Info, 0, sizeof(STDH264_PACKET_INFO));
if (pFrame->nPacketType == VIDEO_I_FRAME) {
pFrame->pPacketBuffer + 16;
pFrame->dwPacketSize - 16;
H264_PLAY_InputData(pDataChnl->GetPlaySDKHandle(), (unsigned char*)pFrame->pPacketBuffer, pFrame->dwPacketSize);
}
else if (pFrame->nPacketType == VIDEO_P_FRAME) //P帧
{
pFrame->pPacketBuffer + 8;
pFrame->dwPacketSize - 8;
H264_PLAY_InputData(pDataChnl->GetPlaySDKHandle(), (unsigned char*)pFrame->pPacketBuffer, pFrame->dwPacketSize);
}
return 1;
}
//播放图像回调函数,有啥用啊不知道
void __stdcall pProc(LONG nPort, LPCSTR pBuf, LONG nSize, LONG nWidth, LONG nHeight, LONG nStamp, LONG nType, USEROBJECT nUser)
{
}
//ShowDialog 消息处理程序
void ShowDialog::OnBnClickedButtonOpencam()
{
//注册设备
CString account;//
CString password;
CString ip;
CString port;
GetDlgItem(IDC_EDIT_ACCOUNT)->GetWindowText(account);
GetDlgItem(IDC_EDIT_PASSWORD)->GetWindowText(password);
GetDlgItem(IDC_EDIT_IP)->GetWindowText(ip);
GetDlgItem(IDC_EDIT_PORT)->GetWindowText(port);
//设备信息,属于输出参数
H264_DVR_DEVICEINFO OutDev;
int nError = 0;
//重连时间
H264_DVR_SetConnectTime(3000, 1);
//获得的是CString 要转换成sdk能用的格式
//转不成的时候去项目->属性->常规->把字符串集改成多字符串集
LPTSTR Account = (LPTSTR)(LPCTSTR)account;
LPTSTR Password = (LPTSTR)(LPCTSTR)password;
LPTSTR Ip = (LPTSTR)(LPCTSTR)ip;
//Cstring 转换short
int Port = atoi((LPCTSTR)port);
LoginHandle = H264_DVR_Login(Ip, Port, Account, Password, &OutDev, &nError);
if (LoginHandle <= 0) {
AfxMessageBox("连接摄像头失败");
}
else {
GetDlgItem(IDC_STATIC_CAM)->SetWindowText("摄像头已连接");
}
//显示画面
CRect rect;
CWnd *pWnd = GetDlgItem(IDC_FRAME);//IDC_picture为picture控件ID
pWnd->GetClientRect(&rect);//rc为控件的大小。
int y = rect.Height();
int x = rect.Width();
//获取实时播放句柄
//播放窗口句柄
HWND hWnd = GetDlgItem(IDC_FRAME)->m_hWnd;
//HWND myhWnd = (HWND)ui->myVideo->winId();
//cout << "hWnd" << myhWnd << endl;
// 播放句柄IDC_FRAME
H264_DVR_CLIENTINFO playstru;
playstru.nChannel = 0;//1的时候就报错-11202,说明是主码流预览,或者连接数过多
playstru.nStream = 0;
playstru.nMode = 0;
//playstru.hWnd = hWnd;
//实时监视句柄
//long watchHandle = 0;
watchHandle = H264_DVR_RealPlay(LoginHandle, &playstru);
if (watchHandle <= 0) {
AfxMessageBox("实时句柄获得失败");
}
if (_PlaySDKHandle == -1)
{
//BYTE byFileHeadBuf; &byFileHeadBuf
//获得播放端口
H264_PLAY_GetPort(&_PlaySDKHandle);
//打开流数据
if (H264_PLAY_OpenStream(_PlaySDKHandle, NULL, 0, SOURCE_BUF_MIN * 50))
{
//设置信息帧回调
//H264_PLAY_SetInfoFrameCallBack(_PlaySDKHandle, videoInfoFramCallback, (DWORD)this);
H264_PLAY_SetStreamOpenMode(_PlaySDKHandle, STREAME_REALTIME);
//只播放I帧,可降低cpu使用率
//H264_PLAY_OnlyIFrame(_PlaySDKHandle, true);
H264_PLAY_SetDisplayCallBack(_PlaySDKHandle, pProc, (USEROBJECT)this);
H264_PLAY_AdjustFluency(_PlaySDKHandle, 7);
//H264_DVR_SetRealDataCallBack(watchHandle, RealDataCallBack, (USEROBJECT)this);
//H264_PLAY_SetDecCallBack(m_nIndex, nProc);
H264_PLAY_Play(_PlaySDKHandle, hWnd);
H264_DVR_SetRealDataCallBack_V2(watchHandle, RealDataCallBack_V2, (USEROBJECT)this);
//第一次调用播放之前要调用H264_PLAY_GetPort, H264_PLAY_OpenStream两个之后才能调用
//H264_PLAY_InputData这个函数,而这个函数通过H264_DVR_SetRealDataCallBack回调函数获得摄像头的播放通道号
//应该就相当于用这个把播放通道定义一下,但是他是怎么做到画面刷新的呢
//H264_DVR_SetRealDataCallBack_V2(watchHandle, RealDataCallBack_V2, (USEROBJECT)this);
//设置实时显示,这个是有用的,但是设置帧率和画面质量就不对了
}
}
}
BOOL ShowDialog::OnInitDialog()
{
//对话框初始化
CDialogEx::OnInitDialog();
GetDlgItem(IDC_EDIT_ACCOUNT)->SetWindowText("admin");
GetDlgItem(IDC_EDIT_PASSWORD)->SetWindowText("");
GetDlgItem(IDC_EDIT_IP)->SetWindowText("192.168.0.90");
GetDlgItem(IDC_EDIT_PORT)->SetWindowText("34567");
// TODO: 在此添加额外的初始化
//初始化sdk,这里先这样写吧。最好是改变界面的文本,报错弹窗口
bool iResult = H264_DVR_Init((fDisConnect)NULL, NULL);
if (iResult == true) {
GetDlgItem(IDC_STATIC_SDK)->SetWindowText("sdk就绪");
}
else {
GetDlgItem(IDC_STATIC_SDK)->SetWindowText("sdk未就绪");
}
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
void ShowDialog::OnBnClickedButtonDown()
{
// TODO: 在此添加控件通知处理程序代码
//获取当前窗口播放句柄
if (watchHandle <= 0)
{
AfxMessageBox("下载失败");
}
CString cFilename;
CTime time = CTime::GetCurrentTime();
cFilename.Format("%s\\record\\%4d%02d%02d_%02d%02d%02d.h264",
"c:",
time.GetYear(),
time.GetMonth(),
time.GetDay(),
time.GetHour(),
time.GetMinute(),
time.GetSecond());
if (-1 == _access("c:\\record", 0))
{
CreateDirectory("c:\\record", NULL);
}
if (m_bRecord)
{
if (H264_PLAY_StopDataRecord(_PlaySDKHandle))
{
m_bRecord = FALSE;
AfxMessageBox("Desktop.StopRecordOk");
}
}
else
{
CString strFileType(".h264");
CString strFileName("");
if (strstr(cFilename, ".h264"))
{
strFileType = _T("h264");
}
else
{
MessageBox("Type Error!");
}
int iNum = cFilename.ReverseFind('\\');
if (-1 == iNum)
{
MessageBox("Type1 Error!");
}
else
{
strFileName = cFilename.Right(cFilename.GetLength() - (iNum + 1));
}
CFileDialog dlg(FALSE, strFileType, strFileName, OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY,
"All File(*.h264;)|*.*||", this);
dlg.m_ofn.lpstrInitialDir = cFilename.Left(iNum);
CString strSaveName;
if (dlg.DoModal() == IDOK)
{
cFilename = dlg.GetPathName();
int nTemp = 0;
for (;;)
{
int nIndex = cFilename.Find('\\', nTemp);
if (nIndex == -1)
{
break;
}
CString str = cFilename.Left(nIndex);
nTemp = nIndex + 1;
_mkdir(str);
}
//H264_PLAY_StartDataRecord(_PlaySDKHandle, cFilename.GetBuffer(0), MEDIA_FILE_NONE)
//H264_DVR_API bool CALL_METHOD H264_DVR_StartLocalRecord(long lRealHandle,char*szSaveFileName,long type=0);
//H264_PLAY_API BOOL CALLMETHOD H264_PLAY_StartDataRecord(LONG nPort, LPCSTR sFileName, LONG nType);
LPTSTR adress = (LPTSTR)(LPCTSTR)cFilename.GetBuffer(0);
//H264_DVR_StartLocalRecord(watchHandle, adress, long(0))
if (H264_PLAY_StartDataRecord(watchHandle, adress, long(0)))
{
m_bRecord = TRUE;
H264_PLAY_GetStreamOpenMode(_PlaySDKHandle);
AfxMessageBox("Desktop.StartRecordOk");
}
else
{
int a = H264_DVR_GetLastError();
AfxMessageBox("Desktop.StartRecordFail");
}
}
}
}
基本流程就是:登录设备->获得登录句柄
设置实时显示->获得实时显示句柄
设置回调函数->设置H264播放
H264按照给的句柄播放
下载的功能就在代码后面,不在多说了。
存在问题
朋友们,雄迈没有sdk技术支持,建议用别的
很多设置不能用
他这个是私有h264头,需要自己修改成标准264,不然保存的视频或者源264码流只能用雄迈的软件才能用。
调用二次开发sdk要把
NETSDK.DLL源程序代码文件夹下,就是该文件夹上级路径
还有streamread.dll
更不要忘了在需要调用sdk的地方在程序最前面#include "netsdk.h"
如果MFC读取ID号失败,一般是因为没有#include "Resource.h",且这个里面的id号有重复的冲突。随便改一个别的号就行了。
调用一个.dll文件需要.h文件和.lib文件。如果.h文件找不到函数,可以在项目-属性-连接-输入里面加入.lib文件的名字。多个用 ; 隔开。
综上所述,用大华的sdk了