基于海康SDK的C++实时视频流逐帧抓取存图小工具

目录

效果

项目

使用

代码

下载


效果

项目

使用

PlayDemo.exe <IP> <Port> <Username> <Password>

代码

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

#include <string>

#include <iostream>

#include <Windows.h>

#include <thread>

#include <time.h>

#include <conio.h>

#include <filesystem>

#include <iomanip>

#include <chrono>

#include <sstream>

#include <direct.h>

using namespace std;

#include "PlayM4.h"

#include "HCNetSDK.h"

int times = 0;

LONG lRealPlayHandle;

LONG m_lPort[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };//全局的播放库port号

std::string imgFolder = "img"; // 图片保存文件夹

//播放库硬解码回调

void CALLBACK DisplayCBFun(DISPLAY_INFO_YUV* pstDisplayInfo) {

//每100次保存一次yuv数据

if (times % 100 == 0) {

FILE* fp = NULL;

string ansiString = "example" + to_string(pstDisplayInfo->nPort) + "___" + to_string(times / 100) + ".yuv";

fp = fopen(ansiString.c_str(), "wb");

// 将字符数组写入文件

fwrite(pstDisplayInfo->pBuf, sizeof(char), pstDisplayInfo->nBufLen, fp); // 不包括末尾的空字符

// 关闭文件

fclose(fp);

}

times++;

printf("Buf长度:%d\n画面宽:%d\n画面高:%d\n数据类型:%d\nn播放库句柄:%d\n", pstDisplayInfo->nBufLen, pstDisplayInfo->nWidth, pstDisplayInfo->nHeight, pstDisplayInfo->nType, pstDisplayInfo->nPort);

}

//播放库解码回调

void CALLBACK DecCBFunIm(long nPort, char* pBuf, long nSize, FRAME_INFO* pFrameInfo, void* nUser, void* nReserved2) {

// 获取当前时间(精确到毫秒)

auto now = std::chrono::system_clock::now();

auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);

auto epoch = now_ms.time_since_epoch();

auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);

long long milliseconds = value.count();

// 转换为时间结构

std::time_t time = std::chrono::system_clock::to_time_t(now);

std::tm tm = *std::localtime(&time);

// 格式化时间字符串

std::ostringstream oss;

oss << std::put_time(&tm, "%Y%m%d_%H%M%S_") << std::setfill('0') << std::setw(3) << (milliseconds % 1000);

std::string baseName = imgFolder + "/" + oss.str();

// 保存JPEG图片并计算耗时

auto jpgStart = std::chrono::high_resolution_clock::now();

std::string jpgPath = baseName + ".jpg";

BOOL jpgResult = PlayM4_ConvertToJpegFile(pBuf, nSize, pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nType, const_cast<char*>(jpgPath.c_str()));

auto jpgEnd = std::chrono::high_resolution_clock::now();

auto jpgDuration = std::chrono::duration_cast<std::chrono::milliseconds>(jpgEnd - jpgStart);

// 保存BMP图片并计算耗时

//auto bmpStart = std::chrono::high_resolution_clock::now();

//std::string bmpPath = baseName + ".bmp";

//BOOL bmpResult = PlayM4_ConvertToBmpFile(pBuf, nSize, pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nType, const_cast<char*>(bmpPath.c_str()));

//auto bmpEnd = std::chrono::high_resolution_clock::now();

//auto bmpDuration = std::chrono::duration_cast<std::chrono::milliseconds>(bmpEnd - bmpStart);

// 输出保存结果和时间

if (jpgResult) {

std::cout << "JPEG saved: " << jpgPath << " (Size: " << nSize << " bytes, " << pFrameInfo->nWidth << "x" << pFrameInfo->nHeight << ")" << " 耗时: " << jpgDuration.count() << " ms" << std::endl;

}

else {

std::cout << "Failed to save JPEG. Error code: " << GetLastError() << std::endl;

}

//if (bmpResult) {

