#include "ANSMJPEG.h" #include "ANSMatRegistry.h" #include "ANSGpuFrameOps.h" #include "ANSCVVendorGate.h" // anscv_vendor_gate::IsNvidiaGpuAvailable() #include #include #include "media_codec.h" #include #if defined(_WIN32) #include #pragma comment(lib, "dxgi.lib") #elif defined(__linux__) #include #include #include #endif extern "C" { #include #include #include } // Note: per-instance thread safety is handled by ANSMJPEGClient::_mutex // Mat registry thread safety is handled by anscv_mat_replace's internal registry_mutex static bool ansmjpegLicenceValid = false; // Global once_flag to protect license checking static std::once_flag ansmjpegLicenseOnceFlag; static std::once_flag mjpegHwDecoderAutoConfigOnceFlag; namespace ANSCENTER { ANSMJPEGClient::ANSMJPEGClient() { _useFullURL = false; _username = ""; _password = ""; _url = ""; _imageWidth = 0; _imageHeight = 0; _pts = 0; _lastJpegImage = ""; _isPlaying = false; // Auto-configure HW decoder pool on first client creation. std::call_once(mjpegHwDecoderAutoConfigOnceFlag, []() { AutoConfigureHWDecoders(0); }); } ANSMJPEGClient::~ANSMJPEGClient() noexcept { Destroy(); } void ANSMJPEGClient::Destroy() { decltype(_playerClient) clientToClose; { std::lock_guard lock(_mutex); if (_playerClient) { if (_isPlaying) { _playerClient->stop(); _isPlaying = false; } } clientToClose = std::move(_playerClient); } if (clientToClose) { clientToClose->close(); } } static void VerifyGlobalANSMJPEGLicense(const std::string& licenseKey) { try { ansmjpegLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1007, "ANSCV");//Default productId=1005 if (!ansmjpegLicenceValid) { // we also support ANSTS license ansmjpegLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1003, "ANSVIS");//Default productId=1003 (ANSVIS) } if (!ansmjpegLicenceValid) { // we also support ANSTS license ansmjpegLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1008, "ANSTS");//Default productId=1008 (ANSTS) } } catch (std::exception& e) { ansmjpegLicenceValid = false; } } void ANSMJPEGClient::CheckLicense() { std::lock_guard lock(_mutex); try { // Check once globally std::call_once(ansmjpegLicenseOnceFlag, [this]() { VerifyGlobalANSMJPEGLicense(_licenseKey); }); // Update this instance's local license flag _licenseValid = ansmjpegLicenceValid; } catch (const std::exception& e) { this->_logger.LogFatal("ANSMJPEGClient::CheckLicense. Error:", e.what(), __FILE__, __LINE__); } } bool ANSMJPEGClient::Init(std::string licenseKey, std::string url) { std::lock_guard lock(_mutex); _licenseKey = licenseKey; CheckLicense(); if (!_licenseValid) { this->_logger.LogError("ANSMJPEGClient::Init.", "Invalid license", __FILE__, __LINE__); return false; } _url = url; _useFullURL = true; return Setup(); } bool ANSMJPEGClient::Init(std::string licenseKey, std::string username, std::string password, std::string url) { std::lock_guard lock(_mutex); _licenseKey = licenseKey; CheckLicense(); if (!_licenseValid) { this->_logger.LogError("ANSMJPEGClient::Init.", "Invalid license", __FILE__, __LINE__); return false; } _url = url; _username = username; _password = password; _useFullURL = false; return Setup(); } void ANSMJPEGClient::SetBBox(cv::Rect bbox) { std::lock_guard lock(_mutex); _playerClient->setBbox(bbox); } void ANSMJPEGClient::SetCrop(bool crop) { std::lock_guard lock(_mutex); _playerClient->setCrop(crop); } bool ANSMJPEGClient::Setup() { std::lock_guard lock(_mutex); if (_useFullURL) { return _playerClient->open(_url); } else { return _playerClient->open(_username, _password, _url); } } bool ANSMJPEGClient::Reconnect() { { std::lock_guard lock(_mutex); _isPlaying = false; } _playerClient->close(); std::lock_guard lock(_mutex); Setup(); _isPlaying = _playerClient->play(); return _isPlaying; } bool ANSMJPEGClient::Start() { std::lock_guard lock(_mutex); Setup(); _isPlaying = _playerClient->play(); return _isPlaying; } bool ANSMJPEGClient::Stop() { decltype(_playerClient.get()) player = nullptr; { std::lock_guard lock(_mutex); if (_isPlaying) { _isPlaying = false; player = _playerClient.get(); } } if (player) { player->stop(); } return true; } bool ANSMJPEGClient::Pause() { std::lock_guard lock(_mutex); _isPlaying = false; return _playerClient->pause(); } bool ANSMJPEGClient::IsPaused() { std::lock_guard lock(_mutex); return _playerClient->isPaused(); } bool ANSMJPEGClient::IsPlaying() { std::lock_guard lock(_mutex); return _playerClient->isPlaying(); } bool ANSMJPEGClient::IsRecording() { std::lock_guard lock(_mutex); return _playerClient->isRecording(); } std::string ANSMJPEGClient::GetJpegImage(int& width, int& height, int64_t& pts) { std::lock_guard lock(_mutex); // If the player is playing, process the frame if (_isPlaying) { // Get a new frame from the player client _lastJpegImage = _playerClient->getJpegImage(width, height, pts); // Update internal state variables _pts = pts; _imageWidth = width; _imageHeight = height; // Return the frame return _lastJpegImage; } // If the player is not playing, return the last known frame else { width = _imageWidth; height = _imageHeight; pts = _pts; return _lastJpegImage; } } bool ANSMJPEGClient::areImagesIdentical(const cv::Mat& img1, const cv::Mat& img2) { double ageMs = _playerClient->getLastFrameAgeMs(); if (ageMs > 5000.0) return true; if (ageMs > 0.0) return false; if (img1.empty() && img2.empty()) return true; if (img1.empty() || img2.empty()) return false; if (img1.size() != img2.size() || img1.type() != img2.type()) return false; if (img1.data == img2.data) return true; if (img1.isContinuous() && img2.isContinuous()) { const size_t totalBytes = img1.total() * img1.elemSize(); return std::memcmp(img1.data, img2.data, totalBytes) == 0; } const size_t rowSize = img1.cols * img1.elemSize(); for (int i = 0; i < img1.rows; i++) { if (std::memcmp(img1.ptr(i), img2.ptr(i), rowSize) != 0) return false; } return true; } cv::Mat ANSMJPEGClient::GetImage(int& width, int& height, int64_t& pts) { std::lock_guard lock(_mutex); // Return last known frame if not playing if (!_isPlaying) { width = _imageWidth; height = _imageHeight; pts = _pts; return _pLastFrame; // Shallow copy (fast) } int imageW = 0, imageH = 0; int64_t currentPts = 0; // Get image directly without intermediate assignment cv::Mat currentImage = _playerClient->getImage(imageW, imageH, currentPts); // Check if image is valid first (cheaper than comparison) if (currentImage.empty()) { width = _imageWidth; height = _imageHeight; pts = _pts; return _pLastFrame; } // Use PTS to detect duplicate frames (much faster than pixel comparison) if (currentPts == _pts && !_pLastFrame.empty()) { width = _imageWidth; height = _imageHeight; pts = _pts; return _pLastFrame; } // Update PTS first _pts = currentPts; pts = currentPts; // Handle non-rotated case if (_imageRotateDeg == 0) { // Apply display resize if configured if (_displayWidth > 0 && _displayHeight > 0 && (currentImage.cols != _displayWidth || currentImage.rows != _displayHeight)) { cv::Mat displayResult; cv::resize(currentImage, displayResult, cv::Size(_displayWidth, _displayHeight), 0, 0, cv::INTER_LINEAR); currentImage = displayResult; imageW = _displayWidth; imageH = _displayHeight; } _imageWidth = imageW; _imageHeight = imageH; width = imageW; height = imageH; _pLastFrame = currentImage; return currentImage; } // Handle rotation case int rotatedWidth, rotatedHeight; double absAngle = std::abs(_imageRotateDeg); if (absAngle == 90.0 || absAngle == 270.0) { rotatedWidth = imageH; rotatedHeight = imageW; } else { rotatedWidth = imageW; rotatedHeight = imageH; } cv::Point2f center(imageW / 2.0f, imageH / 2.0f); cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, _imageRotateDeg, 1.0); cv::Mat rotatedImage; cv::warpAffine(currentImage, rotatedImage, rotationMatrix, cv::Size(rotatedWidth, rotatedHeight), cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); // Apply display resize if configured if (_displayWidth > 0 && _displayHeight > 0 && (rotatedImage.cols != _displayWidth || rotatedImage.rows != _displayHeight)) { cv::Mat displayResult; cv::resize(rotatedImage, displayResult, cv::Size(_displayWidth, _displayHeight), 0, 0, cv::INTER_LINEAR); rotatedImage = displayResult; rotatedWidth = _displayWidth; rotatedHeight = _displayHeight; } // Update dimensions - use calculated dimensions, not cols/rows _imageWidth = rotatedWidth; _imageHeight = rotatedHeight; width = rotatedWidth; height = rotatedHeight; // Store and return rotated image _pLastFrame = rotatedImage; return rotatedImage; } void ANSMJPEGClient::EnableAudio(bool status) { std::lock_guard lock(_mutex); _playerClient->enableAudio(status); } void ANSMJPEGClient::SetAudioVolume(int volume) { std::lock_guard lock(_mutex); _playerClient->setVolume(volume); } void ANSMJPEGClient::SetMaxHWDecoders(int maxDecoders) { if (maxDecoders >= 0) { g_hw_decoder_max = static_cast(maxDecoders); } } // NVDEC hardware cap: fixed-function ASIC with ~32 concurrent session limit. static constexpr int NVDEC_HW_SESSION_CAP = 32; static int estimateMaxSessions(int smCount, size_t totalVramBytes) { int smBased; if (smCount >= 80) smBased = 64; else if (smCount >= 50) smBased = 48; else if (smCount >= 30) smBased = 32; else if (smCount >= 20) smBased = 16; else smBased = 8; size_t totalVramMB = totalVramBytes / (1024 * 1024); size_t reservedMB = 1024; size_t availableMB = (totalVramMB > reservedMB) ? (totalVramMB - reservedMB) : 256; int vramBased = static_cast(availableMB / 80); if (vramBased < 4) vramBased = 4; int result = (smBased < vramBased) ? smBased : vramBased; if (result > NVDEC_HW_SESSION_CAP) result = NVDEC_HW_SESSION_CAP; return result; } static int estimateSessionsByVendor(uint32_t vendorId, size_t dedicatedMB, size_t sharedMB) { if (vendorId == 0x10DE) { size_t availMB = (dedicatedMB > 1024) ? (dedicatedMB - 1024) : 512; int sessions = static_cast(availMB / 80); if (sessions < 8) sessions = 8; if (sessions > 64) sessions = 64; return sessions; } else if (vendorId == 0x8086) { size_t effectiveMB = (dedicatedMB > 0) ? dedicatedMB : (sharedMB / 4); if (effectiveMB >= 2048) return 16; else if (effectiveMB >= 512) return 12; else return 8; } else if (vendorId == 0x1002) { if (dedicatedMB >= 4096) return 32; else if (dedicatedMB >= 1024) return 16; else return 8; } return 4; } static const char* vendorIdToName(uint32_t vendorId) { if (vendorId == 0x10DE) return "NVIDIA"; if (vendorId == 0x8086) return "Intel"; if (vendorId == 0x1002) return "AMD"; return "Unknown"; } #if defined(_WIN32) static int AutoConfigureHWDecoders_Platform_MJPEG() { IDXGIFactory1* pFactory = nullptr; HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pFactory); if (FAILED(hr) || !pFactory) { g_hw_decoder_max = 4; fprintf(stderr, "[HWDecode] MJPEG AutoConfigure: DXGI unavailable, defaulting to %d sessions\n", g_hw_decoder_max); return g_hw_decoder_max; } IDXGIAdapter1* pAdapter = nullptr; int totalSessions = 0; bool foundHwGpu = false; for (UINT i = 0; pFactory->EnumAdapters1(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; i++) { DXGI_ADAPTER_DESC1 desc; pAdapter->GetDesc1(&desc); pAdapter->Release(); if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) continue; char gpuName[128] = {}; wcstombs(gpuName, desc.Description, sizeof(gpuName) - 1); uint32_t vendorId = desc.VendorId; size_t dedicatedMB = desc.DedicatedVideoMemory / (1024 * 1024); size_t sharedMB = desc.SharedSystemMemory / (1024 * 1024); int maxSessions = estimateSessionsByVendor(vendorId, dedicatedMB, sharedMB); fprintf(stderr, "[HWDecode] MJPEG AutoConfigure: GPU[%d] \"%s\" Vendor=%s(0x%04X) VRAM=%zuMB Shared=%zuMB -> max %d decode sessions\n", i, gpuName, vendorIdToName(vendorId), vendorId, dedicatedMB, sharedMB, maxSessions); totalSessions += maxSessions; foundHwGpu = true; } pFactory->Release(); if (!foundHwGpu) { g_hw_decoder_max = 4; fprintf(stderr, "[HWDecode] MJPEG AutoConfigure: No hardware GPU found, defaulting to %d sessions (software fallback)\n", g_hw_decoder_max); return g_hw_decoder_max; } g_hw_decoder_max = static_cast(totalSessions); fprintf(stderr, "[HWDecode] MJPEG AutoConfigure: Total %d decode sessions across all GPUs\n", totalSessions); return totalSessions; } #elif defined(__linux__) static int AutoConfigureHWDecoders_Platform_MJPEG() { int totalSessions = 0; bool foundHwGpu = false; int gpuIndex = 0; for (int cardNum = 0; cardNum < 16; cardNum++) { char vendorPath[256]; snprintf(vendorPath, sizeof(vendorPath), "/sys/class/drm/card%d/device/vendor", cardNum); std::ifstream vendorFile(vendorPath); if (!vendorFile.is_open()) continue; uint32_t vendorId = 0; vendorFile >> std::hex >> vendorId; vendorFile.close(); if (vendorId == 0) continue; char gpuName[128] = "Unknown GPU"; char ueventPath[256]; snprintf(ueventPath, sizeof(ueventPath), "/sys/class/drm/card%d/device/uevent", cardNum); std::ifstream ueventFile(ueventPath); if (ueventFile.is_open()) { std::string line; while (std::getline(ueventFile, line)) { if (line.find("PCI_SLOT_NAME=") == 0) { snprintf(gpuName, sizeof(gpuName), "PCI %s", line.substr(14).c_str()); break; } } ueventFile.close(); } size_t dedicatedMB = 0; size_t sharedMB = 0; char resourcePath[256]; snprintf(resourcePath, sizeof(resourcePath), "/sys/class/drm/card%d/device/resource", cardNum); std::ifstream resourceFile(resourcePath); if (resourceFile.is_open()) { std::string line; if (std::getline(resourceFile, line)) { unsigned long long start = 0, end = 0; if (sscanf(line.c_str(), "%llx %llx", &start, &end) == 2 && end > start) { dedicatedMB = (end - start + 1) / (1024 * 1024); } } resourceFile.close(); } if (vendorId == 0x8086 && dedicatedMB < 512) { std::ifstream meminfo("/proc/meminfo"); if (meminfo.is_open()) { std::string line; while (std::getline(meminfo, line)) { if (line.find("MemTotal:") == 0) { unsigned long totalKB = 0; sscanf(line.c_str(), "MemTotal: %lu", &totalKB); sharedMB = totalKB / 1024; break; } } meminfo.close(); } } int maxSessions = estimateSessionsByVendor(vendorId, dedicatedMB, sharedMB); fprintf(stderr, "[HWDecode] MJPEG AutoConfigure: GPU[%d] \"%s\" Vendor=%s(0x%04X) VRAM=%zuMB Shared=%zuMB -> max %d VAAPI decode sessions\n", gpuIndex, gpuName, vendorIdToName(vendorId), vendorId, dedicatedMB, sharedMB, maxSessions); totalSessions += maxSessions; foundHwGpu = true; gpuIndex++; } if (!foundHwGpu) { g_hw_decoder_max = 4; fprintf(stderr, "[HWDecode] MJPEG AutoConfigure: No hardware GPU found in /sys/class/drm, defaulting to %d sessions (software fallback)\n", g_hw_decoder_max); return g_hw_decoder_max; } g_hw_decoder_max = static_cast(totalSessions); fprintf(stderr, "[HWDecode] MJPEG AutoConfigure: Total %d VAAPI decode sessions across all GPUs\n", totalSessions); return totalSessions; } #else static int AutoConfigureHWDecoders_Platform_MJPEG() { g_hw_decoder_max = 4; fprintf(stderr, "[HWDecode] MJPEG AutoConfigure: Unsupported platform, defaulting to %d sessions\n", g_hw_decoder_max); return g_hw_decoder_max; } #endif int ANSMJPEGClient::AutoConfigureHWDecoders(int maxPerGpuOverride) { // Skip the CUDA probe on non-NVIDIA hardware — the Platform fallback // handles Intel/AMD auto configuration. See ANSCVVendorGate.h. if (!anscv_vendor_gate::IsNvidiaGpuAvailable()) { return AutoConfigureHWDecoders_Platform_MJPEG(); } int gpuCount = 0; cudaError_t err = cudaGetDeviceCount(&gpuCount); if (err != cudaSuccess || gpuCount <= 0) { return AutoConfigureHWDecoders_Platform_MJPEG(); } std::vector maxPerGpuList(gpuCount); int total = 0; for (int i = 0; i < gpuCount; i++) { cudaDeviceProp prop; cudaGetDeviceProperties(&prop, i); int smCount = prop.multiProcessorCount; if (maxPerGpuOverride > 0) { maxPerGpuList[i] = maxPerGpuOverride; } else { maxPerGpuList[i] = estimateMaxSessions(smCount, prop.totalGlobalMem); } total += maxPerGpuList[i]; size_t vramMB = prop.totalGlobalMem / (1024 * 1024); fprintf(stderr, "[HWDecode] MJPEG AutoConfigure: GPU[%d] \"%s\" SM=%d VRAM=%zuMB -> max %d decode sessions\n", i, prop.name, smCount, vramMB, maxPerGpuList[i]); } HWDecoderPool::instance().configure(maxPerGpuList); return total; } void ANSMJPEGClient::SetHWDecoding(int hwMode, int preferredGpu) { std::lock_guard lock(_mutex); _playerClient->setHWDecoding(hwMode, preferredGpu); } bool ANSMJPEGClient::IsHWDecodingActive() { std::lock_guard lock(_mutex); return _playerClient->isHWDecodingActive(); } int ANSMJPEGClient::GetHWDecodingGpuIndex() { std::lock_guard lock(_mutex); return _playerClient->getHWDecodingGpuIndex(); } void ANSMJPEGClient::SetDisplayResolution(int width, int height) { std::lock_guard lock(_mutex); _displayWidth = width; _displayHeight = height; } void ANSMJPEGClient::SetImageQuality(int mode) { std::lock_guard lock(_mutex); _playerClient->setImageQuality(mode); // 0=fast (AI), 1=quality (display) } void ANSMJPEGClient::SetTargetFPS(double intervalMs) { std::lock_guard lock(_mutex); _playerClient->setTargetFPS(intervalMs); // 0=no limit, 100=~10FPS, 200=~5FPS } void ANSMJPEGClient::SetNV12FastPath(bool enable) { std::lock_guard lock(_mutex); _useNV12FastPath = enable; } AVFrame* ANSMJPEGClient::GetNV12Frame() { std::lock_guard lock(_mutex); return _playerClient->getNV12Frame(); // Returns clone, caller must av_frame_free } AVFrame* ANSMJPEGClient::GetCudaHWFrame() { std::lock_guard lock(_mutex); return _playerClient->getCudaHWFrame(); } bool ANSMJPEGClient::IsCudaHWAccel() { std::lock_guard lock(_mutex); return _playerClient->isCudaHWAccel(); } std::string ANSMJPEGClient::MatToBinaryData(const cv::Mat& image) { std::lock_guard lock(_mutex); if (!image.empty()) { if ((image.data != nullptr) && (image.u != nullptr)) { try { // Encode the image to a memory buffer std::vector imageData; bool success = cv::imencode(".jpg", image, imageData); if (!success) { this->_logger.LogError("ANSMJPEGClient::MatToBinaryData. Error:", "Failed to encode the image.", __FILE__, __LINE__); return ""; } std::string binaryData(imageData.begin(), imageData.end()); return binaryData; } catch (const std::exception& e) { this->_logger.LogFatal("ANSMJPEGClient::MatToBinaryData. Error:", e.what(), __FILE__, __LINE__); return ""; } catch (...) { this->_logger.LogFatal("ANSMJPEGClient::MatToBinaryData. Error:", "Caught unknown exception!", __FILE__, __LINE__); return ""; } } else return ""; } else return ""; } } extern "C" __declspec(dllexport) int CreateANSMJPEGHandle(ANSCENTER::ANSMJPEGClient * *Handle, const char* licenseKey, const char* username, const char* password, const char* url) { if (!Handle || !licenseKey || !url) return -1; try { auto ptr = std::make_unique(); std::string _username = username ? username : ""; std::string _password = password ? password : ""; bool result = false; if (_username.empty() && _password.empty()) result = ptr->Init(licenseKey, url); else result = ptr->Init(licenseKey, username, password, url); if (result) { *Handle = ptr.release(); extern void anscv_unregister_handle(void*); extern void anscv_register_handle(void*, void(*)(void*)); anscv_register_handle(*Handle, [](void* p) { auto* h = static_cast(p); try { h->Stop(); } catch (...) {} try { h->Destroy(); } catch (...) {} try { delete h; } catch (...) {} }); return 1; } *Handle = nullptr; return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int ReleaseANSMJPEGHandle(ANSCENTER::ANSMJPEGClient * *Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { extern void anscv_unregister_handle(void*); anscv_unregister_handle(*Handle); // unique_ptr destructor calls ~ANSMJPEGClient which calls Destroy() std::unique_ptr ptr(*Handle); *Handle = nullptr; return 0; } catch (...) { if (Handle) *Handle = nullptr; return -1; } } extern "C" __declspec(dllexport) int GetMJPEGStrImage(ANSCENTER::ANSMJPEGClient * *Handle, int& width, int& height, int64_t & timeStamp, std::string & jpegImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { jpegImage = (*Handle)->GetJpegImage(width, height, timeStamp); if (!jpegImage.empty()) return 1; else return 0; } catch (const std::exception& e) { std::cerr << "Error getting MJPEG image: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Error getting MJPEG image: Unknown exception." << std::endl; return 0; } } extern "C" __declspec(dllexport) int GetMJPEGImage(ANSCENTER::ANSMJPEGClient * *Handle, int& width, int& height, int64_t & timeStamp, LStrHandle jpegImage) { if (Handle == nullptr || *Handle == nullptr) return -1; try { std::string jpegString = (*Handle)->GetJpegImage(width, height, timeStamp); int size = jpegString.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(jpegImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*jpegImage)->cnt = size; memcpy((*jpegImage)->str, jpegString.c_str(), size); return 1; } else { std::cerr << "Error resizing jpegImage handle: " << error << std::endl; return 0; } } else { std::cerr << "No image data retrieved from MJPEG client." << std::endl; return 0; } } catch (const std::exception& e) { std::cerr << "Error getting MJPEG image: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Error getting MJPEG image: Unknown exception." << std::endl; return 0; } } extern "C" __declspec(dllexport) int GetMJPEGCVImage(ANSCENTER::ANSMJPEGClient** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image) { if (!Handle || !*Handle || !image) { std::cerr << "Error: Invalid input parameters in GetMJPEGCVImage" << std::endl; return -1; } try { cv::Mat img = (*Handle)->GetImage(width, height, timeStamp); if (img.empty()) { return 0; } // Thread-safe Mat pointer swap (anscv_mat_replace has its own internal lock) anscv_mat_replace(image, std::move(img)); // NV12 GPU fast path (optional — disabled by default for stability) if ((*Handle)->IsNV12FastPath()) { int gpuIdx = (*Handle)->GetHWDecodingGpuIndex(); AVFrame* cudaHW = (*Handle)->GetCudaHWFrame(); if (cudaHW) { AVFrame* cpuNV12 = (*Handle)->GetNV12Frame(); gpu_frame_attach_cuda(*image, cudaHW, gpuIdx, timeStamp, cpuNV12); } else { AVFrame* nv12 = (*Handle)->GetNV12Frame(); if (nv12) { gpu_frame_attach(*image, nv12, gpuIdx, timeStamp); } } } return 1; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in GetMJPEGCVImage: " << e.what() << std::endl; return -2; } catch (const std::exception& e) { std::cerr << "Exception in GetMJPEGCVImage: " << e.what() << std::endl; return -2; } catch (...) { std::cerr << "Unknown exception in GetMJPEGCVImage" << std::endl; return -2; } } extern "C" __declspec(dllexport) int StartMJPEG(ANSCENTER::ANSMJPEGClient * *Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { bool result = (*Handle)->Start(); if (result) return 1; else return 0; } catch (const std::exception& e) { std::cerr << "Error starting MJPEG client: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Error starting MJPEG client: Unknown exception." << std::endl; return 0; } } extern "C" __declspec(dllexport) int ReconnectMJPEG(ANSCENTER::ANSMJPEGClient * *Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { bool result = (*Handle)->Reconnect(); if (result) return 1; else return 0; } catch (const std::exception& e) { std::cerr << "Error reconnecting MJPEG client: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Error reconnecting MJPEG client: Unknown exception." << std::endl; return 0; } } extern "C" __declspec(dllexport) int StopMJPEG(ANSCENTER::ANSMJPEGClient * *Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { bool result = (*Handle)->Stop(); if (result) return 1; else return 0; } catch (const std::exception& e) { std::cerr << "Error stopping MJPEG client: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Error stopping MJPEG client: Unknown exception." << std::endl; return 0; } } extern "C" __declspec(dllexport) int PauseMJPEG(ANSCENTER::ANSMJPEGClient** Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { bool result = (*Handle)->Pause(); if (result) return 1; else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int IsMJPEGPaused(ANSCENTER::ANSMJPEGClient * *Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { bool result = (*Handle)->IsPaused(); if (result) return 1; else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int IsMJPEGRunning(ANSCENTER::ANSMJPEGClient * *Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { bool result = (*Handle)->IsPlaying(); if (result) return 1; else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int IsMJPEGRecording(ANSCENTER::ANSMJPEGClient * *Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { bool result = (*Handle)->IsRecording(); if (result) return 1; else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) void SetMJPEGAudioVolume(ANSCENTER::ANSMJPEGClient * *Handle, int volume) { if (Handle == nullptr || *Handle == nullptr) return; try { (*Handle)->SetAudioVolume(volume); } catch (...) { } } extern "C" __declspec(dllexport) void EnableMJPEGAudioVolume(ANSCENTER::ANSMJPEGClient * *Handle, int status) { if (Handle == nullptr || *Handle == nullptr) return; try { bool audioStatus = false; if (status == 1)audioStatus = true; (*Handle)->EnableAudio(audioStatus); } catch (...) { } } extern "C" __declspec(dllexport) void SetMJPEGImageRotation(ANSCENTER::ANSMJPEGClient * *Handle, double rotationAngle) { if (Handle == nullptr || *Handle == nullptr) return; try { (*Handle)->SetImageRotate(rotationAngle); } catch (...) { } } extern "C" __declspec(dllexport) int SetBBoxMJPEG(ANSCENTER::ANSMJPEGClient** Handle, int x, int y, int width, int height) { if (Handle == nullptr || *Handle == nullptr) return -1; try { cv::Rect bbox(x, y, width, height); (*Handle)->SetBBox(bbox); return 1; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int SetCropFlagMJPEG(ANSCENTER::ANSMJPEGClient** Handle, int cropFlag) { if (Handle == nullptr || *Handle == nullptr) return -1; try { bool crop = false; if (cropFlag == 1) crop = true; (*Handle)->SetCrop(crop); return 1; } catch (...) { return -1; } } extern "C" __declspec(dllexport) void SetMJPEGMaxHWDecoders(int maxDecoders) { ANSCENTER::ANSMJPEGClient::SetMaxHWDecoders(maxDecoders); } extern "C" __declspec(dllexport) int AutoConfigureMJPEGHWDecoders(int maxPerGpuOverride) { return ANSCENTER::ANSMJPEGClient::AutoConfigureHWDecoders(maxPerGpuOverride); } extern "C" __declspec(dllexport) void SetMJPEGHWDecoding(ANSCENTER::ANSMJPEGClient** Handle, int hwMode, int preferredGpu) { if (Handle == nullptr || *Handle == nullptr) return; try { (*Handle)->SetHWDecoding(hwMode, preferredGpu); } catch (...) { } } extern "C" __declspec(dllexport) int IsMJPEGHWDecodingActive(ANSCENTER::ANSMJPEGClient** Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { return (*Handle)->IsHWDecodingActive() ? 1 : 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int GetMJPEGHWDecodingGpuIndex(ANSCENTER::ANSMJPEGClient** Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; try { return (*Handle)->GetHWDecodingGpuIndex(); } catch (...) { return -1; } } extern "C" __declspec(dllexport) void SetMJPEGImageQuality(ANSCENTER::ANSMJPEGClient** Handle, int mode) { if (Handle == nullptr || *Handle == nullptr) return; try { (*Handle)->SetImageQuality(mode); } catch (...) { } } extern "C" __declspec(dllexport) void SetMJPEGDisplayResolution(ANSCENTER::ANSMJPEGClient** Handle, int width, int height) { if (Handle == nullptr || *Handle == nullptr) return; try { (*Handle)->SetDisplayResolution(width, height); } catch (...) { } } extern "C" __declspec(dllexport) void SetMJPEGTargetFPS(ANSCENTER::ANSMJPEGClient** Handle, double intervalMs) { if (Handle == nullptr || *Handle == nullptr) return; try { (*Handle)->SetTargetFPS(intervalMs); } catch (...) { } } extern "C" __declspec(dllexport) void SetMJPEGNV12FastPath(ANSCENTER::ANSMJPEGClient** Handle, int enable) { if (Handle == nullptr || *Handle == nullptr) return; try { (*Handle)->SetNV12FastPath(enable != 0); } catch (...) { } } // ============================================================================ // V2 entry points — accept handle as uint64_t by value (LabVIEW safe) // ============================================================================ extern "C" __declspec(dllexport) int GetMJPEGImage_V2(uint64_t handleVal, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { std::string jpegString = h->GetJpegImage(width, height, timeStamp); int size = jpegString.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(jpegImage, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*jpegImage)->cnt = size; memcpy((*jpegImage)->str, jpegString.c_str(), size); return 1; } else { std::cerr << "Error resizing jpegImage handle: " << error << std::endl; return 0; } } else { std::cerr << "No image data retrieved from MJPEG client." << std::endl; return 0; } } catch (const std::exception& e) { std::cerr << "Error getting MJPEG image: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Error getting MJPEG image: Unknown exception." << std::endl; return 0; } } extern "C" __declspec(dllexport) int GetMJPEGCVImage_V2(uint64_t handleVal, int& width, int& height, int64_t& timeStamp, cv::Mat** image) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; if (!image) return -1; try { cv::Mat img = h->GetImage(width, height, timeStamp); if (img.empty()) { return 0; } anscv_mat_replace(image, std::move(img)); // Attach NV12 frame for GPU fast-path inference (side-table registry) int gpuIdx = h->GetHWDecodingGpuIndex(); AVFrame* cudaHW = h->GetCudaHWFrame(); if (cudaHW) { AVFrame* cpuNV12 = h->GetNV12Frame(); gpu_frame_attach_cuda(*image, cudaHW, gpuIdx, timeStamp, cpuNV12); } else { AVFrame* nv12 = h->GetNV12Frame(); if (nv12) { gpu_frame_attach(*image, nv12, gpuIdx, timeStamp); } } return 1; } catch (const cv::Exception& e) { std::cerr << "OpenCV exception in GetMJPEGCVImage_V2: " << e.what() << std::endl; return -2; } catch (const std::exception& e) { std::cerr << "Exception in GetMJPEGCVImage_V2: " << e.what() << std::endl; return -2; } catch (...) { std::cerr << "Unknown exception in GetMJPEGCVImage_V2" << std::endl; return -2; } } extern "C" __declspec(dllexport) int StartMJPEG_V2(uint64_t handleVal) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { bool result = h->Start(); if (result) return 1; else return 0; } catch (const std::exception& e) { std::cerr << "Error starting MJPEG client: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Error starting MJPEG client: Unknown exception." << std::endl; return 0; } } extern "C" __declspec(dllexport) int ReconnectMJPEG_V2(uint64_t handleVal) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { bool result = h->Reconnect(); if (result) return 1; else return 0; } catch (const std::exception& e) { std::cerr << "Error reconnecting MJPEG client: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Error reconnecting MJPEG client: Unknown exception." << std::endl; return 0; } } extern "C" __declspec(dllexport) int StopMJPEG_V2(uint64_t handleVal) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { bool result = h->Stop(); if (result) return 1; else return 0; } catch (const std::exception& e) { std::cerr << "Error stopping MJPEG client: " << e.what() << std::endl; return 0; } catch (...) { std::cerr << "Error stopping MJPEG client: Unknown exception." << std::endl; return 0; } } extern "C" __declspec(dllexport) int PauseMJPEG_V2(uint64_t handleVal) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { bool result = h->Pause(); if (result) return 1; else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int IsMJPEGPaused_V2(uint64_t handleVal) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { bool result = h->IsPaused(); if (result) return 1; else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int IsMJPEGRunning_V2(uint64_t handleVal) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { bool result = h->IsPlaying(); if (result) return 1; else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int IsMJPEGRecording_V2(uint64_t handleVal) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { bool result = h->IsRecording(); if (result) return 1; else return 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) void SetMJPEGAudioVolume_V2(uint64_t handleVal, int volume) { auto* h = reinterpret_cast(handleVal); if (!h) return; try { h->SetAudioVolume(volume); } catch (...) { } } extern "C" __declspec(dllexport) void EnableMJPEGAudioVolume_V2(uint64_t handleVal, int status) { auto* h = reinterpret_cast(handleVal); if (!h) return; try { bool audioStatus = false; if (status == 1) audioStatus = true; h->EnableAudio(audioStatus); } catch (...) { } } extern "C" __declspec(dllexport) void SetMJPEGImageRotation_V2(uint64_t handleVal, double rotationAngle) { auto* h = reinterpret_cast(handleVal); if (!h) return; try { h->SetImageRotate(rotationAngle); } catch (...) { } } extern "C" __declspec(dllexport) int SetBBoxMJPEG_V2(uint64_t handleVal, int x, int y, int width, int height) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { cv::Rect bbox(x, y, width, height); h->SetBBox(bbox); return 1; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int SetCropFlagMJPEG_V2(uint64_t handleVal, int cropFlag) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { bool crop = false; if (cropFlag == 1) crop = true; h->SetCrop(crop); return 1; } catch (...) { return -1; } } extern "C" __declspec(dllexport) void SetMJPEGHWDecoding_V2(uint64_t handleVal, int hwMode, int preferredGpu) { auto* h = reinterpret_cast(handleVal); if (!h) return; try { h->SetHWDecoding(hwMode, preferredGpu); } catch (...) { } } extern "C" __declspec(dllexport) int IsMJPEGHWDecodingActive_V2(uint64_t handleVal) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { return h->IsHWDecodingActive() ? 1 : 0; } catch (...) { return -1; } } extern "C" __declspec(dllexport) int GetMJPEGHWDecodingGpuIndex_V2(uint64_t handleVal) { auto* h = reinterpret_cast(handleVal); if (!h) return -1; try { return h->GetHWDecodingGpuIndex(); } catch (...) { return -1; } } extern "C" __declspec(dllexport) void SetMJPEGImageQuality_V2(uint64_t handleVal, int mode) { auto* h = reinterpret_cast(handleVal); if (!h) return; try { h->SetImageQuality(mode); } catch (...) { } }