基于海康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類型的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;
}

下載

源碼下載

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/99034.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/99034.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/99034.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

windows|引用賬戶被鎖定 且暫時無法登錄

問題描述尷了個尬&#xff0c;一直認為筆記本鎖屏密碼記得很牢靠&#xff0c;沒想到因為少敲了一個點&#xff08;.&#xff09;&#xff0c;多次輸入登陸失敗&#xff0c;導致賬戶被鎖定了&#xff0c;提示&#xff1a;引用賬戶被鎖定 且暫時無法登錄。然后用手機搜索了一下&a…

系統核心解析:深入操作系統內部機制——進程管理與控制指南(三)【進程優先級/切換/調度】

???~~~~~~歡迎光臨知星小度博客空間~~~~~~??? ???零星地變得優秀~也能拼湊出星河~??? ???我們一起努力成為更好的自己~??? ???如果這一篇博客對你有幫助~別忘了點贊分享哦~??? ???如果有什么問題可以評論區留言或者私信我哦~??? ??????個人…

量子-resistant密碼學研究

當亞馬遜CloudFront在2025年9月宣布為所有TLS連接默認啟用后量子加密支持時&#xff0c;這一舉措標志著抗量子密碼學從學術研究正式邁入大規模實用部署階段。與此同時&#xff0c;密碼學家們發出警告&#xff1a;一臺擁有不到一百萬噪聲量子比特的計算機&#xff0c;可能在一周…

ARM 架構的存儲器模型

ARM 架構的存儲器模型 ARM 的存儲器模型是一個相對復雜但設計精密的體系&#xff0c;它定義了處理器如何與內存進行交互&#xff0c;包括內存訪問的順序、可見性以及緩存行為等。這對于理解多核編程、并發控制和底層系統性能至關重要。 ARM 架構&#xff0c;特別是 ARMv8 及以后…

機器學習-多層感知機MLP

線性方法->多層感知機&#xff08;MLP&#xff09; 一個全連接&#xff08;線性、dense&#xff09;層有參數W∈Rm?nW\in\R^{m*n}W∈Rm?n,b∈Rmb\in\R^mb∈Rm&#xff0c;其用于計算輸出yWxb∈RmyWxb\in\R^myWxb∈Rm 線性回歸&#xff1a;全連接層有1個輸出softmax 回歸&a…

PostgreSQL——并行查詢

這里寫目錄標題一、并行查詢相關自己置參數二、并行掃描2.1、并行順序掃描2.2、并行索引掃描2.3、并行index-only掃描2.4、并行bitmap heap掃描三、并行聚合四、多表關聯4.1、Nested loop多表關聯4.2、Merge join多表關聯4.3、Hash join多表關聯了解 Oracle 的朋友應該知道 Ora…

智能體賦能金融多模態報告自動化生成:技術原理與實現流程全解析

在金融領域&#xff0c;研報作為決策參考的核心載體&#xff0c;其生成過程往往涉及海量數據采集、多維度分析及專業內容整合&#xff0c;傳統人工制作模式不僅耗時耗力&#xff0c;還難以滿足實時性與標準化需求。隨著人工智能技術的發展&#xff0c;“智能體賦能的金融多模態…

uniapp和vue3項目中引入echarts 、lime-echart(微信小程序、H5等)

目錄標題1、獲取 lime-echart插件2、安裝 echarts3、相關代碼4、在線定制5、效果截圖1、獲取 lime-echart插件 https://gitee.com/liangei/lime-echart 將其中組件和靜態資源分別放入當前項目對應的文件夾中&#xff1a; 2、安裝 echarts npm install echarts --save具體查…

ZYNQ7020+AD9361裸機驅動驗證

1. 程序編譯驗證 a. 下載源代碼 首先需要從GitHub下載相應的源碼&#xff0c;打開git bash&#xff0c;然后在mingwin中使用以下命令下載源碼。 git clone --recursive https://github.com/MicroPhase/antsdr_standalone.git 注意&#xff1a;在下載源碼的時候&#xff0c;使…

Grafana配置連接時候證書與mongosqld啟動證書的關系

目錄 證書角色說明 1. BI Connector 端的證書 (--sslPEMKeyFile) 2. Grafana 端的證書 (TLS/SSL Client Certificate & Key) 它們之間的關系 配置建議 情況一&#xff1a;只需要服務器驗證&#xff08;最常見&#xff09; 情況二&#xff1a;需要雙向SSL認證&#x…