// std::cout << "BMP saved: " << bmpPath << " in " << bmpDuration.count() << " ms" << std::endl;

//}

//else {

// std::cout << "Failed to save BMP. Error code: " << GetLastError() << std::endl;

//}

//// 输出总耗时

//auto totalDuration = std::chrono::duration_cast<std::chrono::milliseconds>(bmpEnd - jpgStart);

//std::cout << "Total time for both images: " << totalDuration.count() << " ms" << std::endl;

}

//sdk码流回调

void CALLBACK g_RealDataCallBack_V30(LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, void* dwUser)

{

DWORD dRet = 0;

BOOL inData = FALSE;

LONG lPort = -1;

switch (dwDataType)

{

case NET_DVR_SYSHEAD: //系统头

if (!PlayM4_GetPort(&lPort)) //获取播放库未使用的通道号

{

printf("申请播放库资源失败");

break;

}

printf("播放库句柄:%d\n", lPort);

m_lPort[lRealPlayHandle] = lPort; //第一次回调的是系统头,将获取的播放库port号赋值给全局port,下次回调数据时即使用此port号播放

if (dwBufSize > 0) {

//设置实时流播放模式

if (!PlayM4_SetStreamOpenMode(m_lPort[lRealPlayHandle], STREAME_REALTIME))

{

printf("PlayM4_SetStreamOpenMode Error\n");

printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));

break;

}

else

{

printf("PlayM4_SetStreamOpenMode Sus!\n");

}

//打开流接口

if (!PlayM4_OpenStream(m_lPort[lRealPlayHandle], pBuffer, dwBufSize, 5 * 1024 * 1024))

{

printf("PlayM4_OpenStream Error\n");

printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));

break;

}

else

{

printf("PlayM4_OpenStream Sus!\n");

}

//设置解码模式,第二个参数0为软解码,1为硬解码(硬解码需要硬件支持)

//if (!PlayM4_SetDecodeEngine(m_lPort[lRealPlayHandle], 1))

//{

// printf("PlayM4_SetDecodeEngine Error\n");

// printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));

// break;

//}

//else

//{

// printf("PlayM4_SetDecodeEngine Sus!\n");

//}

////设置硬解码回调,若设置为硬解码模式,需要使用该接口设置硬解码回调

//if (!PlayM4_SetDisplayCallBackYUV(m_lPort[lRealPlayHandle], DisplayCBFun, FALSE, NULL))

//{

// printf("PlayM4_SetDisplayCallBackYUV Error\n");

// printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));

// break;

//}

//else

//{

// printf("PlayM4_SetDecodeEngine Sus!\n");

//}

//设置解码回调函数 解码显示 回调yuv数据,软解模式下,使用该回调

if (!PlayM4_SetDecCallBackExMend(m_lPort[lRealPlayHandle], DecCBFunIm, NULL, 0, NULL))

{

printf("PlayM4_SetDecCallBackExMend Error\n");

printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));

break;

}

else

{

printf("PlayM4_SetDecodeEngine Sus!\n");

}

if (!PlayM4_Play(m_lPort[lRealPlayHandle], NULL)) //播放开始hWnd[lRealHandle]

{

printf("PlayM4_Play Error\n");

printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));

break;

}

else

{

printf("PlayM4_SetDecodeEngine Sus!\n");

}

}

break;

case NET_DVR_STREAMDATA: //码流数据

if (dwBufSize > 0 && m_lPort[lRealPlayHandle] != -1)

{

//送数据入播放库

while (!PlayM4_InputData(m_lPort[lRealPlayHandle], pBuffer, dwBufSize))

{

int dwError = PlayM4_GetLastError(m_lPort[lRealPlayHandle]);

printf("播放库句柄ID:%d,错误码:%d\n", m_lPort[lRealPlayHandle], dwError);

if (dwError == 11) //缓冲区满,需要重复送入数据

{

continue;

}

}

}

break;

default: //其他数据

if (dwBufSize > 0 && m_lPort[lRealPlayHandle] != -1)

