#ifndef ANSRTSP_H #define ANSRTSP_H #define ANSRTSP_API __declspec(dllexport) #include #include "ANSLicense.h" #include "LabVIEWHeader/extcode.h" #include #include "sys_inc.h" #include "rtsp_cln.h" #include "hqueue.h" #include "http.h" #include "http_parse.h" #include "video_decoder.h" #include "audio_decoder.h" #include "rtsp_player.h" #include #include #include #include #include namespace ANSCENTER { class ANSRTSP_API ANSRTSPClient { protected: std::unique_ptr _playerClient = std::make_unique(); std::string _username; std::string _password; std::string _url; bool _useFullURL; int _imageRotateDeg = 0; int _displayWidth = 0; // 0 = no resize (return original resolution) int _displayHeight = 0; cv::Mat _pLastFrame; std::string _lastJpegImage; int _imageWidth,_imageHeight; int64_t _pts; bool _isPlaying; bool _useNV12FastPath = false; // false = original stable CPU path, true = NV12 GPU fast path std::recursive_mutex _mutex; // --- Per-client inference guard --- // Tracks how many GPU frames from this client are currently in-flight // (grabbed by GetRTSPCVImage but not yet released after inference). // Destroy() waits for this to reach 0 before freeing NVDEC surfaces, // preventing the use-after-free crash when LabVIEW stops a camera // while AI inference is still reading CUDA device pointers. std::atomic _inFlightFrames{0}; std::condition_variable_any _inFlightDone; public: void IncrementInFlight() { _inFlightFrames.fetch_add(1, std::memory_order_acq_rel); } void DecrementInFlight() { if (_inFlightFrames.fetch_sub(1, std::memory_order_acq_rel) <= 1) { _inFlightDone.notify_all(); } } // Atomically check _isPlaying AND increment _inFlightFrames under the // same mutex. Returns true if the caller may proceed to access CUDA // resources (GetCudaHWFrame + D2D copy). Returns false if the player // is stopping/reconnecting — caller must NOT touch CUDA resources. // // This closes the race window where Reconnect() sets _isPlaying=false // and calls close() while GetRTSPCVImage is between GetCudaHWFrame() // and the D2D copy in gpu_frame_attach_cuda(). bool TryIncrementInFlight() { std::lock_guard lock(_mutex); if (!_isPlaying) return false; _inFlightFrames.fetch_add(1, std::memory_order_acq_rel); return true; } ANSRTSPClient(); ~ANSRTSPClient() noexcept; [[nodiscard]] bool Init(std::string licenseKey, std::string url); [[nodiscard]] bool Init(std::string licenseKey, std::string username, std::string password, std::string url); [[nodiscard]] bool Setup(); [[nodiscard]] bool Reconnect(); [[nodiscard]] bool Start(); [[nodiscard]] bool IsPaused(); [[nodiscard]] bool IsPlaying(); [[nodiscard]] bool IsRecording(); [[nodiscard]] bool Stop(); [[nodiscard]] bool Pause(); void Destroy(); void SetBBox(cv::Rect bbox); void SetCrop(bool crop); void EnableAudio(bool status); void SetAudioVolume(int volume); [[nodiscard]] cv::Mat GetImage(int& width, int& height, int64_t& pts); [[nodiscard]] std::string MatToBinaryData(const cv::Mat& image); [[nodiscard]] std::string GetJpegImage(int& width, int& height, int64_t& pts); bool areImagesIdentical(const cv::Mat& img1, const cv::Mat& img2); void SetImageRotate(int mode) { std::lock_guard lock(_mutex); _imageRotateDeg = mode; if (mode > 360) _imageRotateDeg = 360; }; static void SetMaxHWDecoders(int maxDecoders); static int AutoConfigureHWDecoders(int maxPerGpuOverride = 0); void SetHWDecoding(int hwMode, int preferredGpu = -1); bool IsHWDecodingActive(); int GetHWDecodingGpuIndex(); void SetDisplayResolution(int width, int height); // Set display output size; 0,0 = original (no resize) void SetImageQuality(int mode); // 0=fast (AI), 1=quality (display) void SetTargetFPS(double intervalMs); // Set min interval between processed frames in ms (0 = no limit, 100 = ~10 FPS, 200 = ~5 FPS) void SetNV12FastPath(bool enable); // true = NV12 GPU fast path (zero-copy inference), false = original CPU path (stable) bool IsNV12FastPath() const { return _useNV12FastPath; } double GetLastFrameAgeMs(); // Milliseconds since last frame from decoder (detects truly stale cameras, unaffected by SetTargetFPS) AVFrame* GetNV12Frame(); // Returns cloned NV12 frame for GPU fast-path (caller must av_frame_free) AVFrame* GetCudaHWFrame(); // Returns CUDA HW frame (device ptrs) for zero-copy inference bool IsCudaHWAccel(); // true when decoder uses CUDA (NV12 stays in GPU VRAM) public: void CheckLicense(); SPDLogger& _logger = SPDLogger::GetInstance("ANSRTSP", false); bool _licenseValid{ false }; std::string _licenseKey; //std::once_flag licenseOnceFlag; }; } extern "C" __declspec(dllexport) int CreateANSRTSPHandle(ANSCENTER::ANSRTSPClient * *Handle, const char* licenseKey, const char* username, const char* password, const char* url); extern "C" __declspec(dllexport) int ReleaseANSRTSPHandle(ANSCENTER::ANSRTSPClient * *Handle); extern "C" __declspec(dllexport) int GetRTSPImage(ANSCENTER::ANSRTSPClient **Handle, int &width, int &height, int64_t& timeStamp, LStrHandle jpegImage); extern "C" __declspec(dllexport) int GetRTSPStrImage(ANSCENTER::ANSRTSPClient * *Handle, int& width, int& height, int64_t & timeStamp, std::string& jpegImage); extern "C" __declspec(dllexport) int GetRTSPCVImage(ANSCENTER::ANSRTSPClient** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image); extern "C" __declspec(dllexport) int StartRTSP(ANSCENTER::ANSRTSPClient * *Handle); extern "C" __declspec(dllexport) int ReconnectRTSP(ANSCENTER::ANSRTSPClient * *Handle); extern "C" __declspec(dllexport) int StopRTSP(ANSCENTER::ANSRTSPClient * *Handle); extern "C" __declspec(dllexport) int PauseRTSP(ANSCENTER::ANSRTSPClient** Handle); extern "C" __declspec(dllexport) int IsRTSPPaused(ANSCENTER::ANSRTSPClient * *Handle); extern "C" __declspec(dllexport) int IsRTSPRunning(ANSCENTER::ANSRTSPClient * *Handle); extern "C" __declspec(dllexport) int IsRTSPRecording(ANSCENTER::ANSRTSPClient * *Handle); extern "C" __declspec(dllexport) void SetRTSPAudioVolume(ANSCENTER::ANSRTSPClient * *Handle, int volume); extern "C" __declspec(dllexport) void EnableRTSPAudioVolume(ANSCENTER::ANSRTSPClient * *Handle, int status); extern "C" __declspec(dllexport) void SetRTSPImageRotation(ANSCENTER::ANSRTSPClient * *Handle, double rotationAngle); extern "C" __declspec(dllexport) int SetBBoxRTSP(ANSCENTER::ANSRTSPClient** Handle, int x, int y, int width, int height); extern "C" __declspec(dllexport) int SetCropFlagRTSP(ANSCENTER::ANSRTSPClient** Handle, int cropFlag); extern "C" __declspec(dllexport) void SetMaxHWDecoders(int maxDecoders); extern "C" __declspec(dllexport) int AutoConfigureHWDecoders(int maxPerGpuOverride); extern "C" __declspec(dllexport) void SetRTSPHWDecoding(ANSCENTER::ANSRTSPClient** Handle, int hwMode, int preferredGpu = -1); extern "C" __declspec(dllexport) int IsRTSPHWDecodingActive(ANSCENTER::ANSRTSPClient** Handle); extern "C" __declspec(dllexport) int GetRTSPHWDecodingGpuIndex(ANSCENTER::ANSRTSPClient** Handle); extern "C" __declspec(dllexport) void SetRTSPImageQuality(ANSCENTER::ANSRTSPClient** Handle, int mode); extern "C" __declspec(dllexport) void SetRTSPDisplayResolution(ANSCENTER::ANSRTSPClient** Handle, int width, int height); extern "C" __declspec(dllexport) void SetRTSPTargetFPS(ANSCENTER::ANSRTSPClient** Handle, double intervalMs); extern "C" __declspec(dllexport) void SetRTSPNV12FastPath(ANSCENTER::ANSRTSPClient** Handle, int enable); extern "C" __declspec(dllexport) double GetRTSPLastFrameAgeMs(ANSCENTER::ANSRTSPClient** Handle); #endif