// 这段 MFC 示例源代码演示如何使用 MFC Microsoft Office Fluent 用户界面 // (“Fluent UI”)。该示例仅供参考, // 用以补充《Microsoft 基础类参考》和 // MFC C++ 库软件随附的相关电子文档。 // 复制、使用或分发 Fluent UI 的许可条款是单独提供的。 // 若要了解有关 Fluent UI 许可计划的详细信息,请访问 // https://go.microsoft.com/fwlink/?LinkId=238214. // // 版权所有(C) Microsoft Corporation // 保留所有权利。 // VideoCaptureView.cpp: CVideoCaptureView 类的实现 // #include "stdafx.h" #include "framework.h" // SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的 // ATL 项目中进行定义,并允许与该项目共享文档代码。 #ifndef SHARED_HANDLERS #include "VideoCapture.h" #endif #include "VideoCaptureDoc.h" #include "VideoCaptureView.h" #include "MainFrm.h" #ifdef _DEBUG #define new DEBUG_NEW #endif CMemoryClient g_ub530; static CMainFrame* g_pMainFrame = NULL; VOID Dbg(CHAR* pszStr, ...) { char szData[MAX_PATH] = "[SC] "; va_list args; va_start(args, pszStr); _vsnprintf_s(szData + 5, MAX_PATH - 5, MAX_PATH - 6, pszStr, args); va_end(args); strcat_s(szData, "\n"); OutputDebugStringA(szData); } // FORMAT CHANGED CALLBACK FUNCTION // QRETURN on_process_format_changed(PVOID pDevice, ULONG nVideoInput, ULONG nAudioInput, ULONG nVideoWidth, ULONG nVideoHeight, BOOL bVideoIsInterleaved, double dVideoFrameRate, ULONG nAudioChannels, ULONG nAudioBitsPerSample, ULONG nAudioSampleFrequency, PVOID pUserData) { //Dbg("format changed Detected \n"); CVideoCaptureView* pView = (CVideoCaptureView*)pUserData; if (pView == NULL) { return QCAP_RT_OK; } if (pView->GetSafeHwnd() == NULL) { return QCAP_RT_OK; } pView->m_nVideoWidth = nVideoWidth; pView->m_nVideoHeight = nVideoHeight; pView->m_bVideoIsInterleaved = bVideoIsInterleaved; pView->m_dVideoFrameRate = dVideoFrameRate; pView->m_nAudioChannels = nAudioChannels; pView->m_nAudioBitsPerSample = nAudioBitsPerSample; pView->m_nAudioSampleFrequency = nAudioSampleFrequency; // OUTPUT FORMAT CHANGED MESSAGE // CHAR strVideoInput[MAX_PATH] = { 0 }; CHAR strAudioInput[MAX_PATH] = { 0 }; CHAR strFrameType[MAX_PATH] = { 0 }; UINT nVH = 0; if (nVideoInput == 0) { sprintf_s(strVideoInput, "COMPOSITE"); } if (nVideoInput == 1) { sprintf_s(strVideoInput, "SVIDEO"); } if (nVideoInput == 2) { sprintf_s(strVideoInput, "HDMI"); } if (nVideoInput == 3) { sprintf_s(strVideoInput, "DVI_D"); } if (nVideoInput == 4) { sprintf_s(strVideoInput, "COMPONENTS (YCBCR)"); } if (nVideoInput == 5) { sprintf_s(strVideoInput, "DVI_A (RGB / VGA)"); } if (nVideoInput == 6) { sprintf_s(strVideoInput, "SDI"); } if (nVideoInput == 7) { sprintf_s(strVideoInput, "AUTO"); } if (nAudioInput == 0) { sprintf_s(strAudioInput, "EMBEDDED_AUDIO"); } if (nAudioInput == 1) { sprintf_s(strAudioInput, "LINE_IN"); } if (bVideoIsInterleaved == TRUE) { nVH = nVideoHeight / 2; } else { nVH = nVideoHeight; } if (bVideoIsInterleaved == TRUE) { sprintf_s(strFrameType, " I "); } else { sprintf_s(strFrameType, " P "); } pView->m_strFormatChangedOutput.Format( _T("INFO : %d x %d%s @%2.3f FPS , %d CH x %d BITS x %d HZ , VIDEO INPUT : %s , AUDIO INPUT : %s"), nVideoWidth, nVH, strFrameType, dVideoFrameRate, nAudioChannels, nAudioBitsPerSample, nAudioSampleFrequency, strVideoInput, strAudioInput ); Global::WriteTextLog(_T("采集卡格式变更:%d x %d%s @%2.3f FPS , %d CH x %d BITS x %d HZ , 视频输入源 : %s , 音频输入源 : %s"), nVideoWidth, nVH, strFrameType, dVideoFrameRate, nAudioChannels, nAudioBitsPerSample, nAudioSampleFrequency, strVideoInput, strAudioInput); ::SendMessage(g_pMainFrame->m_hWnd, MSG_STATUS_BAR, (WPARAM)& pView->m_strFormatChangedOutput, ID_STATUSBAR_PANE1); // NO SOURCE // if (nVideoWidth == 0 && nVideoHeight == 0 && dVideoFrameRate == 0.0 && nAudioChannels == 0 && nAudioBitsPerSample == 0 && nAudioSampleFrequency == 0) { pView->m_bNoSignal = TRUE; } else { pView->m_bNoSignal = FALSE; } pView->SetTimer(0x00000000, 1, NULL); return QCAP_RT_OK; } // NO SIGNAL DETEACTED CALLBACK FUNCTION // QRETURN on_process_no_signal_detected(PVOID pDevice, ULONG nVideoInput, ULONG nAudioInput, PVOID pUserData) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CVideoCaptureView* pView = (CVideoCaptureView*)pUserData; //Dbg("No Signal Detected \n"); if (pView == NULL) { return QCAP_RT_OK; } if (pView->GetSafeHwnd() == NULL) { return QCAP_RT_OK; } //pView->SetRibbonStatusBarText("当前采集卡无信号。", ID_STATUSBAR_PANE1); Global::WriteTextLog(_T("当前采集卡无信号")); ::SendMessage(g_pMainFrame->m_hWnd, MSG_STATUS_BAR, (WPARAM)& CString("当前采集卡无信号"), ID_STATUSBAR_PANE1); pView->m_bNoSignal = TRUE; pView->SetTimer(0x00000000, 1, NULL); return QCAP_RT_OK; } // SIGNAL REMOVED CALLBACK FUNCTION // QRETURN on_process_signal_removed(PVOID pDevice, ULONG nVideoInput, ULONG nAudioInput, PVOID pUserData) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CVideoCaptureView* pView = (CVideoCaptureView*)pUserData; if (pView == NULL) { return QCAP_RT_OK; } if (pView->GetSafeHwnd() == NULL) { return QCAP_RT_OK; } //pView->SetRibbonStatusBarText("当前采集卡信号被移除。", ID_STATUSBAR_PANE1); Global::WriteTextLog(_T("当前采集卡信号被移除")); ::SendMessage(g_pMainFrame->m_hWnd, MSG_STATUS_BAR, (WPARAM)& CString("当前采集卡信号被移除"), ID_STATUSBAR_PANE1); pView->m_bNoSignal = TRUE; pView->SetTimer(0x00000000, 1, NULL); return QCAP_RT_OK; } // PREVIEW VIDEO CALLBACK FUNCTION // QRETURN on_process_preview_video_buffer(PVOID pDevice, double dSampleTime, BYTE* pFrameBuffer, ULONG nFrameBufferLen, PVOID pUserData) { CVideoCaptureView* pView = (CVideoCaptureView*)pUserData; if (pView) { // 尝试加锁; if (!pView->m_mut_cpature.try_lock()) { #ifdef DEBUG OutputDebugStringA("!pView->m_mut_cpature.try_lock()\n"); #endif return QCAP_RT_OK; } // 是否触发截图; if (pView->m_bCaptureImage) { // 加锁; std::lock_guard lk(pView->m_mut_thread); // 复制数据; pView->m_dwBufferLen = nFrameBufferLen; pView->m_pBuffer = new BYTE[nFrameBufferLen]; memcpy(pView->m_pBuffer, pFrameBuffer, nFrameBufferLen); // 通知线程保存图片; pView->m_thread_cond.notify_one(); // 恢复截图标记,防止重复截图; pView->m_bCaptureImage = FALSE; #ifdef DEBUG OutputDebugStringA("preview_video_buffer:pView->m_bCaptureImage\n"); #endif } // 解锁; pView->m_mut_cpature.unlock(); } return QCAP_RT_OK; } // PREVIEW AUDIO CALLBACK FUNCTION // QRETURN on_process_preview_audio_buffer(PVOID pDevice, double dSampleTime, BYTE* pFrameBuffer, ULONG nFrameBufferLen, PVOID pUserData) { return QCAP_RT_OK; } // VIDEO HARDARE ENCODER CALLBACK FUNCTION // QRETURN on_process_hardware_encoder_video_buffer(PVOID pDevice, UINT iRecNum, double dSampleTime, BYTE* pFrameBuffer, ULONG nFrameBufferLen, BOOL bIsKeyFrame, PVOID pUserData) { return QCAP_RT_OK; } QRETURN on_process_snapshot_done(PVOID pDevice, CHAR* pszFilePathName, PVOID pUserData) { return QCAP_RT_OK; } QRETURN on_process_snapshot_stream(PVOID pDevice, CHAR* pszFilePathName, BYTE* pStreamBuffer, ULONG nStreamBufferLen, PVOID pUserData) { // 注意:在调用本回调函数前,已经保存了图片! CVideoCaptureView* pView = (CVideoCaptureView*)pUserData; if (pView->m_bHoriontal || pView->m_bVertically) Global::SaveImgByRotate(pszFilePathName, pStreamBuffer, nStreamBufferLen, pView->m_bHoriontal, pView->m_bVertically); return QCAP_RT_OK; } // CVideoCaptureView IMPLEMENT_DYNCREATE(CVideoCaptureView, CView) BEGIN_MESSAGE_MAP(CVideoCaptureView, CView) // 标准打印命令 ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CVideoCaptureView::OnFilePrintPreview) ON_WM_CONTEXTMENU() ON_WM_RBUTTONUP() ON_WM_CREATE() ON_WM_TIMER() ON_COMMAND(ID_CUT_BMP, &CVideoCaptureView::OnCutBmp) ON_COMMAND(ID_CUT_JPG, &CVideoCaptureView::OnCutJpg) ON_COMMAND(ID_START_RECORD, &CVideoCaptureView::OnStartRecord) ON_COMMAND(ID_STOP_RECORD, &CVideoCaptureView::OnStopRecord) ON_COMMAND(ID_CHECK_VERTICALLY, &CVideoCaptureView::OnCheckVertically) ON_COMMAND(ID_CHECK_HORIONTAL, &CVideoCaptureView::OnCheckHoriontal) ON_UPDATE_COMMAND_UI(ID_CHECK_VERTICALLY, &CVideoCaptureView::OnUpdateCheckVertically) ON_UPDATE_COMMAND_UI(ID_CHECK_HORIONTAL, &CVideoCaptureView::OnUpdateCheckHoriontal) ON_COMMAND(ID_TRAYMENU_RECONNECT, &CVideoCaptureView::OnTraymenuReconnect) ON_COMMAND(ID_CHECK_SUPORT_GPU, &CVideoCaptureView::OnCheckSuportGpu) ON_COMMAND(ID_CHECK_FORMAT_MP4, &CVideoCaptureView::OnCheckFormatMp4) ON_COMMAND(ID_CHECK_FORMAT_AVI, &CVideoCaptureView::OnCheckFormatAvi) ON_UPDATE_COMMAND_UI(ID_CHECK_SUPORT_GPU, &CVideoCaptureView::OnUpdateCheckSuportGpu) ON_UPDATE_COMMAND_UI(ID_CHECK_FORMAT_MP4, &CVideoCaptureView::OnUpdateCheckFormatMp4) ON_UPDATE_COMMAND_UI(ID_CHECK_FORMAT_AVI, &CVideoCaptureView::OnUpdateCheckFormatAvi) ON_UPDATE_COMMAND_UI(ID_START_RECORD, &CVideoCaptureView::OnUpdateStartRecord) ON_UPDATE_COMMAND_UI(ID_STOP_RECORD, &CVideoCaptureView::OnUpdateStopRecord) END_MESSAGE_MAP() // CVideoCaptureView 构造/析构 CVideoCaptureView::CVideoCaptureView() noexcept { // TODO: 在此处添加构造代码 m_hVideoDevice = NULL; m_bIsRecord = FALSE; m_bNoSignal = FALSE; m_bVertically = FALSE; m_bHoriontal = FALSE; m_bAppQuit = FALSE; m_pBuffer = NULL; m_bCaptureImage = FALSE; m_dwBufferLen = 0; m_bIsMp4 = TRUE; m_bSupportGPU = FALSE; } CVideoCaptureView::~CVideoCaptureView() { g_ub530.EndOfThread(); m_bAppQuit = TRUE; HwUninitialize(); } BOOL CVideoCaptureView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: 在此处通过修改 // CREATESTRUCT cs 来修改窗口类或样式 return CView::PreCreateWindow(cs); } // CVideoCaptureView 绘图 void CVideoCaptureView::OnDraw(CDC* pDC) { CVideoCaptureDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TO FILL IN THE TEXT // if (m_hVideoDevice == 0 || m_bNoSignal) { CFont font; ULONG nFontSize = 300; font.CreatePointFont(nFontSize, TEXT("Arial"), NULL); HFONT hOldFont = (HFONT)pDC->SelectObject(&font); pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(RGB(255, 255, 255)); CRect rect_client; GetClientRect(&rect_client); CString string = _T(""); if (m_hVideoDevice == 0) { CBrush brush_fill_rect(RGB(0, 0, 0)); pDC->FillRect(&rect_client, &brush_fill_rect); string = TEXT("No Device"); //g_pMainFrame->SetRibbonStatusBarText("当前采集卡设备被移除。", ID_STATUSBAR_PANE1); } else if(m_bNoSignal) { CBrush brush_fill_rect(RGB(0, 0, 255)); pDC->FillRect(&rect_client, &brush_fill_rect); string = TEXT("No Source"); //g_pMainFrame->SetRibbonStatusBarText("当前采集卡信号被移除。", ID_STATUSBAR_PANE1); } pDC->DrawText(string, &rect_client, DT_SINGLELINE | DT_CENTER | DT_VCENTER); pDC->SelectObject(&hOldFont); font.DeleteObject(); } // TODO: 在此处为本机数据添加绘制代码 } // CVideoCaptureView 打印 BOOL CVideoCaptureView::HwInitialize() { if (m_hVideoDevice != NULL) return TRUE; // CALLBACK FUNCTION PF_FORMAT_CHANGED_CALLBACK pFormatChangedCB = { 0 }; PF_VIDEO_PREVIEW_CALLBACK pPreviewVideoCB = { 0 }; PF_AUDIO_PREVIEW_CALLBACK pPreviewAudioCB = { 0 }; PF_VIDEO_HARDWARE_ENCODER_CALLBACK pHardwareEncoderVideoCB = { 0 }; PF_NO_SIGNAL_DETECTED_CALLBACK pNoSignalDetectedCB = { 0 }; PF_SIGNAL_REMOVED_CALLBACK pSignalRemovedCB = { 0 }; PF_SNAPSHOT_DONE_CALLBACK pSnapShotDoneCB = { 0 }; PF_SNAPSHOT_STREAM_CALLBACK pSnapShotStreamCB = { 0 }; // CREATE CAPTURE DEVICE // if (m_hVideoDevice == NULL) QCAP_CREATE("CY3014 USB", 0, m_hWnd, &m_hVideoDevice, 1); if (m_hVideoDevice == NULL) { return FALSE; } // REGISTER FORMAT CHANGED CALLBACK FUNCTION pFormatChangedCB = on_process_format_changed; QCAP_REGISTER_FORMAT_CHANGED_CALLBACK(m_hVideoDevice, pFormatChangedCB, this); // REGISTER PREVIEW VIDEO CALLBACK FUNCTION pPreviewVideoCB = on_process_preview_video_buffer; QCAP_REGISTER_VIDEO_PREVIEW_CALLBACK(m_hVideoDevice, pPreviewVideoCB, this); // REGISTER PREVIEW AUDIO CALLBACK FUNCTION //pPreviewAudioCB = on_process_preview_audio_buffer; //QCAP_REGISTER_AUDIO_PREVIEW_CALLBACK(m_hVideoDevice, pPreviewAudioCB, this); // REGISTER HARDWARE ENCODER VIDEO CALLBACK FUNCTION //pHardwareEncoderVideoCB = on_process_hardware_encoder_video_buffer; //QCAP_REGISTER_VIDEO_HARDWARE_ENCODER_CALLBACK(m_hVideoDevice, 0, pHardwareEncoderVideoCB, this); // REGISTER NO SIGNAL DETECTED CALLBACK FUNCTION pNoSignalDetectedCB = on_process_no_signal_detected; QCAP_REGISTER_NO_SIGNAL_DETECTED_CALLBACK(m_hVideoDevice, pNoSignalDetectedCB, this); // REGISTER SIGNAL REMOVED CALLBACK FUNCTION pSignalRemovedCB = on_process_signal_removed; QCAP_REGISTER_SIGNAL_REMOVED_CALLBACK(m_hVideoDevice, pSignalRemovedCB, this); #if 1 // pSnapShotDoneCB = on_process_snapshot_done; QCAP_REGISTER_SNAPSHOT_DONE_CALLBACK(m_hVideoDevice, pSnapShotDoneCB, this); // pSnapShotStreamCB = on_process_snapshot_stream; QCAP_REGISTER_SNAPSHOT_STREAM_CALLBACK(m_hVideoDevice, pSnapShotStreamCB, this); #endif QCAP_SET_VIDEO_DEINTERLACE_TYPE(m_hVideoDevice, QCAP_SOFTWARE_DEINTERLACE_TYPE_BLENDING); QCAP_SET_VIDEO_DEINTERLACE(m_hVideoDevice, 0); QCAP_SET_AUDIO_VOLUME(m_hVideoDevice, 100); QCAP_SET_VIDEO_HARDWARE_ENCODER_PROPERTY(m_hVideoDevice, 0, QCAP_ENCODER_FORMAT_H264, QCAP_RECORD_MODE_CBR, 8000, 12 * 1024 * 1024, 30, 0, 0, QCAP_DOWNSCALE_MODE_OFF, 0, 0); QCAP_SET_VIDEO_INPUT(m_hVideoDevice, QCAP_INPUT_TYPE_AUTO); QCAP_RUN(m_hVideoDevice); // 刷新区域; Invalidate(); // UPDATE USER INTERFACE RESOURCE if (m_hVideoDevice == 0) { /*m_oSetupDialog.m_btnVideoInput.EnableWindow(FALSE); m_oSetupDialog.m_btnVideoQuality.EnableWindow(FALSE); m_oSetupDialog.m_btnAudioInput.EnableWindow(FALSE); m_oSetupDialog.m_btnSnapshot_bmp.EnableWindow(FALSE); m_oSetupDialog.m_btnSnapshot_jpg.EnableWindow(FALSE); m_oSetupDialog.m_btnRecordStart_1_1.EnableWindow(FALSE); m_oSetupDialog.m_btnRecordStop_1_1.EnableWindow(FALSE); m_oSetupDialog.m_btnRecordStart_1_2.EnableWindow(FALSE); m_oSetupDialog.m_btnRecordStop_1_2.EnableWindow(FALSE); m_oSetupDialog.m_btnRecordStart_1_3.EnableWindow(FALSE); m_oSetupDialog.m_btnRecordStop_1_3.EnableWindow(FALSE); m_oSetupDialog.m_checkGPU_1_1.EnableWindow(FALSE); m_oSetupDialog.m_checkGPU_1_1.SetCheck(0); m_oSetupDialog.m_checkGPU_1_2.EnableWindow(FALSE); m_oSetupDialog.m_checkGPU_1_2.SetCheck(0); m_oSetupDialog.m_checkGPU_1_3.EnableWindow(FALSE); m_oSetupDialog.m_checkGPU_1_3.SetCheck(0); m_oSetupDialog.m_checkAutoDeinterlace.EnableWindow(FALSE); m_oSetupDialog.m_checkAutoDeinterlace.SetCheck(0);*/ } else { /*m_oSetupDialog.m_btnSnapshot_bmp.EnableWindow(TRUE); m_oSetupDialog.m_btnSnapshot_jpg.EnableWindow(TRUE); m_oSetupDialog.m_btnRecordStart_1_1.EnableWindow(TRUE); m_oSetupDialog.m_btnRecordStop_1_1.EnableWindow(FALSE); m_oSetupDialog.m_btnRecordStart_1_2.EnableWindow(TRUE); m_oSetupDialog.m_btnRecordStop_1_2.EnableWindow(FALSE); m_oSetupDialog.m_btnRecordStart_1_3.EnableWindow(TRUE); m_oSetupDialog.m_btnRecordStop_1_3.EnableWindow(FALSE); m_oSetupDialog.m_checkGPU_1_1.EnableWindow(FALSE); m_oSetupDialog.m_checkGPU_1_1.SetCheck(0); m_oSetupDialog.m_checkGPU_1_2.EnableWindow(TRUE); m_oSetupDialog.m_checkGPU_1_2.SetCheck(0); m_oSetupDialog.m_checkGPU_1_3.EnableWindow(TRUE); m_oSetupDialog.m_checkGPU_1_3.SetCheck(0); m_oSetupDialog.m_checkAutoDeinterlace.EnableWindow(TRUE); m_oSetupDialog.m_checkAutoDeinterlace.SetCheck(0);*/ } return TRUE; } BOOL CVideoCaptureView::HwUninitialize() { if (m_hVideoDevice != 0) { QCAP_STOP(m_hVideoDevice); QCAP_DESTROY(m_hVideoDevice); m_hVideoDevice = NULL; } return TRUE; } void CVideoCaptureView::CaptureSingleImage(LPTSTR lpszFileName, BOOL bIsJPG) { if (m_hVideoDevice != NULL) { if (bIsJPG) { QCAP_SNAPSHOT_JPG(m_hVideoDevice, lpszFileName, 100, FALSE, 5000); //参数4:是否异步; } else { QCAP_SNAPSHOT_BMP(m_hVideoDevice, lpszFileName, FALSE, 5000); } #ifdef _DEBUG Global::WriteTextLog("完成:CaptureSingleImage"); #endif } } std::string CVideoCaptureView::CaptureSingleImageAutoName(LPCTSTR lpszDir, BOOL bIsJPG) { if (m_hVideoDevice != NULL) { TCHAR szPath[MAX_PATH] = { 0 }; // 毫秒级; time_point tp = time_point_cast(system_clock::now()); auto tt = std::chrono::system_clock::to_time_t(tp); std::tm now = { 0 }; localtime_s(&now, &tt); // 时间戳转成本地时间; int msc = tp.time_since_epoch().count() % 1000; if (bIsJPG) { _stprintf_s(szPath, _T("%s%04d%02d%02d%02d%02d%02d%03d.jpg"), lpszDir ? lpszDir : Global::g_szCurModuleDir, now.tm_year + 1990, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, msc); QCAP_SNAPSHOT_JPG(m_hVideoDevice, szPath, 100, FALSE, 5000); } else { _stprintf_s(szPath, _T("%s%04d%02d%02d%02d%02d%02d%03d.bmp"), lpszDir ? lpszDir : Global::g_szCurModuleDir, now.tm_year + 1990, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, msc); QCAP_SNAPSHOT_BMP(m_hVideoDevice, szPath, FALSE, 5000); } #ifdef _DEBUG Global::WriteTextLog("完成:CaptureSingleImageAutoName"); #endif return std::string(szPath); } return std::string(); } void CVideoCaptureView::CaptureMultiImage(LPCTSTR lpszDir, LPCTSTR lpszPrefix, BOOL bIsJPG, int nDurationTime) { std::thread t([&](CVideoCaptureView* p, LPCTSTR lpszDir, LPCTSTR lpszPrefix, BOOL bIsJPG, int nDurationTime) { // 连续截图; TCHAR szDir[MAX_PATH] = { 0 }; TCHAR szPath[MAX_PATH] = { 0 }; _stprintf_s(szDir, lpszPrefix ? _T("%s%s-") : _T("%s%s"), lpszDir ? lpszDir : Global::g_szCurModuleDir, lpszPrefix ? lpszPrefix : _T("")); auto start = system_clock::now(); while (true) { // C++11获取当前时间; time_point tp = time_point_cast(system_clock::now()); if (bIsJPG) { _stprintf_s(szPath, _T("%s%lld.jpg"), szDir, tp.time_since_epoch().count()); QCAP_SNAPSHOT_JPG(m_hVideoDevice, szPath, 100, TRUE); // 等待磁盘完成写入; //Sleep(20); } else { _stprintf_s(szPath, _T("%s%lld.bmp"), szDir, tp.time_since_epoch().count()); QCAP_SNAPSHOT_BMP(m_hVideoDevice, szPath, TRUE); // 等待磁盘完成写入; //Sleep(530); } auto duration = duration_cast(system_clock::now() - start); if (duration.count() >= nDurationTime) break; } }, this, lpszDir, lpszPrefix, bIsJPG, nDurationTime); t.detach(); } void CVideoCaptureView::CaptureSingleImageEx(LPTSTR lpszFileName, BOOL bIsJPG) { { // 加锁; std::lock_guard lk(m_mut_cpature); // 标记截图; m_bCaptureImage = TRUE; // 初始化数据; ZeroMemory(&m_CaptureInfo, sizeof(CaptureInfo)); m_CaptureInfo.bIsJPG = bIsJPG; m_CaptureInfo.bSingle = TRUE; m_CaptureInfo.IsAutoName = FALSE; m_CaptureInfo.nCaputerCount = 0; m_CaptureInfo.nKeepTime = 0; memset(m_CaptureInfo.szPrefix, 0, 64); //_stprintf_s(m_CaptureInfo.szSaveDir, _T("%s"), lpszFileName); _stprintf_s(m_CaptureInfo.szSaveDir, _T("%s"), lpszFileName); //m_strCaptureName = lpszFileName; } // 等待截图完成; { // 加锁; std::unique_lock lk(m_mut_cpature); m_capture_cond.wait(lk, [&]() {return !m_pBuffer; }); // 解锁; lk.unlock(); } } std::string CVideoCaptureView::CaptureSingleImageAutoNameEx(LPCTSTR lpszDir, BOOL bIsJPG) { { // 加锁; std::lock_guard lk(m_mut_cpature); // 标记截图; m_bCaptureImage = TRUE; // 初始化数据; ZeroMemory(&m_CaptureInfo, sizeof(CaptureInfo)); m_CaptureInfo.bIsJPG = bIsJPG; m_CaptureInfo.bSingle = TRUE; m_CaptureInfo.IsAutoName = FALSE; m_CaptureInfo.nCaputerCount = 0; m_CaptureInfo.nKeepTime = 0; memset(m_CaptureInfo.szPrefix, 0, 64); memset(m_CaptureInfo.szSaveDir, 0, MAX_PATH); // 毫秒级; time_point tp = time_point_cast(system_clock::now()); auto tt = std::chrono::system_clock::to_time_t(tp); std::tm now = { 0 }; localtime_s(&now, &tt); // 时间戳转成本地时间; int msc = tp.time_since_epoch().count() % 1000; if (bIsJPG) { _stprintf_s(m_CaptureInfo.szSaveDir, _T("%s%04d%02d%02d%02d%02d%02d%03d.jpg"), lpszDir ? lpszDir : Global::g_szCurModuleDir, now.tm_year + 1990, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, msc); } else { _stprintf_s(m_CaptureInfo.szSaveDir, _T("%s%04d%02d%02d%02d%02d%02d%03d.bmp"), lpszDir ? lpszDir : Global::g_szCurModuleDir, now.tm_year + 1990, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, msc); } #ifdef DEBUG OutputDebugStringA(m_CaptureInfo.szSaveDir); OutputDebugStringA("\tCVideoCaptureView::CaptureSingleImageAutoNameEx\n"); #endif // DEBUG } // 等待截图完成; { // 加锁; std::unique_lock lk(m_mut_cpature); m_capture_cond.wait(lk, [&]() {return !m_pBuffer; }); // 解锁; lk.unlock(); } return std::string(m_CaptureInfo.szSaveDir); } void CVideoCaptureView::CaptureMultiImageEx(LPCTSTR lpszDir, LPCTSTR lpszPrefix, BOOL bIsJPG, int nDurationTime) { std::thread t([&](CVideoCaptureView* p, LPCTSTR lpszDir, LPCTSTR lpszPrefix, BOOL bIsJPG, int nDurationTime) { // 连续截图; TCHAR szDir[MAX_PATH] = { 0 }; _stprintf_s(szDir, lpszPrefix ? _T("%s%s-") : _T("%s%s"), lpszDir ? lpszDir : Global::g_szCurModuleDir, lpszPrefix ? lpszPrefix : _T("")); auto start = system_clock::now(); while (true) { // C++11获取当前时间; time_point tp = time_point_cast(system_clock::now()); { // 加锁; std::lock_guard lk(m_mut_cpature); // 标记截图; m_bCaptureImage = TRUE; // 初始化数据; ZeroMemory(&m_CaptureInfo, sizeof(CaptureInfo)); m_CaptureInfo.bIsJPG = bIsJPG; m_CaptureInfo.bSingle = FALSE; m_CaptureInfo.IsAutoName = TRUE; m_CaptureInfo.nCaputerCount = 0; m_CaptureInfo.nKeepTime = nDurationTime; _stprintf_s(m_CaptureInfo.szPrefix, _T("%s"), lpszPrefix); if (bIsJPG) { _stprintf_s(m_CaptureInfo.szSaveDir, _T("%s%lld.jpg"), szDir, tp.time_since_epoch().count()); } else { _stprintf_s(m_CaptureInfo.szSaveDir, _T("%s%lld.bmp"), szDir, tp.time_since_epoch().count()); } } // 等待截图完成; { // 加锁; std::unique_lock lk(m_mut_cpature); m_capture_cond.wait(lk, [&]() {return !m_pBuffer; }); // 解锁; lk.unlock(); } auto duration = duration_cast(system_clock::now() - start); if (duration.count() >= nDurationTime) break; } }, this, lpszDir, lpszPrefix, bIsJPG, nDurationTime); t.detach(); } BOOL CVideoCaptureView::SaveImageByCaptureInfo(const CaptureInfo& capInfo) { if (m_pBuffer == NULL) { return FALSE; } BOOL bRet = FALSE; // 保存截图; HGLOBAL hMemery = GlobalAlloc(GMEM_MOVEABLE, m_nVideoWidth * m_nVideoHeight * 4); if (hMemery != NULL) { BYTE* pDstFrameBuffer = NULL; pDstFrameBuffer = (BYTE*)GlobalLock(hMemery); if (pDstFrameBuffer != NULL) { QRESULT QRet = QCAP_COLORSPACE_YUY2_TO_ABGR32(m_pBuffer, m_nVideoWidth, m_nVideoHeight, 0, pDstFrameBuffer, m_nVideoWidth, m_nVideoHeight, 0); Status stat = GenericError; Bitmap* pImg = ::new Bitmap( m_nVideoWidth, m_nVideoHeight, m_nVideoWidth * 4, PixelFormat32bppRGB, pDstFrameBuffer); if (m_bHoriontal && !m_bVertically) pImg->RotateFlip(RotateNoneFlipX);// 水平翻转; else if (m_bHoriontal && m_bVertically) pImg->RotateFlip(Rotate180FlipNone);// 270度; else if (!m_bHoriontal && m_bVertically) pImg->RotateFlip(Rotate180FlipX);// 垂直翻转; CLSID encoderClsid = { 0 }; CString strFileName = m_CaptureInfo.szSaveDir; // 需要判断路径是否存在,不存在创建目录; int nIndex = strFileName.ReverseFind(_T('\\')); if (nIndex != -1) { if (!PathFileExists(strFileName.Left(nIndex))) { // 如果文件夹不存在,创建; SHCreateDirectoryEx(NULL, strFileName.Left(nIndex), NULL); } } BSTR newfile = strFileName.AllocSysString(); if (!capInfo.bIsJPG) { Global::GetEncoderClsid(L"image/bmp", &encoderClsid); stat = pImg->Save(newfile, &encoderClsid, NULL); } else { Global::GetEncoderClsid(L"image/jpeg", &encoderClsid); EncoderParameters encoderParameters; encoderParameters.Count = 1; encoderParameters.Parameter[0].Guid = EncoderQuality; encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong; encoderParameters.Parameter[0].NumberOfValues = 1; // Save the image as a JPEG with quality level 100. ULONG uQuality = 100; encoderParameters.Parameter[0].Value = &uQuality; stat = pImg->Save(newfile, &encoderClsid, &encoderParameters); } if (pImg) ::delete pImg; pImg = NULL; SysFreeString(newfile); bRet = (stat == 0 ? TRUE : FALSE); #ifdef DEBUG if (bRet) { OutputDebugStringA(strFileName); OutputDebugStringA("\tSave Image::Ok\n"); } #endif } #ifdef DEBUG else { OutputDebugStringA("pDstFrameBuffer == NULL\n"); } #endif GlobalUnlock(hMemery); } #ifdef DEBUG else { DWORD dwError = GetLastError(); CString strError = _T(""); strError.Format(_T("hMemery == NULL分配内存出错:%ld\n"), dwError); OutputDebugStringA(strError); } #endif if (m_pBuffer) delete[]m_pBuffer; m_pBuffer = NULL; // 必须调用GlobalFree释放; GlobalFree(hMemery); return bRet; } void CVideoCaptureView::CaptureImageThread(CVideoCaptureView* pView) { CString strFileName = _T(""); while (!pView->m_bAppQuit) { std::unique_lock lk(pView->m_mut_thread); // 这里使用unique_lock是为了后面方便解锁 #if 1 // 避免线程虚假唤醒; pView->m_thread_cond.wait(lk, [&]() {return pView->m_pBuffer; }); #else while (!pView->m_pBuffer) pView->m_data_cond.wait(lk); #endif // 保存截图; pView->SaveImageByCaptureInfo(pView->m_CaptureInfo); // 通知截图完成; pView->m_capture_cond.notify_one(); // 解锁; lk.unlock(); // sleep 10ms; //this_thread::sleep_for(chrono::milliseconds(10)); } } void CVideoCaptureView::StartRecord(DWORD dwDuration, LPCTSTR lpSavePath) { QRESULT QRet = QCAP_RS_SUCCESSFUL; if (!m_bIsMp4) // 音频格式; { QRet = QCAP_SET_AUDIO_RECORD_PROPERTY(m_hVideoDevice, 0, QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_PCM); //_tcscat_s(szPath, _T(".avi")); } else { QRet = QCAP_SET_AUDIO_RECORD_PROPERTY(m_hVideoDevice, 0, QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_AAC); //_tcscat_s(szPath, _T(".mp4")); } // 设置视频属性; QRet = QCAP_SET_VIDEO_RECORD_PROPERTY( m_hVideoDevice, 0, m_bSupportGPU ? QCAP_ENCODER_TYPE_INTEL_MEDIA_SDK : QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_H264, QCAP_RECORD_MODE_CBR, 8000, 16 * 1024 * 1024, // 16最高质量; 30, // 每秒多少帧 0, 0, QCAP_DOWNSCALE_MODE_OFF); // 开始录屏; QRet = QCAP_START_RECORD(m_hVideoDevice, 0, const_cast(lpSavePath), QCAP_RECORD_FLAG_FULL, 0.0, 0.0, 0.0, 0, NULL); m_bIsRecord = TRUE; } void CVideoCaptureView::StopRecord() { QCAP_STOP_RECORD(m_hVideoDevice, 0); m_bIsRecord = FALSE; } void CVideoCaptureView::OnFilePrintPreview() { #ifndef SHARED_HANDLERS AFXPrintPreview(this); #endif } BOOL CVideoCaptureView::OnPreparePrinting(CPrintInfo* pInfo) { // 默认准备 return DoPreparePrinting(pInfo); } void CVideoCaptureView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: 添加额外的打印前进行的初始化过程 } void CVideoCaptureView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: 添加打印后进行的清理过程 } void CVideoCaptureView::OnRButtonUp(UINT /* nFlags */, CPoint point) { ClientToScreen(&point); OnContextMenu(this, point); } void CVideoCaptureView::OnContextMenu(CWnd* /* pWnd */, CPoint point) { #ifndef SHARED_HANDLERS theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE); #endif } // CVideoCaptureView 诊断 #ifdef _DEBUG void CVideoCaptureView::AssertValid() const { CView::AssertValid(); } void CVideoCaptureView::Dump(CDumpContext& dc) const { CView::Dump(dc); } CVideoCaptureDoc* CVideoCaptureView::GetDocument() const // 非调试版本是内联的 { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CVideoCaptureDoc))); return (CVideoCaptureDoc*)m_pDocument; } #endif //_DEBUG // CVideoCaptureView 消息处理程序 void CVideoCaptureView::SetRibbonStatusBarText(CString strText, int uId) { if ( g_pMainFrame ) { g_pMainFrame->SetRibbonStatusBarText(strText, uId); } } int CVideoCaptureView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; // 初始化共享内存; CMemoryClient::m_pView = this; g_ub530.InitMemery(); g_ub530.StartThread(); g_pMainFrame = (CMainFrame*)this->GetParent(); // TODO: 在此添加您专用的创建代码 // 创建视频流,并关联指定显示窗口; HwInitialize(); // 创建截图线程; std::thread t(CaptureImageThread, this); t.detach(); return 0; } void CVideoCaptureView::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (nIDEvent == 0) { KillTimer(0); if (m_bNoSignal) { if (m_bIsRecord) { QCAP_STOP_RECORD(m_hVideoDevice, 0); m_bIsRecord = FALSE; } } else { } } CView::OnTimer(nIDEvent); } void CVideoCaptureView::OnCutBmp() { // TODO: 在此添加命令处理程序代码 CaptureSingleImageAutoName(NULL, FALSE); } void CVideoCaptureView::OnCutJpg() { // TODO: 在此添加命令处理程序代码 CaptureSingleImageAutoName(NULL, TRUE); } void CVideoCaptureView::OnStartRecord() { // TODO: 在此添加命令处理程序代码 TCHAR szPath[MAX_PATH] = { 0 }; // 毫秒级; time_point tp = time_point_cast(system_clock::now()); auto tt = std::chrono::system_clock::to_time_t(tp); std::tm now = { 0 }; localtime_s(&now, &tt); // 时间戳转成本地时间; int msc = tp.time_since_epoch().count() % 1000; _stprintf_s(szPath, _T("%s%04d%02d%02d%02d%02d%02d%03d"), #ifdef _DEBUG _T("D:\\bin\\VideoCapture\\"), #else Global::g_szCurModuleDir, #endif now.tm_year + 1990, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, msc); QRESULT QRet = QCAP_RS_SUCCESSFUL; if (!m_bIsMp4) // 音频格式; { QRet = QCAP_SET_AUDIO_RECORD_PROPERTY(m_hVideoDevice, 0, QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_PCM); _tcscat_s(szPath, _T(".avi")); } else { QRet = QCAP_SET_AUDIO_RECORD_PROPERTY(m_hVideoDevice, 0, QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_AAC); _tcscat_s(szPath, _T(".mp4")); } // 设置视频属性; QRet = QCAP_SET_VIDEO_RECORD_PROPERTY( m_hVideoDevice, 0, m_bSupportGPU ? QCAP_ENCODER_TYPE_INTEL_MEDIA_SDK : QCAP_ENCODER_TYPE_SOFTWARE, QCAP_ENCODER_FORMAT_H264, QCAP_RECORD_MODE_CBR, 8000, 16*1024*1024, // 16最高质量; 30, // 每秒多少帧 0, 0, QCAP_DOWNSCALE_MODE_OFF); // 开始录屏; QRet = QCAP_START_RECORD(m_hVideoDevice, 0, szPath, QCAP_RECORD_FLAG_FULL, 0.0, 0.0, 0.0, 0, NULL); m_bIsRecord = TRUE; } void CVideoCaptureView::OnStopRecord() { // TODO: 在此添加命令处理程序代码 QCAP_STOP_RECORD(m_hVideoDevice, 0); m_bIsRecord = FALSE; } void CVideoCaptureView::OnCheckVertically() { // TODO: 在此添加命令处理程序代码 CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd(); CMFCRibbonButton* pCheckbox = pMain->GetRibbonButton(ID_CHECK_VERTICALLY); m_bVertically = !pCheckbox->IsChecked(); if (m_hVideoDevice) { QCAP_SET_VIDEO_MIRROR(m_hVideoDevice, m_bHoriontal, m_bVertically); } } void CVideoCaptureView::OnCheckHoriontal() { // TODO: 在此添加命令处理程序代码 CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd(); CMFCRibbonButton* pCheckbox = pMain->GetRibbonButton(ID_CHECK_HORIONTAL); m_bHoriontal = !pCheckbox->IsChecked(); if (m_hVideoDevice) { QCAP_SET_VIDEO_MIRROR(m_hVideoDevice, m_bHoriontal, m_bVertically); } } void CVideoCaptureView::OnUpdateCheckVertically(CCmdUI* pCmdUI) { // TODO: 在此添加命令更新用户界面处理程序代码 pCmdUI->SetCheck(m_bVertically); } void CVideoCaptureView::OnUpdateCheckHoriontal(CCmdUI* pCmdUI) { // TODO: 在此添加命令更新用户界面处理程序代码 pCmdUI->SetCheck(m_bHoriontal); } void CVideoCaptureView::OnTraymenuReconnect() { // TODO: 在此添加命令处理程序代码 HwUninitialize();// 断开; Invalidate(); HwInitialize();// 重连; } void CVideoCaptureView::OnCheckSuportGpu() { // TODO: 在此添加命令处理程序代码 CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd(); CMFCRibbonButton* pCheckbox = pMain->GetRibbonButton(ID_CHECK_SUPORT_GPU); m_bSupportGPU = !pCheckbox->IsChecked(); } void CVideoCaptureView::OnCheckFormatMp4() { CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd(); CMFCRibbonButton* pCheckbox = pMain->GetRibbonButton(ID_CHECK_FORMAT_MP4); m_bIsMp4 = !pCheckbox->IsChecked(); } void CVideoCaptureView::OnCheckFormatAvi() { CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd(); CMFCRibbonButton* pCheckbox = pMain->GetRibbonButton(ID_CHECK_FORMAT_AVI); m_bIsMp4 = pCheckbox->IsChecked(); } void CVideoCaptureView::OnUpdateCheckSuportGpu(CCmdUI* pCmdUI) { // TODO: 在此添加命令更新用户界面处理程序代码 pCmdUI->SetCheck(m_bSupportGPU); } void CVideoCaptureView::OnUpdateCheckFormatMp4(CCmdUI* pCmdUI) { // TODO: 在此添加命令更新用户界面处理程序代码 pCmdUI->SetCheck(m_bIsMp4); } void CVideoCaptureView::OnUpdateCheckFormatAvi(CCmdUI* pCmdUI) { // TODO: 在此添加命令更新用户界面处理程序代码 pCmdUI->SetCheck(!m_bIsMp4); } void CVideoCaptureView::OnUpdateStartRecord(CCmdUI* pCmdUI) { // TODO: 在此添加命令更新用户界面处理程序代码 pCmdUI->Enable(!m_bIsRecord); } void CVideoCaptureView::OnUpdateStopRecord(CCmdUI* pCmdUI) { // TODO: 在此添加命令更新用户界面处理程序代码 pCmdUI->Enable(m_bIsRecord); }