{

if (!PlayM4_InputData(m_lPort[lRealPlayHandle], pBuffer, dwBufSize))

{

break;

}

}

break;

}

}

//播放库抓图

void getPic() {

int i = 0;

BOOL bFlag = FALSE;

DWORD dwErr = 0;

LONG dwWidth = 0;

LONG dwHeight = 0;

DWORD dwSize = 0;

DWORD dwCapSize = 0;

//抓10张图

while (i++ < 10) {

//获取当前视频文件的分辨率

int bFlag = PlayM4_GetPictureSize(m_lPort[lRealPlayHandle], &dwWidth, &dwHeight);

if (bFlag == FALSE)

{

dwErr = PlayM4_GetLastError(m_lPort[lRealPlayHandle]);

printf("PlayM4_GetPictureSize, error code: %d\n", dwErr);

break;

}

dwSize = dwWidth * dwHeight * 5;

//申请抓图内存

BYTE* m_pCapBuf = NULL;

if (m_pCapBuf == NULL)

{

m_pCapBuf = new BYTE[dwSize];

if (m_pCapBuf == NULL)

{

return;

}

}

//抓图BMP图片

bFlag = PlayM4_GetJPEG(m_lPort[lRealPlayHandle], m_pCapBuf, dwSize, &dwCapSize);

if (bFlag == FALSE)

{

dwErr = PlayM4_GetLastError(m_lPort[lRealPlayHandle]);

printf("PlayM4_GetLastError, error code: %d\n", dwErr);

break;

}

if (bFlag) {

FILE* fp = NULL;

time_t timep;

time(&timep); //获取从1970至今过了多少秒,存入time_t类型的timep

std::string temp_str = std::to_string(timep) + ".jpg";

fp = fopen(temp_str.c_str(), "wb");

// 将字符数组写入文件,文件即为图片文件

fwrite(m_pCapBuf, sizeof(char), dwCapSize, fp); // 不包括末尾的空字符

// 关闭文件

fclose(fp);

}

if (m_pCapBuf != NULL)

{

delete[] m_pCapBuf;

m_pCapBuf = NULL;

}

printf("完成第%d张抓图\n", i);

//等待1秒后进下下一次抓图

Sleep(1000);

}

}

// 检查文件夹是否存在 (C++14兼容方法)

bool folderExists(const std::string& folderPath) {

struct stat info;

return stat(folderPath.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);

}

// 创建文件夹 (C++14兼容方法)

bool createFolder(const std::string& folderPath) {

return _mkdir(folderPath.c_str()) == 0;

}

// 检查并创建图片保存文件夹

bool ensureImageFolderExists() {

// 检查文件夹是否存在

if (folderExists(imgFolder)) {

std::cout << "使用图片保存文件夹: " << imgFolder << std::endl;

return true;

}

// 如果不存在,尝试创建文件夹

if (createFolder(imgFolder)) {

std::cout << "创建图片保存文件夹: " << imgFolder << std::endl;

return true;

}

std::cerr << "错误: 无法创建图片保存文件夹" << std::endl;

return false;

}

//*********************************

// 函数入口

//*********************************

int main(int argc, char* argv[])