解決HTML/JS開發中的常見問題與實用資源

在前端開發過程中&#xff0c;即使是經驗豐富的開發者也會遇到各種小問題。本文將聚焦于兩個常見問題的解決方案&#xff0c;并推薦一些國內可訪問的優質源碼學習網站&#xff0c;幫助開發者提升效率。 一、字符編碼與亂碼問題解決 在HTML和JavaScript開發中&#xff0c;字符編…

SQLI-labs[Part 2]

本篇為SQLI-labs的Write-Up的第二部分包含Level 23- Level 27Level 23 過濾注釋符 字符注入拼接語句發現注釋符沒有生效 應該是被過濾了那只能通過拼接語句來除去后面的影響拼接?id1 or 11?id1%27%20or%20%271%27%271源碼中最后的導致語句閉合 Level 24 字符二次注入成功登錄…

宋紅康 JVM 筆記 Day17|垃圾回收器

一、今日視頻區間 P169-P203 二、一句話總結 GC分類與性能指標&#xff1b;不同的垃圾回收器概述&#xff1b;Serial回收器&#xff1a;串行回收&#xff1b;ParNew回收器&#xff1a;并行回收&#xff1b;Parallel回收器&#xff1a;吞吐量優先&#xff1b;CMS回收器&#xff…

[硬件電路-194]:NPN三極管、MOS-N, IGBT比較

NPN三極管、MOS-N&#xff08;N溝道MOS管&#xff09;和IGBT&#xff08;絕緣柵雙極型晶體管&#xff09;在電子電路設計中各有其獨特的應用場景和優勢&#xff0c;以下從工作原理、特性、應用領域三個維度進行比較&#xff1a;工作原理NPN三極管&#xff1a;結構&#xff1a;由…

【代碼隨想錄day 25】 力扣 46. 全排列

視頻講解&#xff1a;https://www.bilibili.com/video/BV19v4y1S79W/?vd_sourcea935eaede74a204ec74fd041b917810c 文檔講解&#xff1a;https://programmercarl.com/0046.%E5%85%A8%E6%8E%92%E5%88%97.html#%E6%80%9D%E8%B7%AF 力扣題目&#xff1a;https://leetcode.cn/prob…

指針(五)后半

1、 qsort 函數1.1、qsort 函數排列結構體在這里&#xff0c;我們創建結構體類型的數組&#xff0c;用于 qsort 函數的傳參。#include<stdio.h> #include<stdlib.h> #include<string.h>struct Stu//創建結構體變量 {char name[30];int age; };struct Stu arr…

TDengine 特殊選擇函數 MODE() 用戶手冊

MODE 函數用戶手冊 函數定義 MODE(expr)功能說明 MODE() 函數返回指定列中出現頻率最高的值&#xff08;眾數&#xff09;。如果有多個值具有相同的最高頻率&#xff0c;系統會返回其中一個值。該函數會忽略 NULL 值。 算法原理 MODE 函數的計算過程如下&#xff1a; 數據…

智能外骨骼技術應用場景及價格可接受區間分析

一、引言 智能外骨骼機器人融合機械、人工智能和傳感器技術,增強或恢復人體運動能力。2025年,該技術在醫療康復、工業生產、軍事應用和消費市場快速普及。本文分析其應用場景、市場需求、典型產品、價格可接受區間及相關來源,探討普及的關鍵因素。 二、主要應用場景及產品…

Vue模板中傳遞對象或數組時,避免直接使用字面量[]和{}

在 Vue 中&#xff0c;直接在模板中使用 [] 或 {} 作為 prop 值會導致子組件不必要的重新渲染&#xff0c;因為每次父組件渲染時都會創建新的引用。以下是解決方案和最佳實踐&#xff1a; 1. 避免在模板中直接使用字面量 <!-- 避免這樣寫 --> <ChildComponent :items&…

【C++】list容器的模擬實現

目錄 1. 節點(list_node) 的結構 2. 哨兵位頭節點 3. list容器的成員變量 4. 插入/刪除操作 4.1 插入操作&#xff08;insert&#xff09; 4.2 刪除操作&#xff08;erase&#xff09; 5. 迭代器的實現 6. 不同迭代器和const容器的限制 7. 重載operator-> 8. 迭代器…