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

发布于:2025-09-12 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

效果

项目

使用

代码

下载


效果

项目

使用

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;
}

下载

源码下载


网站公告

今日签到

点亮在社区的每一天
去签到