{

std::string folderPath = "Capture";

if (!CreateDirectoryA(folderPath.c_str(), NULL)) {

if (GetLastError() != ERROR_ALREADY_EXISTS) {

std::cout << "Failed to create directory: " << folderPath << std::endl;

return 1;

}

}

// 检查参数数量

if (argc != 5) {

printf("Usage: %s <IP> <Port> <Username> <Password>\n", argv[0]);

return 1;

}

// 从参数获取连接信息

const char* deviceAddress = argv[1];

WORD wPort = static_cast<WORD>(atoi(argv[2]));

const char* userName = argv[3];

const char* password = argv[4];

// 检查并创建图片保存文件夹

if (!ensureImageFolderExists()) {

std::cerr << "错误: 无法创建或访问图片保存文件夹,程序将退出" << std::endl;

return -1;

}

//---------------------------------------

// 初始化

NET_DVR_Init();

char ansiStringss[] = "./SdkLog";

NET_DVR_SetLogToFile(3, ansiStringss, TRUE);

//设置连接时间与重连时间

NET_DVR_SetConnectTime(2000, 1);

NET_DVR_SetReconnect(10000, true);

// 注册设备

LONG lUserID;

//登录参数,包括设备地址、登录用户、密码等

NET_DVR_USER_LOGIN_INFO struLoginInfo = { 0 };

struLoginInfo.bUseAsynLogin = 0; //同步登录方式

strncpy(struLoginInfo.sDeviceAddress, deviceAddress, NET_DVR_DEV_ADDRESS_MAX_LEN - 1);//设备IP地址

struLoginInfo.wPort = wPort;//设备服务端口

strncpy(struLoginInfo.sUserName, userName, NAME_LEN - 1); //设备登录用户名

strncpy(struLoginInfo.sPassword, password, NAME_LEN - 1);//设备登录密码

//设备信息, 输出参数

NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = { 0 };

//登录

lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);

if (lUserID < 0)

{

printf("Login failed, error code: %d\n", NET_DVR_GetLastError());

NET_DVR_Cleanup();

return 1;

}

//预览相关参数设置

NET_DVR_PREVIEWINFO struPlayInfo = { 0 };

struPlayInfo.hPlayWnd = NULL; //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空

struPlayInfo.lChannel = 1; //预览通道号

struPlayInfo.dwStreamType = 0; //0-主码流,1-子码流,2-码流3,3-码流4,以此类推

struPlayInfo.dwLinkMode = 0; //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP

struPlayInfo.bBlocked = 1; //0- 非阻塞取流,1- 阻塞取流

//启动预览并设置回调数据流

lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &struPlayInfo, g_RealDataCallBack_V30, NULL);//

if (lRealPlayHandle < 0)

{

printf("NET_DVR_RealPlay_V40 error %d\n", NET_DVR_GetLastError());

NET_DVR_Logout(lUserID);

NET_DVR_Cleanup();

return 1;

}

//等待播放库有数据,否则后面无法使用播放库抓图

//Sleep(3000);

// 创建并启动抓图线程

//std::thread t1(getPic);

// 播放库抓图分离线程

//t1.detach();

// 等待按键退出

while (true) {

if (_kbhit()) { // 检测键盘输入

int key = _getch(); // 获取按键

if (key == 27) { // ESC键

printf("ESC pressed, exiting...\n");

break;

}

}

Sleep(100); // 减少CPU占用

}

//关闭预览

NET_DVR_StopRealPlay(lRealPlayHandle);

//释放播放库资源

PlayM4_Stop(m_lPort[lRealPlayHandle]);

//关闭流

PlayM4_CloseStream(m_lPort[lRealPlayHandle]);

//释放播放端口

PlayM4_FreePort(m_lPort[lRealPlayHandle]);

//退出登录

NET_DVR_Logout(lUserID);

//释放sdk资源

NET_DVR_Cleanup();

return 1;

}

复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string>
#include <iostream>
#include <Windows.h>
#include <thread>
#include <time.h>
#include <conio.h>
#include <filesystem>
#include <iomanip>
#include <chrono>
#include <sstream>
#include <direct.h>

using namespace std;
#include "PlayM4.h"
#include "HCNetSDK.h"

int times = 0;
LONG lRealPlayHandle;
LONG m_lPort[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };//全局的播放库port号

std::string imgFolder = "img"; // 图片保存文件夹

