经过上两遍文章,对初步的界面已经是完成了,这一章将加入市场比较常用的大华和海康的SDK,并使用SDK播放这两款IPC的视频码流,实现多窗口播放。因SDK播放时只需要传入窗口句柄即可,所以无需顾虑太多的编解码和释放问题。毕竟SDK开始是入门级的
一、首先关于大华、海康的SDK都可以在其官网下载
大华SDK下载:https://www.dahuatech.com/service/downloadlists/836.html
海康SDK下载:https://www.hikvision.com/cn/download_61.html
二、界面新增配置
2.1、为了灵活配置每个窗口的IP信息,所以在窗口页面做一下改动如下:
2.1.1、IDD_DIALOG_REALPLA_WND(单个窗口布局),这是前一章创建的窗口只是进行了修改
控件说明:
1· 增加Static控件,用于背景图层 (IDC_STATIC_BACK)
2· 增加Button按钮控件,用于单个窗口信息配置弹窗 (IDC_BUTTON_CONFIG)
2.2、增加配置设备信息窗口,这里配置的信息使用ini文件存储的
2.2.1、IDD_DIALOG_DEVICE_CONFIG(配置窗口布局)
控件说明:
界面控件 | 控件ID | 名称/用途 | 映射变量 |
---|---|---|---|
Static Text | IDC_STATIC_DEV_TYPE | 设备类型 : | |
Combo Box | IDC_COMBO_DEV_TYPE | 设备类型下拉框 | m_comDevType |
Static Text | IDC_STATIC_IP | IP: | |
IP Address Control | IDC_IPADDRESS | IP编辑框 | m_ipAddress |
Static Text | IDC_STATIC_PORT | 端口: | |
Edit Control | IDC_EDIT_PORT | 端口编辑框 | m_editPort |
Check Box | IDC_CHECK_DEFAULT | 使用默认账号 | m_chkDefault |
Static Text | IDC_STATIC_USER | 设备账号: | |
Edit Control | IDC_EDIT_USER | 设备账号编辑框 | m_editUser |
Static Text | IDC_STATIC_PWD | 设备密码: | |
Edit Control | IDC_EDIT_PWD | 设备密码编辑框: | m_editPWD |
Button | IDOK | 确定 | |
Button | IDCANCEL | 取消 |
2.2.2、为添加的窗口新增一个类
2.2.3、增加一个窗口初始化函数 OnInitDialog()
如果使用VS2010 先打开DlgDeviceConfig.cpp 然后使用快捷键输入Ctrl+Shift+x 就能弹出类向导界面
或
菜单->项目->类向导
这样DlgDeviceConfig.cpp就多出了一个窗口初始化函数,这里是照顾一下像我之前不懂这些的新人
DlgDeviceConfig.h
#pragma once
// CDlgDeviceConfig 对话框
//增加了一个头文件,用于存储通用数据结构
#include "ProjectDef.h"
class CDlgDeviceConfig : public CDialogEx
{
DECLARE_DYNAMIC(CDlgDeviceConfig)
public:
//这里把构造函数给修改了
CDlgDeviceConfig(int nIndex, CWnd* pParent = NULL); // 标准构造函数
virtual ~CDlgDeviceConfig();
// 对话框数据
enum { IDD = IDD_DIALOG_DEVICE_CONFIG};
private:
RECT m_rect;
CWnd* m_pParent;
CString m_strPath;
int m_nIndex;
protected:
void InitData();
void InitView();
private:
void ShowView(CAMERA_TYPE_ENMU type);
public:
DB_DEVICE_INFO_STRU m_stDeviceInfo;
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnInitDialog();
afx_msg void OnBnClickedOk();
CComboBox m_comDevType;
CEdit m_editPort;
CIPAddressCtrl m_ipAddress;
CButton m_chkDefault;
CEdit m_editUser;
CEdit m_editPWD;
afx_msg void OnCbnSelendokComboDevType();
afx_msg void OnBnClickedCheckDefault();
};
DlgDeviceConfdig.cpp
// DlgDeviceConfig.cpp : 实现文件
//
#include "stdafx.h"
#include "VideoModuleProject.h"
#include "DlgDeviceConfig.h"
#include "afxdialogex.h"
// CDlgDeviceConfig 对话框
IMPLEMENT_DYNAMIC(CDlgDeviceConfig, CDialogEx)
CDlgDeviceConfig::CDlgDeviceConfig(int nIndex, CWnd* pParent /*=NULL*/)
: CDialogEx(CDlgDeviceConfig::IDD, pParent)
{
m_pParent = pParent;
m_nIndex = nIndex;
memset(&m_stDeviceInfo, 0, sizeof(DB_DEVICE_INFO_STRU));
m_strPath = "";
}
CDlgDeviceConfig::~CDlgDeviceConfig()
{
}
void CDlgDeviceConfig::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_COMBO_DEV_TYPE, m_comDevType);
DDX_Control(pDX, IDC_EDIT_PORT, m_editPort);
DDX_Control(pDX, IDC_IPADDRESS, m_ipAddress);
DDX_Control(pDX, IDC_CHECK_DEFAULT, m_chkDefault);
DDX_Control(pDX, IDC_EDIT_USER, m_editUser);
DDX_Control(pDX, IDC_EDIT_PWD, m_editPWD);
}
BEGIN_MESSAGE_MAP(CDlgDeviceConfig, CDialogEx)
ON_BN_CLICKED(IDOK, &CDlgDeviceConfig::OnBnClickedOk)
ON_CBN_SELENDOK(IDC_COMBO_DEV_TYPE, &CDlgDeviceConfig::OnCbnSelendokComboDevType)
ON_BN_CLICKED(IDC_CHECK_DEFAULT, &CDlgDeviceConfig::OnBnClickedCheckDefault)
END_MESSAGE_MAP()
// CDlgDeviceConfig 消息处理程序
BOOL CDlgDeviceConfig::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: 在此添加额外的初始化
MoveWindow(0, 0, 355, 205);
CRect tmpRect;
GetWindowRect(tmpRect);
CRect rc;
m_pParent->GetWindowRect(rc);
printf("rc.width = %d,rc.height=%d,rc.right=%d,rc.bottom=%d,\n", tmpRect.Width(), tmpRect.Height(), rc.left, rc.top);
int x = (rc.right - rc.Width() / 2) - tmpRect.Width() / 2;
int y = (rc.bottom - rc.Height() / 2) - tmpRect.Height() / 2;
SetWindowPos(&wndTopMost, x, y, rc.Width(), rc.Height(), SWP_NOSIZE);
printf("x=%d,y=%d\n", x, y);
char app[1024] = { 0 };
GetAppPath(app, sizeof(app));
char filepath[1024] = { 0 };
sprintf_s(filepath, "%s\\ConfigDevice.ini", app);
m_strPath = filepath;
InitView();
InitData();
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
void CDlgDeviceConfig::InitView()
{
CRect tmpRect;
GetWindowRect(tmpRect);
//设备类型
GetDlgItem(IDC_STATIC_DEV_TYPE)->MoveWindow(15, 10, 65, 20);
GetDlgItem(IDC_COMBO_DEV_TYPE)->MoveWindow(85, 10, 110, 20);
// GetDlgItem(IDC_STATIC_INFO)->MoveWindow(100, 90, 150, 20);
// GetDlgItem(IDC_STATIC_INFO)->ShowWindow(SW_HIDE);
//IP
GetDlgItem(IDC_STATIC_IP)->MoveWindow(15, 50, 40, 20);
GetDlgItem(IDC_IPADDRESS)->MoveWindow(60, 50, 150, 20);
//端口
GetDlgItem(IDC_STATIC_PORT)->MoveWindow(215, 50, 45, 20);
GetDlgItem(IDC_EDIT_PORT)->MoveWindow(275, 50, 60, 20);
//默认用户名(复选框)
GetDlgItem(IDC_CHECK_DEFAULT)->MoveWindow(15, 85, 90, 20);
//用户名
GetDlgItem(IDC_STATIC_USER)->MoveWindow(15, 115, 60, 20);
GetDlgItem(IDC_EDIT_USER)->MoveWindow(80, 115, 100, 20);
//密码
GetDlgItem(IDC_STATIC_PWD)->MoveWindow(190, 115, 60, 20);
GetDlgItem(IDC_EDIT_PWD)->MoveWindow(255, 115, 100, 20);
//按钮
GetDlgItem(IDOK)->MoveWindow(70, 150, 70, 20);
GetDlgItem(IDCANCEL)->MoveWindow(195, 150, 70, 20);
}
void CDlgDeviceConfig::InitData()
{
//初始化下拉框
m_comDevType.InsertString(0, "大华");
m_comDevType.InsertString(1, "海康");
m_comDevType.InsertString(2, "通用");
char buf[1024] = "";
CString szTmp = "";
szTmp.Format("Device%d", m_nIndex);
m_comDevType.SetCurSel(GetPrivateProfileInt(szTmp, DEVICE_TYPE, 0, m_strPath));
GetPrivateProfileString(szTmp, DEVICE_IP, _T("192.168.1.1"), buf, sizeof(buf), m_strPath);
int ip[5] = { 0 };
sscanf(buf, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]);
m_ipAddress.SetAddress(ip[0], ip[1], ip[2], ip[3]);
GetPrivateProfileString(szTmp, DEVICE_PORT, _T("37777"), buf, sizeof(buf), m_strPath);
m_editPort.SetWindowTextA(buf);
m_chkDefault.SetCheck(GetPrivateProfileInt(szTmp, DEFAULT_PWD, 1, m_strPath));
if (m_chkDefault.GetCheck())
{
GetDlgItem(IDC_EDIT_USER)->EnableWindow(FALSE);
GetDlgItem(IDC_EDIT_PWD)->EnableWindow(FALSE);
}
else
{
GetDlgItem(IDC_EDIT_USER)->EnableWindow(TRUE);
GetDlgItem(IDC_EDIT_PWD)->EnableWindow(TRUE);
}
GetPrivateProfileString(szTmp, DEVICE_USER, _T("admin"), buf, sizeof(buf), m_strPath);
m_editUser.SetWindowTextA(buf);
GetPrivateProfileString(szTmp, DEVICE_PWD, _T("admin123"), buf, sizeof(buf), m_strPath);
m_editPWD.SetWindowTextA(buf);
}
void CDlgDeviceConfig::OnBnClickedOk()
{
m_stDeviceInfo.uiType = m_comDevType.GetCurSel();
DWORD dwIP;
m_ipAddress.GetAddress(dwIP);
m_stDeviceInfo.uiIpAddess = (_UI)dwIP;
CString szTmp;
m_editPort.GetWindowText(szTmp);
m_stDeviceInfo.uiPort = _ttoi(szTmp.GetBuffer(0));
if (m_chkDefault.GetCheck())
{
strcpy(m_stDeviceInfo.scUser, "admin");
strcpy(m_stDeviceInfo.scPwd, "admin123");
}
else
{
m_editUser.GetWindowText(szTmp);
strcpy(m_stDeviceInfo.scUser, szTmp.GetBuffer(0));
m_editPWD.GetWindowText(szTmp);
strcpy(m_stDeviceInfo.scPwd, szTmp.GetBuffer(0));
}
//写入配置文件
CString szTitle = "";
szTitle.Format("Device%d", m_nIndex);
m_comDevType.SetCurSel(GetPrivateProfileInt(szTitle, DEVICE_TYPE, 1, m_strPath));
szTmp.Format("%d", m_stDeviceInfo.uiType);
WritePrivateProfileString(szTitle, DEVICE_TYPE, szTmp, m_strPath);
szTmp.Format("%d.%d.%d.%d", dwIP >> 24, (dwIP & 0xFF0000) >> 16, (dwIP & 0xFF00) >> 8, (dwIP & 0xFF));
WritePrivateProfileString(szTitle, DEVICE_IP, szTmp, m_strPath);
szTmp.Format("%d", m_stDeviceInfo.uiPort);
WritePrivateProfileString(szTitle, DEVICE_PORT, szTmp, m_strPath);
WritePrivateProfileString(szTitle, DEVICE_USER, m_stDeviceInfo.scUser, m_strPath);
WritePrivateProfileString(szTitle, DEVICE_PWD, m_stDeviceInfo.scPwd, m_strPath);
WritePrivateProfileString(szTitle, DEFAULT_PWD, m_chkDefault.GetCheck() ? "1" : "0", m_strPath);
CDialogEx::OnOK();
}
void CDlgDeviceConfig::OnCbnSelendokComboDevType()
{
switch (m_comDevType.GetCurSel())
{
case CAMERA_TYPE_DH: //大华
m_editPort.SetWindowTextA("37777");
ShowView(CAMERA_TYPE_DH);
break;
case CAMERA_TYPE_HK: //海康
m_editPort.SetWindowTextA("8000");
ShowView(CAMERA_TYPE_HK);
break;
case CAMERA_TYPE_GENERAL: //RTSP
m_editPort.SetWindowTextA("554");
ShowView(CAMERA_TYPE_GENERAL);
break;
default:
break;
}
}
void CDlgDeviceConfig::ShowView(CAMERA_TYPE_ENMU type)
{
if (type == CAMERA_TYPE_GENERAL)
{
//IP
GetDlgItem(IDC_STATIC_IP)->ShowWindow(SW_HIDE);
GetDlgItem(IDC_IPADDRESS)->ShowWindow(SW_HIDE);
//端口
GetDlgItem(IDC_STATIC_PORT)->ShowWindow(SW_HIDE);
GetDlgItem(IDC_EDIT_PORT)->ShowWindow(SW_HIDE);
//默认用户名(复选框)
GetDlgItem(IDC_CHECK_DEFAULT)->ShowWindow(SW_HIDE);
//用户名
GetDlgItem(IDC_STATIC_USER)->ShowWindow(SW_HIDE);
GetDlgItem(IDC_EDIT_USER)->ShowWindow(SW_HIDE);
//密码
GetDlgItem(IDC_STATIC_PWD)->ShowWindow(SW_HIDE);
GetDlgItem(IDC_EDIT_PWD)->ShowWindow(SW_HIDE);
}
else
{
//IP
GetDlgItem(IDC_STATIC_IP)->ShowWindow(SW_SHOW);
GetDlgItem(IDC_IPADDRESS)->ShowWindow(SW_SHOW);
//端口
GetDlgItem(IDC_STATIC_PORT)->ShowWindow(SW_SHOW);
GetDlgItem(IDC_EDIT_PORT)->ShowWindow(SW_SHOW);
//默认用户名(复选框)
GetDlgItem(IDC_CHECK_DEFAULT)->ShowWindow(SW_SHOW);
//用户名
GetDlgItem(IDC_STATIC_USER)->ShowWindow(SW_SHOW);
GetDlgItem(IDC_EDIT_USER)->ShowWindow(SW_SHOW);
//密码
GetDlgItem(IDC_STATIC_PWD)->ShowWindow(SW_SHOW);
GetDlgItem(IDC_EDIT_PWD)->ShowWindow(SW_SHOW);
}
}
//勾选默认用户名与密码复选框
void CDlgDeviceConfig::OnBnClickedCheckDefault()
{
if (m_chkDefault.GetCheck())
{
GetDlgItem(IDC_EDIT_USER)->EnableWindow(FALSE);
GetDlgItem(IDC_EDIT_PWD)->EnableWindow(FALSE);
}
else
{
GetDlgItem(IDC_EDIT_USER)->EnableWindow(TRUE);
GetDlgItem(IDC_EDIT_PWD)->EnableWindow(TRUE);
}
}
ProjectDef.h 公用的头文件
#pragma once
//类型声明
typedef char _SC;
typedef short _SS;
typedef signed int _SI;
typedef long _SL;
typedef unsigned char _UC;
typedef unsigned short _US;
typedef unsigned long _UL;
typedef unsigned long long _ULL;
#define _UI unsigned int
//配置参数宏
#define DEVICE_TYPE "DeviceType"
#define DEVICE_IP "DeviceIP"
#define DEVICE_PORT "DevicePort"
#define DEFAULT_PWD "DefaultPWD"
#define DEVICE_USER "DeviceUser"
#define DEVICE_PWD "DevicePwd"
//摄像机类型
typedef enum
{
CAMERA_TYPE_DH = 0, //大华
CAMERA_TYPE_HK, //海康
CAMERA_TYPE_GENERAL,//通用(其他)
}CAMERA_TYPE_ENMU;
typedef struct
{
_UI uiIpAddess; //设备IP
_UI uiPort; //设备端口
_SC scUser[32]; //用户名
_SC scPwd[32]; //密码
_UI uiType; //摄像机类型
_UI nChannelID; //视频通道号,从0开始递增的整数
_UI uiStreamType; //视频码流
}DB_DEVICE_INFO_STRU;
运行效果:
剩下的其他界面修改不一 一介绍了,直接进入重头戏,SDK
三、融入SDK到程序实现播放视频,这里也是只介绍两个SDK公用的接口类,详细的SDK使用可以查看官方文档,这里也没什么好介绍的。当然有疑问的在评论区留言
3.1 、增加兼容两家SDK的类模板,初步添加几个常用功能,登录、登出、播放、停止
CameraSDK.h
#ifndef __CAMERASDK_H__
#define __CAMERASDK_H__
#pragma once
class CCameraSDK
{
public:
CCameraSDK()
{
}
~CCameraSDK()
{
}
public:
//登录
virtual bool CameraLogin(_UI uIPAddress,_UI uPort,char* psUser,char* psPwd) = 0;
//登出
virtual void CameraLogout() = 0;
//开始播放
virtual bool SrartRealPlay(DB_DEVICE_INFO_STRU* pCameraInfo,REALPLAY_WND_INFO* pWndInfo) = 0;
//停止播放
virtual bool StopRealPlay() = 0;
};
#endif
3.2、SDK播放线程管控类,用于控制大华和海康SDK的登录、登出、播放、停止。后续的就加上大华和海康的SDK实现就可以了。
VideoPlayCtrl.h
#pragma once
#include "CameraSDK.h"
class CVideoPlayCtrl
{
public:
CVideoPlayCtrl(void);
~CVideoPlayCtrl(void);
public:
BOOL InitSDK();
void CleanupSDK();
void CreateSDK(_UI uiType,CCameraSDK** ppSDK);
void DeleteSDK(_UI uiType,CCameraSDK** ppSDK);
//登录
_UC CameraLogin(DB_DEVICE_INFO_STRU *pCameraInfo);
//登出
_UC CameraLogout();
//开始播放
int StartRealPlay(DB_DEVICE_INFO_STRU *pCameraInfo, REALPLAY_WND_INFO *pWndInfo,BOOL bPlayEx = FALSE);
//停止播放
int StopRealPlay(REALPLAY_WND_INFO *pWndInfo);
protected:
CCameraSDK *m_pCaneraManage;
vector<CCameraSDK *> m_vecpCameraRealPlay;
};
VideoPlayCtrl.cpp
#include "StdAfx.h"
#include "VideoPlayCtrl.h"
#include "DHSdk.h"
#include "HKSdk.h"
CVideoPlayCtrl::CVideoPlayCtrl(void)
{
m_pCaneraManage = NULL;
}
CVideoPlayCtrl::~CVideoPlayCtrl(void)
{
}
BOOL CVideoPlayCtrl::InitSDK()
{
BOOL bRet = FALSE;
bRet = CDHSdk::InitSDK();
bRet = CHKSdk::InitSDK();
return bRet;
}
void CVideoPlayCtrl::CleanupSDK()
{
CDHSdk::CleanupSDK();
CHKSdk::CleanupSDK();
}
void CVideoPlayCtrl::CreateSDK(_UI uiType,CCameraSDK** ppSDK)
{
if(uiType == CAMERA_TYPE_DH)
{
*ppSDK = new CDHSdk();
}
else if(uiType == CAMERA_TYPE_HK)
{
*ppSDK = new CHKSdk();
}
else
{
*ppSDK = NULL;
}
}
void CVideoPlayCtrl::DeleteSDK(_UI uiType,CCameraSDK** ppSDK)
{
if(uiType == CAMERA_TYPE_DH)
{
CDHSdk *pDHSdk = NULL;
pDHSdk = (CDHSdk *)*ppSDK;
delete pDHSdk;
*ppSDK = NULL;
}
else if(uiType == CAMERA_TYPE_HK)
{
CHKSdk *pHKSdk = NULL;
pHKSdk = (CHKSdk *)*ppSDK;
delete pHKSdk;
*ppSDK = NULL;
}
}
//登录
_UC CVideoPlayCtrl::CameraLogin(DB_DEVICE_INFO_STRU *pCameraInfo)
{
_UC ucResult = RESULT_FAIL;
DB_DEVICE_INFO_STRU OldCameraInfo;
REALPLAY_WND_INFO OldWndInfo;
//已有视频句柄
if(m_pCaneraManage != NULL)
{
m_pCaneraManage->GetCurRealPlayInfo(&OldCameraInfo,&OldWndInfo);
if(OldCameraInfo.uiType != pCameraInfo->uiType)
{
//删除对象
DeleteSDK(OldCameraInfo.uiType,&m_pCaneraManage);
//创建新对象
CreateSDK(pCameraInfo->uiType,&m_pCaneraManage);
}
}
else
{
CreateSDK(pCameraInfo->uiType,&m_pCaneraManage);
}
if(m_pCaneraManage == NULL)
{
return RESULT_FAIL;
}
if(m_pCaneraManage->CameraLogin(pCameraInfo->uiIpAddess,pCameraInfo->uiPort,pCameraInfo->scUser,pCameraInfo->scPwd))
{
ucResult = RESULT_SUCCESS;
}
else
{
ucResult = RESULT_FAIL;
DeleteSDK(pCameraInfo->uiType,&m_pCaneraManage);
}
return ucResult;
}
//登出
_UC CVideoPlayCtrl::CameraLogout()
{
DB_DEVICE_INFO_STRU OldCameraInfo;
REALPLAY_WND_INFO OldWndInfo;
if(m_pCaneraManage == NULL)
{
return RESULT_SUCCESS;
}
m_pCaneraManage->GetCurRealPlayInfo(&OldCameraInfo,&OldWndInfo);
DeleteSDK(OldCameraInfo.uiType,&m_pCaneraManage);
return RESULT_SUCCESS;
}
//开始播放
int CVideoPlayCtrl::StartRealPlay(DB_DEVICE_INFO_STRU *pCameraInfo, REALPLAY_WND_INFO *pWndInfo,BOOL bPlayEx)
{
CameraLogin(pCameraInfo);
if(m_pCaneraManage == NULL)
{
CreateSDK(pCameraInfo->uiType, &m_pCaneraManage);
if(m_pCaneraManage == NULL)
{
return RESULT_FAIL;
}
}
//开始播放
if(!bPlayEx)
{
if(!m_pCaneraManage->SrartRealPlay(pCameraInfo,pWndInfo))
{
DeleteSDK(pCameraInfo->uiType, &(m_pCaneraManage));
return RESULT_FAIL;
}
}
else //扩展功能播放
{
if(!m_pCaneraManage->SrartRealPlayEx(pCameraInfo,pWndInfo))
{
DeleteSDK(pCameraInfo->uiType, &(m_pCaneraManage));
return RESULT_FAIL;
}
}
return RESULT_SUCCESS;
}
//停止播放
int CVideoPlayCtrl::StopRealPlay(REALPLAY_WND_INFO *pWndInfo)
{
_UC ucResult = RESULT_SUCCESS;
DB_DEVICE_INFO_STRU OldCameraInfo;
REALPLAY_WND_INFO OldWndInfo;
if(m_pCaneraManage==NULL)
return RESULT_FAIL;
m_pCaneraManage->GetCurRealPlayInfo(&OldCameraInfo, &OldWndInfo);
DeleteSDK(OldCameraInfo.uiType,&m_pCaneraManage);
return ucResult;
}
写到这里就觉得代码量粘贴的有点多了,所以后续的SDK应用就不贴出来了,因为后续的有源码下载的,到时候可以自己阅读,因为代码都有注释说明,相对来说比较容易看明白。
这一章节的效果:
四、因为这个工程需要用到大华和海康的头文件和Lib所以功能目录需要自行添加路径,所以这次上传的代码加上sln,直接能编译。具体的就参考功能项目吧 0.0
程序只是做记录顺便分享的,所以有什么写的不好的。欢迎各位大佬指点!!!
目录篇章
- MFC 多视频界面切换以及使用大华、海康的SDK实时预览IPC画面(专栏篇)
- MFC 多视频播放界面模板–(一)静态文本绘制边框
- MFC多视频播放界面模板–(二)搭建视频播放界面空壳程序
- MFC多视频播放界面模板–(三)搭建播放大华、海康SDK视频播放平台
源代码Gitee:
https://gitee.com/hi_feng/video-module-project_3.git
有下载B的大佬可以走这个CSDN的下载通道:(代码一样的)
https://download.csdn.net/download/qq_36351159/14023229