目錄
效果
項目
使用
代碼
下載
效果
項目
使用
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類型的timepstd::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/HTTPstruPlayInfo.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;
}
下載
源碼下載