//播放库硬解码回调
void CALLBACK DisplayCBFun(DISPLAY_INFO_YUV* pstDisplayInfo) {

	//每100次保存一次yuv数据
	if (times % 100 == 0) {
		FILE* fp = NULL;
		string ansiString = "example" + to_string(pstDisplayInfo->nPort) + "___" + to_string(times / 100) + ".yuv";
		fp = fopen(ansiString.c_str(), "wb");
		// 将字符数组写入文件
		fwrite(pstDisplayInfo->pBuf, sizeof(char), pstDisplayInfo->nBufLen, fp); // 不包括末尾的空字符
		// 关闭文件
		fclose(fp);
	}
	times++;
	printf("Buf长度:%d\n画面宽:%d\n画面高:%d\n数据类型:%d\nn播放库句柄:%d\n", pstDisplayInfo->nBufLen, pstDisplayInfo->nWidth, pstDisplayInfo->nHeight, pstDisplayInfo->nType, pstDisplayInfo->nPort);
}

//播放库解码回调
void CALLBACK DecCBFunIm(long nPort, char* pBuf, long nSize, FRAME_INFO* pFrameInfo, void* nUser, void* nReserved2) {

	// 获取当前时间(精确到毫秒)
	auto now = std::chrono::system_clock::now();
	auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
	auto epoch = now_ms.time_since_epoch();
	auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
	long long milliseconds = value.count();

	// 转换为时间结构
	std::time_t time = std::chrono::system_clock::to_time_t(now);
	std::tm tm = *std::localtime(&time);

	// 格式化时间字符串
	std::ostringstream oss;
	oss << std::put_time(&tm, "%Y%m%d_%H%M%S_") << std::setfill('0') << std::setw(3) << (milliseconds % 1000);
	std::string baseName = imgFolder + "/" + oss.str();

	// 保存JPEG图片并计算耗时
	auto jpgStart = std::chrono::high_resolution_clock::now();
	std::string jpgPath = baseName + ".jpg";
	BOOL jpgResult = PlayM4_ConvertToJpegFile(pBuf, nSize, pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nType, const_cast<char*>(jpgPath.c_str()));
	auto jpgEnd = std::chrono::high_resolution_clock::now();
	auto jpgDuration = std::chrono::duration_cast<std::chrono::milliseconds>(jpgEnd - jpgStart);

	// 保存BMP图片并计算耗时
	//auto bmpStart = std::chrono::high_resolution_clock::now();
	//std::string bmpPath = baseName + ".bmp";
	//BOOL bmpResult = PlayM4_ConvertToBmpFile(pBuf, nSize, pFrameInfo->nWidth, pFrameInfo->nHeight, pFrameInfo->nType, const_cast<char*>(bmpPath.c_str()));
	//auto bmpEnd = std::chrono::high_resolution_clock::now();
	//auto bmpDuration = std::chrono::duration_cast<std::chrono::milliseconds>(bmpEnd - bmpStart);

	// 输出保存结果和时间
	if (jpgResult) {
		std::cout << "JPEG saved: " << jpgPath << " (Size: " << nSize << " bytes, " << pFrameInfo->nWidth << "x" << pFrameInfo->nHeight << ")" << " 耗时: " << jpgDuration.count() << " ms" << std::endl;
	}
	else {
		std::cout << "Failed to save JPEG. Error code: " << GetLastError() << std::endl;
	}

	//if (bmpResult) {
	//	std::cout << "BMP saved: " << bmpPath << " in " << bmpDuration.count() << " ms" << std::endl;
	//}
	//else {
	//	std::cout << "Failed to save BMP. Error code: " << GetLastError() << std::endl;
	//}

	//// 输出总耗时
	//auto totalDuration = std::chrono::duration_cast<std::chrono::milliseconds>(bmpEnd - jpgStart);
	//std::cout << "Total time for both images: " << totalDuration.count() << " ms" << std::endl;

}

//sdk码流回调
void CALLBACK g_RealDataCallBack_V30(LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, void* dwUser)
{
	DWORD dRet = 0;
	BOOL inData = FALSE;
	LONG lPort = -1;
	switch (dwDataType)
	{
	case NET_DVR_SYSHEAD: //系统头

		if (!PlayM4_GetPort(&lPort))  //获取播放库未使用的通道号
		{
			printf("申请播放库资源失败");
			break;
		}
		printf("播放库句柄:%d\n", lPort);
		m_lPort[lRealPlayHandle] = lPort; //第一次回调的是系统头,将获取的播放库port号赋值给全局port,下次回调数据时即使用此port号播放

		if (dwBufSize > 0) {

			//设置实时流播放模式
			if (!PlayM4_SetStreamOpenMode(m_lPort[lRealPlayHandle], STREAME_REALTIME))
			{
				printf("PlayM4_SetStreamOpenMode Error\n");
				printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));
				break;
			}
			else
			{
				printf("PlayM4_SetStreamOpenMode Sus!\n");
			}
			//打开流接口
			if (!PlayM4_OpenStream(m_lPort[lRealPlayHandle], pBuffer, dwBufSize, 5 * 1024 * 1024))
			{
				printf("PlayM4_OpenStream Error\n");
				printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));
				break;
			}
			else
			{
				printf("PlayM4_OpenStream Sus!\n");
			}


			//设置解码模式,第二个参数0为软解码,1为硬解码(硬解码需要硬件支持)
			//if (!PlayM4_SetDecodeEngine(m_lPort[lRealPlayHandle], 1))
			//{
			//	printf("PlayM4_SetDecodeEngine Error\n");
			//	printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));
			//	break;
			//}
			//else
			//{
			//	printf("PlayM4_SetDecodeEngine Sus!\n");
			//}
			////设置硬解码回调,若设置为硬解码模式,需要使用该接口设置硬解码回调
			//if (!PlayM4_SetDisplayCallBackYUV(m_lPort[lRealPlayHandle], DisplayCBFun, FALSE, NULL))
			//{
			//	printf("PlayM4_SetDisplayCallBackYUV Error\n");
			//	printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));
			//	break;
			//}
			//else
			//{
			//	printf("PlayM4_SetDecodeEngine Sus!\n");
			//}

			//设置解码回调函数 解码显示	回调yuv数据,软解模式下,使用该回调			
			if (!PlayM4_SetDecCallBackExMend(m_lPort[lRealPlayHandle], DecCBFunIm, NULL, 0, NULL))
			{
				printf("PlayM4_SetDecCallBackExMend Error\n");
				printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));
				break;
			}
			else
			{
				printf("PlayM4_SetDecodeEngine Sus!\n");
			}

			if (!PlayM4_Play(m_lPort[lRealPlayHandle], NULL)) //播放开始hWnd[lRealHandle]
			{
				printf("PlayM4_Play Error\n");
				printf("GetLastError错误码 :%d\n", PlayM4_GetLastError(m_lPort[lRealPlayHandle]));
				break;
			}
			else
			{
				printf("PlayM4_SetDecodeEngine Sus!\n");
			}
		}
		break;
	case NET_DVR_STREAMDATA:   //码流数据
		if (dwBufSize > 0 && m_lPort[lRealPlayHandle] != -1)
		{
			//送数据入播放库
			while (!PlayM4_InputData(m_lPort[lRealPlayHandle], pBuffer, dwBufSize))
			{
				int dwError = PlayM4_GetLastError(m_lPort[lRealPlayHandle]);
				printf("播放库句柄ID:%d,错误码:%d\n", m_lPort[lRealPlayHandle], dwError);
				if (dwError == 11)  //缓冲区满,需要重复送入数据
				{
					continue;
				}
			}
		}
		break;
	default: //其他数据
		if (dwBufSize > 0 && m_lPort[lRealPlayHandle] != -1)
		{
			if (!PlayM4_InputData(m_lPort[lRealPlayHandle], pBuffer, dwBufSize))
			{
				break;
			}
		}
		break;
	}

}
//播放库抓图
void getPic() {
	int i = 0;
	BOOL   bFlag = FALSE;
	DWORD  dwErr = 0;
	LONG dwWidth = 0;
	LONG dwHeight = 0;
	DWORD dwSize = 0;
	DWORD dwCapSize = 0;

	//抓10张图
	while (i++ < 10) {

		//获取当前视频文件的分辨率
		int bFlag = PlayM4_GetPictureSize(m_lPort[lRealPlayHandle], &dwWidth, &dwHeight);
		if (bFlag == FALSE)
		{
			dwErr = PlayM4_GetLastError(m_lPort[lRealPlayHandle]);
			printf("PlayM4_GetPictureSize, error code: %d\n", dwErr);
			break;
		}
		dwSize = dwWidth * dwHeight * 5;
		//申请抓图内存
		BYTE* m_pCapBuf = NULL;
		if (m_pCapBuf == NULL)
		{
			m_pCapBuf = new BYTE[dwSize];
			if (m_pCapBuf == NULL)
			{
				return;
			}
		}

		//抓图BMP图片
		bFlag = PlayM4_GetJPEG(m_lPort[lRealPlayHandle], m_pCapBuf, dwSize, &dwCapSize);
		if (bFlag == FALSE)
		{
			dwErr = PlayM4_GetLastError(m_lPort[lRealPlayHandle]);
			printf("PlayM4_GetLastError, error code: %d\n", dwErr);
			break;
		}
		if (bFlag) {
			FILE* fp = NULL;
			time_t timep;
			time(&timep); //获取从1970至今过了多少秒,存入time_t类型的timep
			std::string temp_str = std::to_string(timep) + ".jpg";
			fp = fopen(temp_str.c_str(), "wb");
			// 将字符数组写入文件,文件即为图片文件
			fwrite(m_pCapBuf, sizeof(char), dwCapSize, fp); // 不包括末尾的空字符
			// 关闭文件
			fclose(fp);
		}

		if (m_pCapBuf != NULL)
		{
			delete[] m_pCapBuf;
			m_pCapBuf = NULL;
		}
		printf("完成第%d张抓图\n", i);
		//等待1秒后进下下一次抓图
		Sleep(1000);
	}
}

// 检查文件夹是否存在 (C++14兼容方法)
bool folderExists(const std::string& folderPath) {
	struct stat info;
	return stat(folderPath.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
}

// 创建文件夹 (C++14兼容方法)
bool createFolder(const std::string& folderPath) {
	return _mkdir(folderPath.c_str()) == 0;
}

// 检查并创建图片保存文件夹
bool ensureImageFolderExists() {
	// 检查文件夹是否存在
	if (folderExists(imgFolder)) {
		std::cout << "使用图片保存文件夹: " << imgFolder << std::endl;
		return true;
	}

	// 如果不存在,尝试创建文件夹
	if (createFolder(imgFolder)) {
		std::cout << "创建图片保存文件夹: " << imgFolder << std::endl;
		return true;
	}

	std::cerr << "错误: 无法创建图片保存文件夹" << std::endl;
	return false;
}

//*********************************
// 函数入口
//*********************************
int main(int argc, char* argv[])
{
	std::string folderPath = "Capture";
	if (!CreateDirectoryA(folderPath.c_str(), NULL)) {
		if (GetLastError() != ERROR_ALREADY_EXISTS) {
			std::cout << "Failed to create directory: " << folderPath << std::endl;
			return 1;
		}
	}

	// 检查参数数量
	if (argc != 5) {
		printf("Usage: %s <IP> <Port> <Username> <Password>\n", argv[0]);
		return 1;
	}

	// 从参数获取连接信息
	const char* deviceAddress = argv[1];
	WORD wPort = static_cast<WORD>(atoi(argv[2]));
	const char* userName = argv[3];
	const char* password = argv[4];


	// 检查并创建图片保存文件夹
	if (!ensureImageFolderExists()) {
		std::cerr << "错误: 无法创建或访问图片保存文件夹,程序将退出" << std::endl;
		return -1;
	}

	//---------------------------------------
	// 初始化
	NET_DVR_Init();
	char ansiStringss[] = "./SdkLog";
	NET_DVR_SetLogToFile(3, ansiStringss, TRUE);
	//设置连接时间与重连时间
	NET_DVR_SetConnectTime(2000, 1);
	NET_DVR_SetReconnect(10000, true);

	// 注册设备
	LONG lUserID;

	//登录参数,包括设备地址、登录用户、密码等
	NET_DVR_USER_LOGIN_INFO struLoginInfo = { 0 };
	struLoginInfo.bUseAsynLogin = 0; //同步登录方式
	strncpy(struLoginInfo.sDeviceAddress, deviceAddress, NET_DVR_DEV_ADDRESS_MAX_LEN - 1);//设备IP地址
	struLoginInfo.wPort = wPort;//设备服务端口
	strncpy(struLoginInfo.sUserName, userName, NAME_LEN - 1); //设备登录用户名
	strncpy(struLoginInfo.sPassword, password, NAME_LEN - 1);//设备登录密码

	//设备信息, 输出参数
	NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = { 0 };
	//登录
	lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);
	if (lUserID < 0)
	{
		printf("Login failed, error code: %d\n", NET_DVR_GetLastError());
		NET_DVR_Cleanup();
		return 1;
	}
	//预览相关参数设置
	NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
	struPlayInfo.hPlayWnd = NULL;         //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空
	struPlayInfo.lChannel = 1;       //预览通道号
	struPlayInfo.dwStreamType = 0;       //0-主码流,1-子码流,2-码流3,3-码流4,以此类推
	struPlayInfo.dwLinkMode = 0;       //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
	struPlayInfo.bBlocked = 1;       //0- 非阻塞取流,1- 阻塞取流

	//启动预览并设置回调数据流
	lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &struPlayInfo, g_RealDataCallBack_V30, NULL);//

	if (lRealPlayHandle < 0)
	{
		printf("NET_DVR_RealPlay_V40 error %d\n", NET_DVR_GetLastError());
		NET_DVR_Logout(lUserID);
		NET_DVR_Cleanup();
		return 1;
	}

	//等待播放库有数据,否则后面无法使用播放库抓图
	//Sleep(3000);
	// 创建并启动抓图线程
	//std::thread t1(getPic);
	// 播放库抓图分离线程
	//t1.detach();

	// 等待按键退出
	while (true) {
		if (_kbhit()) {  // 检测键盘输入
			int key = _getch();  // 获取按键
			if (key == 27) {  // ESC键
				printf("ESC pressed, exiting...\n");
				break;
			}
		}
		Sleep(100);  // 减少CPU占用
	}

	//关闭预览
	NET_DVR_StopRealPlay(lRealPlayHandle);
	//释放播放库资源
	PlayM4_Stop(m_lPort[lRealPlayHandle]);
	//关闭流
	PlayM4_CloseStream(m_lPort[lRealPlayHandle]);
	//释放播放端口
	PlayM4_FreePort(m_lPort[lRealPlayHandle]);
	//退出登录
	NET_DVR_Logout(lUserID);
	//释放sdk资源
	NET_DVR_Cleanup();
	return 1;
}

下载

源码下载

相关推荐
沐怡旸3 小时前
【底层机制】稀疏文件--是什么、为什么、好在哪、实现机制
c++·面试
向依阳3 小时前
C++:类和对象
c++·类和对象
oioihoii4 小时前
构造函数和析构函数中的多态陷阱:C++的隐秘角落
java·开发语言·c++
小欣加油5 小时前
leetcode LCR 170.交易逆序对的总数
数据结构·c++·算法·leetcode·职场和发展·排序算法
focksorCr5 小时前
编译缓存工具 sccache 效果对比
c++·缓存·rust
长安——归故李6 小时前
【modbus学习】
java·c语言·c++·学习·算法·c#
索迪迈科技6 小时前
STL库——map/set(类函数学习)
开发语言·c++·学习
Dfreedom.6 小时前
在Windows上搭建GPU版本PyTorch运行环境的详细步骤
c++·人工智能·pytorch·python·深度学习
一拳一个呆瓜7 小时前
【MFC】对话框:位置属性(居中、绝对对齐、X位置Y位置)应用示例
c++·mfc