Refactor project structure
This commit is contained in:
1169
modules/ANSCV/ANSFLV.cpp
Normal file
1169
modules/ANSCV/ANSFLV.cpp
Normal file
File diff suppressed because it is too large
Load Diff
111
modules/ANSCV/ANSFLV.h
Normal file
111
modules/ANSCV/ANSFLV.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef ANSFLV_H
|
||||
#define ANSFLV_H
|
||||
#define ANSFLV_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#include "sys_inc.h"
|
||||
#include "http_flv_cln.h"
|
||||
#include "hqueue.h"
|
||||
#include "http.h"
|
||||
#include "http_parse.h"
|
||||
#include "video_decoder.h"
|
||||
#include "audio_decoder.h"
|
||||
#include "http_flv_player.h"
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
class ANSFLV_API ANSFLVClient
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<CHttpFlvPlayer> _playerClient = std::make_unique<CHttpFlvPlayer>();
|
||||
|
||||
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;
|
||||
std::recursive_mutex _mutex;
|
||||
public:
|
||||
ANSFLVClient();
|
||||
~ANSFLVClient() 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 EnableAudio(bool status);
|
||||
void SetAudioVolume(int volume);
|
||||
bool areImagesIdentical(const cv::Mat& img1, const cv::Mat& img2);
|
||||
[[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);
|
||||
void SetImageRotate(int mode) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_imageRotateDeg = mode;
|
||||
if (mode > 360) _imageRotateDeg = 360;
|
||||
};
|
||||
void SetBBox(cv::Rect bbox);
|
||||
void SetCrop(bool crop);
|
||||
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)
|
||||
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("ANSFLV", false);
|
||||
bool _licenseValid{ false };
|
||||
std::string _licenseKey;
|
||||
//std::once_flag licenseOnceFlag;
|
||||
|
||||
};
|
||||
}
|
||||
extern "C" __declspec(dllexport) int CreateANSFLVHandle(ANSCENTER::ANSFLVClient * *Handle, const char* licenseKey, const char* username, const char* password, const char* url);
|
||||
extern "C" __declspec(dllexport) int ReleaseANSFLVHandle(ANSCENTER::ANSFLVClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int GetFLVImage(ANSCENTER::ANSFLVClient * *Handle, int& width, int& height, int64_t & timeStamp, LStrHandle jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetFLVStrImage(ANSCENTER::ANSFLVClient * *Handle, int& width, int& height, int64_t & timeStamp, std::string & jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetFLVCVImage(ANSCENTER::ANSFLVClient** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image);
|
||||
|
||||
extern "C" __declspec(dllexport) int StartFLV(ANSCENTER::ANSFLVClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int ReconnectFLV(ANSCENTER::ANSFLVClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int StopFLV(ANSCENTER::ANSFLVClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int PauseFLV(ANSCENTER::ANSFLVClient** Handle);
|
||||
extern "C" __declspec(dllexport) int IsFLVPaused(ANSCENTER::ANSFLVClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsFLVRunning(ANSCENTER::ANSFLVClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsFLVRecording(ANSCENTER::ANSFLVClient * *Handle);
|
||||
extern "C" __declspec(dllexport) void SetFLVAudioVolume(ANSCENTER::ANSFLVClient * *Handle, int volume);
|
||||
extern "C" __declspec(dllexport) void EnableFLVAudioVolume(ANSCENTER::ANSFLVClient * *Handle, int status);
|
||||
extern "C" __declspec(dllexport) void SetFLVImageRotation(ANSCENTER::ANSFLVClient * *Handle, double rotationAngle);
|
||||
extern "C" __declspec(dllexport) int SetBBoxFLV(ANSCENTER::ANSFLVClient** Handle, int x, int y, int width, int height);
|
||||
extern "C" __declspec(dllexport) int SetCropFlagFLV(ANSCENTER::ANSFLVClient** Handle, int cropFlag);
|
||||
extern "C" __declspec(dllexport) void SetFLVMaxHWDecoders(int maxDecoders);
|
||||
extern "C" __declspec(dllexport) int AutoConfigureFLVHWDecoders(int maxPerGpuOverride);
|
||||
extern "C" __declspec(dllexport) void SetFLVHWDecoding(ANSCENTER::ANSFLVClient** Handle, int hwMode, int preferredGpu = -1);
|
||||
extern "C" __declspec(dllexport) int IsFLVHWDecodingActive(ANSCENTER::ANSFLVClient** Handle);
|
||||
extern "C" __declspec(dllexport) int GetFLVHWDecodingGpuIndex(ANSCENTER::ANSFLVClient** Handle);
|
||||
extern "C" __declspec(dllexport) void SetFLVImageQuality(ANSCENTER::ANSFLVClient** Handle, int mode);
|
||||
extern "C" __declspec(dllexport) void SetFLVDisplayResolution(ANSCENTER::ANSFLVClient** Handle, int width, int height);
|
||||
#endif
|
||||
861
modules/ANSCV/ANSFilePlayer.cpp
Normal file
861
modules/ANSCV/ANSFilePlayer.cpp
Normal file
@@ -0,0 +1,861 @@
|
||||
#include "ANSFilePlayer.h"
|
||||
#include "ANSMatRegistry.h"
|
||||
#include "ANSGpuFrameOps.h"
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libavutil/frame.h>
|
||||
#include "media_codec.h"
|
||||
static bool ansfileplayerLicenceValid = false;
|
||||
// Global once_flag to protect license checking
|
||||
static std::once_flag ansfileplayerLicenseOnceFlag;
|
||||
namespace ANSCENTER {
|
||||
#define CHECK_CUDA(call) do { \
|
||||
cudaError_t err = call; \
|
||||
if (err != cudaSuccess) { \
|
||||
std::cout<<"CUDA error: " + std::string(cudaGetErrorString(err)); \
|
||||
} \
|
||||
} while (0)
|
||||
#define CHECK_NVJPEG(call) do { \
|
||||
nvjpegStatus_t status = call; \
|
||||
if (status != NVJPEG_STATUS_SUCCESS) { \
|
||||
std::cout <<"nvJPEG error: " + std::to_string(status); \
|
||||
} \
|
||||
} while (0)
|
||||
ANSFILEPLAYER::ANSFILEPLAYER() {
|
||||
_url = "";
|
||||
_imageRotateDeg = 0;
|
||||
_imageWidth = 0;
|
||||
_imageHeight = 0;
|
||||
_pts = 0;
|
||||
_isPlaying = false;
|
||||
_lastJpegImage = "";
|
||||
}
|
||||
ANSFILEPLAYER::~ANSFILEPLAYER() noexcept {
|
||||
try {
|
||||
Destroy();
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
void ANSFILEPLAYER::Destroy() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
_url = "";
|
||||
_imageRotateDeg = 0;
|
||||
_isPlaying = false;
|
||||
_lastJpegImage = "";
|
||||
_pLastFrame.release();
|
||||
if (_playerClient) {
|
||||
_playerClient->close();
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogError("ANSFILEPLAYER::Destroy. Exception:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
catch (...) {
|
||||
_logger.LogError("ANSFILEPLAYER::Destroy.", "Unknown exception", __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
void ANSFILEPLAYER::CheckLicense() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
_licenseValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(_licenseKey, 1007, "ANSCV");//Default productId=1005
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
this->_logger.LogFatal("ANSFILEPLAYER::CheckLicense. Error:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
bool ANSFILEPLAYER::Init(std::string licenseKey, std::string url) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_licenseKey = licenseKey;
|
||||
CheckLicense();
|
||||
if (!_licenseValid) {
|
||||
this->_logger.LogError("ANSFILEPLAYER::Init.", "Invalid license", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
//network_init();
|
||||
//sys_buf_init(200);
|
||||
//rtsp_parse_buf_init(200);
|
||||
//http_msg_buf_init(200);
|
||||
_url = url;
|
||||
return Setup();
|
||||
}
|
||||
void ANSFILEPLAYER::SetBBox(cv::Rect bbox) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_playerClient->setBbox(bbox);
|
||||
}
|
||||
void ANSFILEPLAYER::SetCrop(bool crop) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_playerClient->setCrop(crop);
|
||||
}
|
||||
bool ANSFILEPLAYER::Setup() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
return _playerClient->open(_url);
|
||||
}
|
||||
bool ANSFILEPLAYER::Reconnect() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_playerClient->close();
|
||||
Setup();
|
||||
return Start();
|
||||
}
|
||||
bool ANSFILEPLAYER::Start() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_isPlaying = _playerClient->play();
|
||||
return _isPlaying;
|
||||
}
|
||||
bool ANSFILEPLAYER::Stop() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
if (_playerClient->pause()) {
|
||||
_isPlaying = false;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ANSFILEPLAYER::IsPaused() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
return _playerClient->isPaused();
|
||||
}
|
||||
bool ANSFILEPLAYER::IsPlaying() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
return _playerClient->isPlaying();
|
||||
}
|
||||
bool ANSFILEPLAYER::IsRecording() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
return _playerClient->isRecording();
|
||||
}
|
||||
std::string ANSFILEPLAYER::GetJpegImage(int& width, int& height, int64_t& pts) {
|
||||
std::lock_guard<std::recursive_mutex> 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;
|
||||
}
|
||||
}
|
||||
cv::Mat ANSFILEPLAYER::GetImage(int& width, int& height, int64_t& pts) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
if (!_isPlaying) {
|
||||
width = _imageWidth;
|
||||
height = _imageHeight;
|
||||
pts = _pts;
|
||||
return _pLastFrame;
|
||||
}
|
||||
|
||||
_pLastFrame.release();
|
||||
int imgW = 0, imgH = 0;
|
||||
_pLastFrame = _playerClient->getImage(imgW, imgH, pts);
|
||||
|
||||
if (_pLastFrame.empty()) {
|
||||
width = _imageWidth;
|
||||
height = _imageHeight;
|
||||
pts = _pts;
|
||||
return _pLastFrame;
|
||||
}
|
||||
|
||||
cv::Mat result = _pLastFrame;
|
||||
|
||||
// Apply rotation if needed
|
||||
if (_imageRotateDeg >= 1 && _imageRotateDeg != 0) {
|
||||
cv::Point2f center(imgW / 2.0f, imgH / 2.0f);
|
||||
cv::Mat rotMat = cv::getRotationMatrix2D(center, _imageRotateDeg, 1.0);
|
||||
cv::Mat rotated;
|
||||
cv::warpAffine(result, rotated, rotMat, result.size(),
|
||||
cv::INTER_CUBIC, cv::BORDER_CONSTANT, cv::Scalar());
|
||||
result = rotated;
|
||||
}
|
||||
|
||||
// Store full-res for inference (before display resize)
|
||||
_inferenceImage = result;
|
||||
|
||||
// Resize for display if display resolution is set
|
||||
if (_displayWidth > 0 && _displayHeight > 0 &&
|
||||
(result.cols != _displayWidth || result.rows != _displayHeight)) {
|
||||
cv::Mat displayResult;
|
||||
cv::resize(result, displayResult, cv::Size(_displayWidth, _displayHeight),
|
||||
0, 0, cv::INTER_LINEAR);
|
||||
result = displayResult;
|
||||
}
|
||||
|
||||
_pts = pts;
|
||||
width = result.cols;
|
||||
height = result.rows;
|
||||
_imageWidth = width;
|
||||
_imageHeight = height;
|
||||
return result;
|
||||
}
|
||||
void ANSFILEPLAYER::SetDisplayResolution(int width, int height) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_displayWidth = width;
|
||||
_displayHeight = height;
|
||||
}
|
||||
cv::Mat ANSFILEPLAYER::GetInferenceImage() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
return _inferenceImage;
|
||||
}
|
||||
void ANSFILEPLAYER::EnableAudio(bool status) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_playerClient->enableAudio(status);
|
||||
}
|
||||
void ANSFILEPLAYER::SetAudioVolume(int volume) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_playerClient->setVolume(volume);
|
||||
}
|
||||
std::string ANSFILEPLAYER::MatToBinaryData(const cv::Mat& image) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
if (!image.empty()) {
|
||||
if ((image.data != nullptr) && (image.u != nullptr)) {
|
||||
try {
|
||||
std::vector<uchar> imageData;
|
||||
bool success = cv::imencode(".jpg", image, imageData);
|
||||
if (!success) {
|
||||
this->_logger.LogError("ANSFILEPLAYER::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("ANSFILEPLAYER::MatToBinaryData. Error:", e.what(), __FILE__, __LINE__);
|
||||
return "";
|
||||
}
|
||||
catch (...) {
|
||||
this->_logger.LogFatal("ANSFILEPLAYER::MatToBinaryData. Error:", "Caught unknown exception!", __FILE__, __LINE__);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
else return "";
|
||||
}
|
||||
else return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void VerifyGlobalANSFPLicense(const std::string& licenseKey) {
|
||||
try {
|
||||
ansfileplayerLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1007, "ANSCV");//Default productId=1005
|
||||
if (!ansfileplayerLicenceValid) { // we also support ANSTS license
|
||||
ansfileplayerLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1003, "ANSVIS");//Default productId=1003 (ANSVIS)
|
||||
}
|
||||
if (!ansfileplayerLicenceValid) { // we also support ANSTS license
|
||||
ansfileplayerLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1008, "ANSTS");//Default productId=1008 (ANSTS)
|
||||
}
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
ansfileplayerLicenceValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int CreateANSFilePlayerHandle(ANSCENTER::ANSFILEPLAYER** Handle, const char* licenseKey, const char* url) {
|
||||
if (!Handle || !licenseKey || !url) return -1;
|
||||
try {
|
||||
auto ptr = std::make_unique<ANSCENTER::ANSFILEPLAYER>();
|
||||
bool result = ptr->Init(licenseKey, 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<ANSCENTER::ANSFILEPLAYER*>(p);
|
||||
try { h->Stop(); } catch (...) {}
|
||||
try { h->Destroy(); } catch (...) {}
|
||||
try { delete h; } catch (...) {}
|
||||
});
|
||||
return 1;
|
||||
}
|
||||
*Handle = nullptr;
|
||||
return 0;
|
||||
}
|
||||
catch (...) { return 0; }
|
||||
}
|
||||
extern "C" __declspec(dllexport) int ReleaseANSFilePlayerHandle(ANSCENTER::ANSFILEPLAYER** Handle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
extern void anscv_unregister_handle(void*);
|
||||
anscv_unregister_handle(*Handle);
|
||||
// Destructor calls Destroy() — no need to call it explicitly (avoids double-destroy)
|
||||
std::unique_ptr<ANSCENTER::ANSFILEPLAYER> ptr(*Handle);
|
||||
*Handle = nullptr;
|
||||
return 0;
|
||||
}
|
||||
catch (...) {
|
||||
if (Handle) *Handle = nullptr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerStrImage(ANSCENTER::ANSFILEPLAYER** Handle, int& width, int& height, int64_t& timeStamp, std::string& jpegImage) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
jpegImage = (*Handle)->GetJpegStringImage(width, height, timeStamp);
|
||||
if (!jpegImage.empty()) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error getting image data from FilePlayer client: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerImage(ANSCENTER::ANSFILEPLAYER** Handle, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
std::string jpegString = (*Handle)->GetJpegStringImage(width, height, timeStamp);
|
||||
int size = jpegString.length();
|
||||
if (size > 0) {
|
||||
MgErr error;
|
||||
// Resize the jpegImage handle to hold the image data
|
||||
error = DSSetHandleSize(jpegImage, sizeof(int32) + size * sizeof(uChar));
|
||||
// Check if resizing the handle was successful
|
||||
if (error == noErr) {
|
||||
// Set the size of the image in the handle
|
||||
(*jpegImage)->cnt = size;
|
||||
// Use memcpy to copy the data from the std::string to the LStrHandle's str buffer
|
||||
memcpy((*jpegImage)->str, jpegString.c_str(), size);
|
||||
// Return success
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
// Return failure if there was an error in resizing the handle
|
||||
std::cerr << "Error resizing jpegImage handle: " << error << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If the JPEG image string is empty, return failure
|
||||
std::cerr << "No image data retrieved from FilePlayer client." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error getting image data from FilePlayer client: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerCVImage(ANSCENTER::ANSFILEPLAYER** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image) {
|
||||
if (!Handle || !(*Handle) || !image) {
|
||||
std::cerr << "Error: Invalid input parameters in GetFilePlayerCVImage." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
cv::Mat img = (*Handle)->GetImage(width, height, timeStamp);
|
||||
if (img.empty()) {
|
||||
return 0;
|
||||
}
|
||||
anscv_mat_replace(image, std::move(img));
|
||||
|
||||
// Attach NV12/CUDA frames to registry for inference zero-copy (same as ANSRTSP/ANSVideoPlayer).
|
||||
// CFilePlayer inherits getNV12Frame()/getCudaHWFrame() from CVideoPlayer.
|
||||
int gpuIdx = (*Handle)->_playerClient->getHWDecodingGpuIndex();
|
||||
AVFrame* cudaHW = (*Handle)->_playerClient->getCudaHWFrame();
|
||||
if (cudaHW) {
|
||||
AVFrame* cpuNV12 = (*Handle)->_playerClient->getNV12Frame();
|
||||
gpu_frame_attach_cuda(*image, cudaHW, gpuIdx, timeStamp, cpuNV12);
|
||||
} else {
|
||||
AVFrame* nv12 = (*Handle)->_playerClient->getNV12Frame();
|
||||
if (nv12) {
|
||||
gpu_frame_attach(*image, nv12, gpuIdx, timeStamp);
|
||||
} else {
|
||||
// CPU-only fallback: attach full-res BGR clone for ANSRTYOLO
|
||||
cv::Mat infImg = (*Handle)->GetInferenceImage();
|
||||
if (!infImg.empty() && infImg.data != (*image)->data) {
|
||||
(*Handle)->_inferenceClonePrev = (*Handle)->_inferenceCloneCurr;
|
||||
(*Handle)->_inferenceCloneCurr = infImg.clone();
|
||||
|
||||
GpuFrameData data{};
|
||||
data.avframe = nullptr;
|
||||
data.yPlane = (*Handle)->_inferenceCloneCurr.data;
|
||||
data.uvPlane = nullptr;
|
||||
data.yLinesize = static_cast<int>((*Handle)->_inferenceCloneCurr.step[0]);
|
||||
data.uvLinesize = 0;
|
||||
data.width = (*Handle)->_inferenceCloneCurr.cols;
|
||||
data.height = (*Handle)->_inferenceCloneCurr.rows;
|
||||
data.pixelFormat = 1000; // ANSCV_PIX_FMT_BGR24
|
||||
data.gpuIndex = -1;
|
||||
data.pts = timeStamp;
|
||||
data.isCudaDevicePtr = false;
|
||||
data.cpuAvframe = nullptr;
|
||||
data.cpuYPlane = nullptr;
|
||||
data.cpuUvPlane = nullptr;
|
||||
data.cpuYLinesize = 0;
|
||||
data.cpuUvLinesize = 0;
|
||||
|
||||
ANSGpuFrameRegistry::instance().attach(*image, std::move(data));
|
||||
auto pending = ANSGpuFrameRegistry::instance().drain_pending();
|
||||
for (void* p : pending) {
|
||||
AVFrame* stale = static_cast<AVFrame*>(p);
|
||||
av_frame_free(&stale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
catch (const cv::Exception& e) {
|
||||
std::cerr << "OpenCV exception in GetFilePlayerCVImage: " << e.what() << std::endl;
|
||||
return -2;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Exception in GetFilePlayerCVImage: " << e.what() << std::endl;
|
||||
return -2;
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "Unknown exception in GetFilePlayerCVImage" << std::endl;
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int StartFilePlayer(ANSCENTER::ANSFILEPLAYER** 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 file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int ReconnectFilePlayer(ANSCENTER::ANSFILEPLAYER** 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 file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int StopFilePlayer(ANSCENTER::ANSFILEPLAYER** 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 file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerPaused(ANSCENTER::ANSFILEPLAYER** Handle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
bool result = (*Handle)->IsPaused();
|
||||
if (result) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error checking if file player is paused: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerRunning(ANSCENTER::ANSFILEPLAYER** Handle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
bool result = (*Handle)->IsPlaying();
|
||||
if (result) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error checking if file player is running: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerRecording(ANSCENTER::ANSFILEPLAYER** Handle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
bool result = (*Handle)->IsRecording();
|
||||
if (result) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error checking if file player is recording: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerAudioVolume(ANSCENTER::ANSFILEPLAYER** Handle, int volume)
|
||||
{
|
||||
if (Handle == nullptr || *Handle == nullptr) return;
|
||||
try {
|
||||
(*Handle)->SetAudioVolume(volume);
|
||||
}
|
||||
catch (...) { }
|
||||
}
|
||||
extern "C" __declspec(dllexport) void EnableFilePlayerAudioVolume(ANSCENTER::ANSFILEPLAYER** Handle, int status)
|
||||
{
|
||||
if (Handle == nullptr || *Handle == nullptr) return;
|
||||
try {
|
||||
bool audioStatus = false;
|
||||
if (status == 1)audioStatus = true;
|
||||
(*Handle)->EnableAudio(audioStatus);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error enabling audio for file player: " << e.what() << std::endl;
|
||||
}
|
||||
catch (...) { }
|
||||
}
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerImageRotation(ANSCENTER::ANSFILEPLAYER** Handle, double rotationAngle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return;
|
||||
try {
|
||||
(*Handle)->SetImageRotate(rotationAngle);
|
||||
}
|
||||
catch (...) { }
|
||||
}
|
||||
extern "C" __declspec(dllexport) int SetBBoxFilePlayer(ANSCENTER::ANSFILEPLAYER** 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 (const std::exception& e) {
|
||||
std::cerr << "Error setting bounding box for file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int SetCropFlagFilePlayer(ANSCENTER::ANSFILEPLAYER** 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 (const std::exception& e) {
|
||||
std::cerr << "Error setting crop flag for file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// V2 functions: accept uint64_t handleVal by value (LabVIEW-safe)
|
||||
// ============================================================================
|
||||
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerImage_V2(uint64_t handleVal, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h) return -1;
|
||||
try {
|
||||
std::string jpegString = h->GetJpegStringImage(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 FilePlayer client." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error getting image data from FilePlayer client: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerCVImage_V2(uint64_t handleVal, int& width, int& height, int64_t& timeStamp, cv::Mat** image) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h || !image) return -1;
|
||||
try {
|
||||
cv::Mat img = h->GetImage(width, height, timeStamp);
|
||||
if (img.empty()) {
|
||||
return 0;
|
||||
}
|
||||
anscv_mat_replace(image, std::move(img));
|
||||
|
||||
// NV12/CUDA registry attach (same as V1)
|
||||
int gpuIdx = h->_playerClient->getHWDecodingGpuIndex();
|
||||
AVFrame* cudaHW = h->_playerClient->getCudaHWFrame();
|
||||
if (cudaHW) {
|
||||
AVFrame* cpuNV12 = h->_playerClient->getNV12Frame();
|
||||
gpu_frame_attach_cuda(*image, cudaHW, gpuIdx, timeStamp, cpuNV12);
|
||||
} else {
|
||||
AVFrame* nv12 = h->_playerClient->getNV12Frame();
|
||||
if (nv12) {
|
||||
gpu_frame_attach(*image, nv12, gpuIdx, timeStamp);
|
||||
} else {
|
||||
cv::Mat infImg = h->GetInferenceImage();
|
||||
if (!infImg.empty() && infImg.data != (*image)->data) {
|
||||
h->_inferenceClonePrev = h->_inferenceCloneCurr;
|
||||
h->_inferenceCloneCurr = infImg.clone();
|
||||
|
||||
GpuFrameData data{};
|
||||
data.avframe = nullptr;
|
||||
data.yPlane = h->_inferenceCloneCurr.data;
|
||||
data.uvPlane = nullptr;
|
||||
data.yLinesize = static_cast<int>(h->_inferenceCloneCurr.step[0]);
|
||||
data.uvLinesize = 0;
|
||||
data.width = h->_inferenceCloneCurr.cols;
|
||||
data.height = h->_inferenceCloneCurr.rows;
|
||||
data.pixelFormat = 1000;
|
||||
data.gpuIndex = -1;
|
||||
data.pts = timeStamp;
|
||||
data.isCudaDevicePtr = false;
|
||||
data.cpuAvframe = nullptr;
|
||||
data.cpuYPlane = nullptr;
|
||||
data.cpuUvPlane = nullptr;
|
||||
data.cpuYLinesize = 0;
|
||||
data.cpuUvLinesize = 0;
|
||||
|
||||
ANSGpuFrameRegistry::instance().attach(*image, std::move(data));
|
||||
auto pending = ANSGpuFrameRegistry::instance().drain_pending();
|
||||
for (void* p : pending) {
|
||||
AVFrame* stale = static_cast<AVFrame*>(p);
|
||||
av_frame_free(&stale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
catch (const cv::Exception& e) {
|
||||
std::cerr << "OpenCV exception in GetFilePlayerCVImage_V2: " << e.what() << std::endl;
|
||||
return -2;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Exception in GetFilePlayerCVImage_V2: " << e.what() << std::endl;
|
||||
return -2;
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "Unknown exception in GetFilePlayerCVImage_V2" << std::endl;
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int StartFilePlayer_V2(uint64_t handleVal) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(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 file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int ReconnectFilePlayer_V2(uint64_t handleVal) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(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 file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int StopFilePlayer_V2(uint64_t handleVal) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(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 file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerPaused_V2(uint64_t handleVal) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h) return -1;
|
||||
try {
|
||||
bool result = h->IsPaused();
|
||||
if (result) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error checking if file player is paused: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerRunning_V2(uint64_t handleVal) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h) return -1;
|
||||
try {
|
||||
bool result = h->IsPlaying();
|
||||
if (result) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error checking if file player is running: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerRecording_V2(uint64_t handleVal) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h) return -1;
|
||||
try {
|
||||
bool result = h->IsRecording();
|
||||
if (result) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error checking if file player is recording: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerAudioVolume_V2(uint64_t handleVal, int volume) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h) return;
|
||||
try {
|
||||
h->SetAudioVolume(volume);
|
||||
}
|
||||
catch (...) { }
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void EnableFilePlayerAudioVolume_V2(uint64_t handleVal, int status) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h) return;
|
||||
try {
|
||||
bool audioStatus = false;
|
||||
if (status == 1) audioStatus = true;
|
||||
h->EnableAudio(audioStatus);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error enabling audio for file player: " << e.what() << std::endl;
|
||||
}
|
||||
catch (...) { }
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerImageRotation_V2(uint64_t handleVal, double rotationAngle) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h) return;
|
||||
try {
|
||||
h->SetImageRotate(rotationAngle);
|
||||
}
|
||||
catch (...) { }
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int SetBBoxFilePlayer_V2(uint64_t handleVal, int x, int y, int width, int height) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h) return -1;
|
||||
try {
|
||||
cv::Rect bbox(x, y, width, height);
|
||||
h->SetBBox(bbox);
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error setting bounding box for file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int SetCropFlagFilePlayer_V2(uint64_t handleVal, int cropFlag) {
|
||||
auto* h = reinterpret_cast<ANSCENTER::ANSFILEPLAYER*>(handleVal);
|
||||
if (!h) return -1;
|
||||
try {
|
||||
bool crop = false;
|
||||
if (cropFlag == 1) crop = true;
|
||||
h->SetCrop(crop);
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error setting crop flag for file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerDisplayResolution(ANSCENTER::ANSFILEPLAYER** Handle, int width, int height) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return;
|
||||
try {
|
||||
(*Handle)->SetDisplayResolution(width, height);
|
||||
} catch (...) { }
|
||||
}
|
||||
117
modules/ANSCV/ANSFilePlayer.h
Normal file
117
modules/ANSCV/ANSFilePlayer.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef ANSFILEPLAYER_H
|
||||
#define ANSFILEPLAYER_H
|
||||
#define ANSFILEPLAYER_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#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 "file_player.h"
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <turbojpeg.h>
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
class ANSFILEPLAYER_API ANSFILEPLAYER
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<CFilePlayer> _playerClient = std::make_unique<CFilePlayer>();
|
||||
protected:
|
||||
std::string _url;
|
||||
double _imageRotateDeg = 0;
|
||||
cv::Mat _pLastFrame;
|
||||
std::string _lastJpegImage;
|
||||
int _imageWidth = 0, _imageHeight = 0;
|
||||
int64_t _pts = 0;
|
||||
bool _isPlaying = false;
|
||||
std::recursive_mutex _mutex;
|
||||
|
||||
// Display/inference split (same pattern as ANSVideoPlayer)
|
||||
int _displayWidth = 0;
|
||||
int _displayHeight = 0;
|
||||
cv::Mat _inferenceImage; // full-res, no display resize
|
||||
public:
|
||||
// Public: accessed by extern "C" GetFilePlayerCVImage for NV12/CUDA registry attachment
|
||||
cv::Mat _inferenceCloneCurr; // current clone for registry (keeps data alive)
|
||||
cv::Mat _inferenceClonePrev; // previous clone (keeps data alive until next frame)
|
||||
|
||||
ANSFILEPLAYER();
|
||||
~ANSFILEPLAYER() noexcept;
|
||||
[[nodiscard]] bool Init(std::string licenseKey, std::string url);
|
||||
[[nodiscard]] bool Setup();
|
||||
[[nodiscard]] bool Reconnect();
|
||||
[[nodiscard]] bool Start();
|
||||
void SetBBox(cv::Rect bbox);
|
||||
void SetCrop(bool crop);
|
||||
[[nodiscard]] bool IsPaused();
|
||||
[[nodiscard]] bool IsPlaying();
|
||||
[[nodiscard]] bool IsRecording();
|
||||
[[nodiscard]] bool Stop();
|
||||
void Destroy();
|
||||
void EnableAudio(bool status);
|
||||
void SetAudioVolume(int volume);
|
||||
void SetImageRotate(double mode) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_imageRotateDeg = mode;
|
||||
if (mode > 360) _imageRotateDeg = 360;
|
||||
};
|
||||
void SetDisplayResolution(int width, int height);
|
||||
[[nodiscard]] cv::Mat GetImage(int& width, int& height, int64_t& pts);
|
||||
[[nodiscard]] cv::Mat GetInferenceImage();
|
||||
[[nodiscard]] std::string GetJpegImage(int& width, int& height, int64_t& pts);
|
||||
[[nodiscard]] std::string GetJpegStringImage(int& width, int& height, int64_t& pts) {
|
||||
return GetJpegImage(width, height, pts);
|
||||
}
|
||||
[[nodiscard]] std::string MatToBinaryData(const cv::Mat& image);
|
||||
public:
|
||||
void CheckLicense();
|
||||
SPDLogger& _logger = SPDLogger::GetInstance("ANSFilePlayer", false);
|
||||
bool _licenseValid{ false };
|
||||
std::string _licenseKey;
|
||||
};
|
||||
}
|
||||
extern "C" __declspec(dllexport) int CreateANSFilePlayerHandle(ANSCENTER::ANSFILEPLAYER* *Handle, const char* licenseKey, const char* url);
|
||||
extern "C" __declspec(dllexport) int ReleaseANSFilePlayerHandle(ANSCENTER::ANSFILEPLAYER* *Handle);
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerImage(ANSCENTER::ANSFILEPLAYER* *Handle, int& width, int& height, int64_t & timeStamp, LStrHandle jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerStrImage(ANSCENTER::ANSFILEPLAYER* *Handle, int& width, int& height, int64_t & timeStamp, std::string & jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerCVImage(ANSCENTER::ANSFILEPLAYER** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image);
|
||||
extern "C" __declspec(dllexport) int StartFilePlayer(ANSCENTER::ANSFILEPLAYER* *Handle);
|
||||
extern "C" __declspec(dllexport) int ReconnectFilePlayer(ANSCENTER::ANSFILEPLAYER* *Handle);
|
||||
extern "C" __declspec(dllexport) int StopFilePlayer(ANSCENTER::ANSFILEPLAYER* *Handle);
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerPaused(ANSCENTER::ANSFILEPLAYER* *Handle);
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerRunning(ANSCENTER::ANSFILEPLAYER* *Handle);
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerRecording(ANSCENTER::ANSFILEPLAYER* *Handle);
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerAudioVolume(ANSCENTER::ANSFILEPLAYER* *Handle, int volume);
|
||||
extern "C" __declspec(dllexport) void EnableFilePlayerAudioVolume(ANSCENTER::ANSFILEPLAYER* *Handle, int status);
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerImageRotation(ANSCENTER::ANSFILEPLAYER* *Handle, double rotationAngle);
|
||||
extern "C" __declspec(dllexport) int SetBBoxFilePlayer(ANSCENTER::ANSFILEPLAYER** Handle, int x, int y, int width, int height);
|
||||
extern "C" __declspec(dllexport) int SetCropFlagFilePlayer(ANSCENTER::ANSFILEPLAYER** Handle, int cropFlag);
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerDisplayResolution(ANSCENTER::ANSFILEPLAYER** Handle, int width, int height);
|
||||
|
||||
// V2 functions: accept uint64_t handleVal by value (LabVIEW-safe)
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerImage_V2(uint64_t handleVal, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetFilePlayerCVImage_V2(uint64_t handleVal, int& width, int& height, int64_t& timeStamp, cv::Mat** image);
|
||||
extern "C" __declspec(dllexport) int StartFilePlayer_V2(uint64_t handleVal);
|
||||
extern "C" __declspec(dllexport) int ReconnectFilePlayer_V2(uint64_t handleVal);
|
||||
extern "C" __declspec(dllexport) int StopFilePlayer_V2(uint64_t handleVal);
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerPaused_V2(uint64_t handleVal);
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerRunning_V2(uint64_t handleVal);
|
||||
extern "C" __declspec(dllexport) int IsFilePlayerRecording_V2(uint64_t handleVal);
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerAudioVolume_V2(uint64_t handleVal, int volume);
|
||||
extern "C" __declspec(dllexport) void EnableFilePlayerAudioVolume_V2(uint64_t handleVal, int status);
|
||||
extern "C" __declspec(dllexport) void SetFilePlayerImageRotation_V2(uint64_t handleVal, double rotationAngle);
|
||||
extern "C" __declspec(dllexport) int SetBBoxFilePlayer_V2(uint64_t handleVal, int x, int y, int width, int height);
|
||||
extern "C" __declspec(dllexport) int SetCropFlagFilePlayer_V2(uint64_t handleVal, int cropFlag);
|
||||
#endif
|
||||
762
modules/ANSCV/ANSFilePlayer_CV.cpp
Normal file
762
modules/ANSCV/ANSFilePlayer_CV.cpp
Normal file
@@ -0,0 +1,762 @@
|
||||
#include "ANSFilePlayer_CV.h"
|
||||
#include "ANSMatRegistry.h"
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libavutil/frame.h>
|
||||
#include "media_codec.h"
|
||||
static bool ansfileplayerLicenceValid = false;
|
||||
// Global once_flag to protect license checking
|
||||
static std::once_flag ansfileplayerLicenseOnceFlag;
|
||||
namespace ANSCENTER {
|
||||
#define CHECK_CUDA(call) do { \
|
||||
cudaError_t err = call; \
|
||||
if (err != cudaSuccess) { \
|
||||
std::cout<<"CUDA error: " + std::string(cudaGetErrorString(err)); \
|
||||
} \
|
||||
} while (0)
|
||||
#define CHECK_NVJPEG(call) do { \
|
||||
nvjpegStatus_t status = call; \
|
||||
if (status != NVJPEG_STATUS_SUCCESS) { \
|
||||
std::cout <<"nvJPEG error: " + std::to_string(status); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
ANSFILEPLAYER_CV::ANSFILEPLAYER_CV() :_stopThread(std::make_shared<std::atomic<bool>>(false))
|
||||
{
|
||||
_resWidth = 0;
|
||||
_resHeight = 0;
|
||||
m_bPaused = false;
|
||||
_crop = false;
|
||||
_isPlaying = false;
|
||||
_lastJpegImage = "";
|
||||
_bbox = cv::Rect(0, 0, 0, 0);
|
||||
_jpegCompressor = nullptr;
|
||||
_previousPTS = 0;
|
||||
_totalFrames = 0;
|
||||
}
|
||||
ANSFILEPLAYER_CV::~ANSFILEPLAYER_CV() noexcept {
|
||||
try {
|
||||
StopTimerThread();
|
||||
Destroy();
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
void ANSFILEPLAYER_CV::Destroy() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
_previousImage.release();
|
||||
_lastJpegImage = "";
|
||||
_isPlaying = false;
|
||||
_resWidth = 0;
|
||||
_resHeight = 0;
|
||||
_currentFrame = 0;
|
||||
_previousPTS = 0;
|
||||
if (cap.isOpened()) {
|
||||
cap.release();
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
_logger.LogError("ANSFILEPLAYER_CV::Destroy. Exception:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
catch (...) {
|
||||
_logger.LogError("ANSFILEPLAYER_CV::Destroy.", "Unknown exception", __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
static void VerifyGlobalANSFPLicense(const std::string& licenseKey) {
|
||||
try {
|
||||
ansfileplayerLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1007, "ANSCV");//Default productId=1005
|
||||
if (!ansfileplayerLicenceValid) { // we also support ANSTS license
|
||||
ansfileplayerLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1003, "ANSVIS");//Default productId=1003 (ANSVIS)
|
||||
}
|
||||
if (!ansfileplayerLicenceValid) { // we also support ANSTS license
|
||||
ansfileplayerLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1008, "ANSTS");//Default productId=1008 (ANSTS)
|
||||
}
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
ansfileplayerLicenceValid = false;
|
||||
}
|
||||
}
|
||||
void ANSFILEPLAYER_CV::CheckLicense() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
// Check once globally
|
||||
std::call_once(ansfileplayerLicenseOnceFlag, [this]() {
|
||||
VerifyGlobalANSFPLicense(_licenseKey);
|
||||
});
|
||||
|
||||
// Update this instance's local license flag
|
||||
_licenseValid = ansfileplayerLicenceValid;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogFatal("ANSFILEPLAYER_CV::CheckLicense. Error:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ANSFILEPLAYER_CV::Init(std::string licenseKey, std::string url) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_licenseKey = licenseKey;
|
||||
CheckLicense();
|
||||
if (!_licenseValid) {
|
||||
_logger.LogError("ANSFILEPLAYER_CV::Init.", "Invalid license", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
_url = url;
|
||||
return Setup();
|
||||
}
|
||||
bool ANSFILEPLAYER_CV::Setup() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_currentFrame = 0;
|
||||
if (cap.isOpened()) return true;
|
||||
cap.open(_url, cv::CAP_FFMPEG); // USe FFMPEG for better codec support
|
||||
if (!cap.isOpened()) {
|
||||
cap.open(_url, cv::CAP_ANY);
|
||||
if (!cap.isOpened())return false;
|
||||
}
|
||||
try {
|
||||
_resWidth = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
|
||||
_resHeight = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
|
||||
_totalFrames = static_cast<int64_t>(cap.get(cv::CAP_PROP_FRAME_COUNT));
|
||||
_fps = cap.get(cv::CAP_PROP_FPS);
|
||||
cap.set(cv::CAP_PROP_POS_FRAMES, _currentFrame);
|
||||
if (_fps > 0) {
|
||||
if (_timerThread && _timerThread->joinable()) return true; // Exit if the thread is already running
|
||||
int interval_ms = static_cast<int>(1000 / _fps);
|
||||
*_stopThread = false;
|
||||
_timerThread = std::make_shared<std::thread>(&ANSFILEPLAYER_CV::StartTimer, this, _stopThread, interval_ms);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::Setup:", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void ANSFILEPLAYER_CV::StartTimer(std::shared_ptr<std::atomic<bool>> stopFlag, int interval_ms) {
|
||||
while (!stopFlag->load()) {
|
||||
std::unique_lock<std::mutex> lock(_cvMutex);
|
||||
// Wait for the specified interval or until stopFlag is set
|
||||
if (_cv.wait_for(lock, std::chrono::milliseconds(interval_ms), [&]() {
|
||||
return stopFlag->load();
|
||||
})) {
|
||||
break; // Exit the loop if stopFlag is true
|
||||
}
|
||||
lock.unlock(); // Unlock to avoid deadlocks in further operations
|
||||
int delayMs = 0;
|
||||
int actualSleepTime = interval_ms;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
auto start = std::chrono::system_clock::now();
|
||||
if (_isPlaying) {
|
||||
try {
|
||||
_currentFrame = static_cast<int64_t>(cap.get(cv::CAP_PROP_POS_FRAMES));
|
||||
// Add check for _stopThread before blocking operations
|
||||
if (!cap.grab() || _currentFrame >= _totalFrames - 1) {
|
||||
if (cap.isOpened()) {
|
||||
cap.release();
|
||||
cap.open(_url, cv::CAP_FFMPEG); // USe FFMPEG for better codec support
|
||||
if (!cap.isOpened()) {
|
||||
cap.open(_url, cv::CAP_ANY);
|
||||
}
|
||||
}
|
||||
if (cap.isOpened()) {
|
||||
cap.set(cv::CAP_PROP_POS_FRAMES, 0);
|
||||
_currentFrame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::StartTimer. Exception occurred:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
auto end = std::chrono::system_clock::now();
|
||||
delayMs = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||
// Calculate actual sleep time based on any delay introduced
|
||||
actualSleepTime = interval_ms - delayMs;
|
||||
if (actualSleepTime < 1) actualSleepTime = 1;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(actualSleepTime));
|
||||
}
|
||||
}
|
||||
void ANSFILEPLAYER_CV::StopTimerThread() {
|
||||
*_stopThread = true; // Signal to stop the thread
|
||||
_cv.notify_all(); // Notify the condition variable to unblock the thread
|
||||
if (_timerThread && _timerThread->joinable()) {
|
||||
_timerThread->join(); // Join the timer thread to ensure cleanup
|
||||
}
|
||||
}
|
||||
bool ANSFILEPLAYER_CV::Reconnect() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
_currentFrame = 0;
|
||||
if (cap.isOpened()) {
|
||||
cap.release();
|
||||
}
|
||||
Setup();
|
||||
_isPlaying = cap.isOpened() && cap.grab();
|
||||
return _isPlaying;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::Reconnect. Exception occurred:", e.what(), __FILE__, __LINE__);
|
||||
_currentFrame = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
bool ANSFILEPLAYER_CV::Start() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
if (!cap.isOpened()) {
|
||||
cap.open(_url, cv::CAP_FFMPEG);
|
||||
if (!cap.isOpened()) {
|
||||
cap.open(_url, cv::CAP_ANY);
|
||||
}
|
||||
if (!cap.isOpened()) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::Start. Exception occurred:", "Failed to open video source: ", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (!cap.set(cv::CAP_PROP_POS_FRAMES, _currentFrame)) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::Start. Exception occurred:", "Warning: Unable to seek to frame", __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::Start. Exception occurred:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
_isPlaying = cap.isOpened() && cap.grab();
|
||||
return _isPlaying;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::Start. Exception occurred:", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ANSFILEPLAYER_CV::Stop() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
try {
|
||||
if (cap.isOpened()) {
|
||||
try {
|
||||
double frame_pos = cap.get(cv::CAP_PROP_POS_FRAMES);
|
||||
if (frame_pos >= 0) {
|
||||
_currentFrame = static_cast<int64_t>(frame_pos);
|
||||
}
|
||||
else {
|
||||
_currentFrame = 0;
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::Stop. Exception occurred:", "Unable to retrieve current frame position", __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::Stop. Exception occurred:", e.what(), __FILE__, __LINE__);
|
||||
_currentFrame = 0;
|
||||
}
|
||||
cap.release();
|
||||
}
|
||||
_isPlaying = false;
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::Stop. Exception occurred:", e.what(), __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
void ANSFILEPLAYER_CV::SetBBox(cv::Rect bbox) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_bbox = bbox;
|
||||
}
|
||||
void ANSFILEPLAYER_CV::SetCrop(bool crop) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_crop = crop;
|
||||
}
|
||||
bool ANSFILEPLAYER_CV::IsPaused() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
return !cap.isOpened();
|
||||
}
|
||||
bool ANSFILEPLAYER_CV::IsPlaying() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
return cap.isOpened();
|
||||
}
|
||||
bool ANSFILEPLAYER_CV::IsRecording() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
return false;// do not support recording for webcam
|
||||
}
|
||||
|
||||
|
||||
cv::Mat ANSFILEPLAYER_CV::GetImage(int& width, int& height, int64_t& pts) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
|
||||
if (!_isPlaying) {
|
||||
if (!_previousImage.empty()) {
|
||||
width = _previousImage.cols;
|
||||
height = _previousImage.rows;
|
||||
pts = _previousPTS;
|
||||
return _previousImage;
|
||||
}
|
||||
return cv::Mat();
|
||||
}
|
||||
|
||||
try {
|
||||
if (!cap.isOpened()) {
|
||||
if (!_previousImage.empty()) {
|
||||
width = _previousImage.cols;
|
||||
height = _previousImage.rows;
|
||||
pts = _previousPTS;
|
||||
return _previousImage;
|
||||
}
|
||||
return cv::Mat();
|
||||
}
|
||||
|
||||
cv::Mat frame;
|
||||
if (!cap.read(frame) || frame.empty()) {
|
||||
// Return last good frame if available
|
||||
if (!_previousImage.empty()) {
|
||||
width = _previousImage.cols;
|
||||
height = _previousImage.rows;
|
||||
pts = _previousPTS;
|
||||
return _previousImage;
|
||||
}
|
||||
return cv::Mat();
|
||||
}
|
||||
|
||||
cv::Mat result;
|
||||
|
||||
// Apply cropping if enabled
|
||||
if (_crop) {
|
||||
// Validate and clamp crop region
|
||||
_bbox.x = std::clamp(_bbox.x, 0, frame.cols - 1);
|
||||
_bbox.y = std::clamp(_bbox.y, 0, frame.rows - 1);
|
||||
_bbox.width = std::min(_bbox.width, frame.cols - _bbox.x);
|
||||
_bbox.height = std::min(_bbox.height, frame.rows - _bbox.y);
|
||||
|
||||
if (_bbox.width > 0 && _bbox.height > 0) {
|
||||
// CRITICAL: Clone to avoid dangling reference
|
||||
result = frame(_bbox).clone();
|
||||
}
|
||||
else {
|
||||
result = frame.clone();
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = frame.clone();
|
||||
}
|
||||
|
||||
// Apply rotation if specified
|
||||
if (_imageRotateDeg > 0) {
|
||||
// Fast path for 90-degree rotations
|
||||
if (std::abs(_imageRotateDeg - 90.0) < 0.01) {
|
||||
cv::rotate(result, result, cv::ROTATE_90_CLOCKWISE);
|
||||
}
|
||||
else if (std::abs(_imageRotateDeg - 180.0) < 0.01) {
|
||||
cv::rotate(result, result, cv::ROTATE_180);
|
||||
}
|
||||
else if (std::abs(_imageRotateDeg - 270.0) < 0.01) {
|
||||
cv::rotate(result, result, cv::ROTATE_90_COUNTERCLOCKWISE);
|
||||
}
|
||||
else {
|
||||
// Arbitrary angle rotation
|
||||
const cv::Point2f center(result.cols / 2.0f, result.rows / 2.0f);
|
||||
cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, _imageRotateDeg, 1.0);
|
||||
|
||||
cv::Mat rotated;
|
||||
// Use INTER_LINEAR instead of INTER_CUBIC (2-3x faster, minimal quality loss)
|
||||
cv::warpAffine(result, rotated, rotationMatrix, result.size(),
|
||||
cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar());
|
||||
result = rotated;
|
||||
}
|
||||
}
|
||||
|
||||
// Update PTS (presentation timestamp)
|
||||
if (_previousPTS < INT64_MAX) {
|
||||
_previousPTS++;
|
||||
}
|
||||
else {
|
||||
_previousPTS = 0;
|
||||
}
|
||||
|
||||
// Update cached frame
|
||||
_previousImage = result;
|
||||
|
||||
// Set output parameters
|
||||
width = result.cols;
|
||||
height = result.rows;
|
||||
pts = _previousPTS;
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (const cv::Exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::GetImage. OpenCV exception:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::GetImage. Exception:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
catch (...) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::GetImage. Unknown exception", "", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
return cv::Mat();
|
||||
}
|
||||
|
||||
void ANSFILEPLAYER_CV::EnableAudio(bool status) {
|
||||
// please support audio enable for webcam
|
||||
}
|
||||
void ANSFILEPLAYER_CV::SetAudioVolume(int volume) {
|
||||
// support audio volumne
|
||||
}
|
||||
std::string ANSFILEPLAYER_CV::encodeJpegString(const cv::Mat& img, int quality) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
if (!_isPlaying) return _lastJpegImage;
|
||||
unsigned char* jpegBuf = nullptr;
|
||||
tjhandle jpegCompressor = nullptr;
|
||||
try {
|
||||
// Initialize TurboJPEG compressor
|
||||
jpegCompressor = tjInitCompress();
|
||||
if (!jpegCompressor) {
|
||||
this->_logger.LogError("Failed to initialize TurboJPEG compressor.", tjGetErrorStr(), __FILE__, __LINE__);
|
||||
return _lastJpegImage;
|
||||
}
|
||||
|
||||
int maxBufferSize = img.cols * img.rows * 3;
|
||||
jpegBuf = tjAlloc(maxBufferSize);
|
||||
if (!jpegBuf) {
|
||||
this->_logger.LogError("Failed to allocate memory for JPEG buffer.", tjGetErrorStr(), __FILE__, __LINE__);
|
||||
tjDestroy(jpegCompressor);
|
||||
return _lastJpegImage;
|
||||
}
|
||||
|
||||
long unsigned int jpegSize = maxBufferSize;
|
||||
int subsamp = TJSAMP_444;
|
||||
int pixelFormat = img.channels() == 3 ? TJPF_BGR : TJPF_GRAY;
|
||||
|
||||
// Compress the image
|
||||
if (tjCompress2(jpegCompressor, img.data, img.cols, 0, img.rows, pixelFormat,
|
||||
&jpegBuf, &jpegSize, subsamp, quality, TJFLAG_FASTDCT) != 0) {
|
||||
this->_logger.LogError("Compression error:", tjGetErrorStr(), __FILE__, __LINE__);
|
||||
tjFree(jpegBuf);
|
||||
tjDestroy(jpegCompressor);
|
||||
return _lastJpegImage;
|
||||
}
|
||||
|
||||
// Create string from JPEG buffer
|
||||
std::string jpegString(reinterpret_cast<char*>(jpegBuf), jpegSize);
|
||||
_lastJpegImage = jpegString;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogError("Exception occurred:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
// Cleanup resources
|
||||
if (jpegBuf) tjFree(jpegBuf);
|
||||
if (jpegCompressor) tjDestroy(jpegCompressor);
|
||||
|
||||
return _lastJpegImage;
|
||||
}
|
||||
std::string ANSFILEPLAYER_CV::MatToBinaryData(const cv::Mat& image) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
// Check if the image is empty or has invalid data
|
||||
if (!_isPlaying) return _lastJpegImage;
|
||||
if (image.empty() || !image.data || !image.u) {
|
||||
return _lastJpegImage;
|
||||
}
|
||||
try {
|
||||
// Encode the image to a memory buffer
|
||||
return encodeJpegString(image, 85);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogFatal("ANSFILEPLAYER_CV::MatToBinaryData. Exception occurred:", e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
catch (...) {
|
||||
this->_logger.LogFatal("ANSFILEPLAYER_CV::MatToBinaryData.", "Unknown exception occurred.", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
// Return an empty string in case of failure
|
||||
return _lastJpegImage;
|
||||
}
|
||||
std::string ANSFILEPLAYER_CV::GetJpegStringImage(int& width, int& height, int64_t& pts) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
if (!_isPlaying) return _lastJpegImage;
|
||||
try {
|
||||
cv::Mat image = GetImage(width, height, pts);
|
||||
std::string jpegString = MatToBinaryData(image);
|
||||
image.release();
|
||||
return jpegString;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
this->_logger.LogError("ANSFILEPLAYER_CV::GetJpegStringImage. Exception occurred:", e.what(), __FILE__, __LINE__);
|
||||
return _lastJpegImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int CreateANSFileCVPlayerHandle(ANSCENTER::ANSFILEPLAYER_CV** Handle, const char* licenseKey, const char* url) {
|
||||
if (!Handle || !licenseKey || !url) return -1;
|
||||
try {
|
||||
auto ptr = std::make_unique<ANSCENTER::ANSFILEPLAYER_CV>();
|
||||
bool result = ptr->Init(licenseKey, 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<ANSCENTER::ANSFILEPLAYER_CV*>(p);
|
||||
try { h->Stop(); } catch (...) {}
|
||||
try { h->Destroy(); } catch (...) {}
|
||||
try { delete h; } catch (...) {}
|
||||
});
|
||||
return 1;
|
||||
}
|
||||
*Handle = nullptr;
|
||||
return 0;
|
||||
}
|
||||
catch (...) { return 0; }
|
||||
}
|
||||
extern "C" __declspec(dllexport) int ReleaseANSFileCVPlayerHandle(ANSCENTER::ANSFILEPLAYER_CV** Handle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
extern void anscv_unregister_handle(void*);
|
||||
anscv_unregister_handle(*Handle);
|
||||
// Destructor calls Destroy() <20> no need to call it explicitly (avoids double-destroy)
|
||||
std::unique_ptr<ANSCENTER::ANSFILEPLAYER_CV> ptr(*Handle);
|
||||
*Handle = nullptr;
|
||||
return 0;
|
||||
}
|
||||
catch (...) {
|
||||
if (Handle) *Handle = nullptr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int GetFileCVPlayerStrImage(ANSCENTER::ANSFILEPLAYER_CV** Handle, int& width, int& height, int64_t& timeStamp, std::string& jpegImage) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
jpegImage = (*Handle)->GetJpegStringImage(width, height, timeStamp);
|
||||
if (!jpegImage.empty()) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error getting image data from FilePlayer client: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int GetFileCVPlayerImage(ANSCENTER::ANSFILEPLAYER_CV** Handle, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
std::string jpegString = (*Handle)->GetJpegStringImage(width, height, timeStamp);
|
||||
int size = jpegString.length();
|
||||
if (size > 0) {
|
||||
MgErr error;
|
||||
// Resize the jpegImage handle to hold the image data
|
||||
error = DSSetHandleSize(jpegImage, sizeof(int32) + size * sizeof(uChar));
|
||||
// Check if resizing the handle was successful
|
||||
if (error == noErr) {
|
||||
// Set the size of the image in the handle
|
||||
(*jpegImage)->cnt = size;
|
||||
// Use memcpy to copy the data from the std::string to the LStrHandle's str buffer
|
||||
memcpy((*jpegImage)->str, jpegString.c_str(), size);
|
||||
// Return success
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
// Return failure if there was an error in resizing the handle
|
||||
std::cerr << "Error resizing jpegImage handle: " << error << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If the JPEG image string is empty, return failure
|
||||
std::cerr << "No image data retrieved from FilePlayer client." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error getting image data from FilePlayer client: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int GetFileCVPlayerCVImage(ANSCENTER::ANSFILEPLAYER_CV** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image) {
|
||||
if (!Handle || !(*Handle) || !image) {
|
||||
std::cerr << "Error: Invalid input parameters in GetFilePlayerCVImage." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
cv::Mat img = (*Handle)->GetImage(width, height, timeStamp);
|
||||
if (img.empty()) {
|
||||
return 0; // No valid image retrieved
|
||||
}
|
||||
// anscv_mat_replace has its own internal registry_mutex <20> no global mutex needed
|
||||
anscv_mat_replace(image, std::move(img));
|
||||
|
||||
return 1; // Success
|
||||
}
|
||||
catch (const cv::Exception& e) {
|
||||
std::cerr << "OpenCV exception in GetFilePlayerCVImage: " << e.what() << std::endl;
|
||||
return -2;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Exception in GetFilePlayerCVImage: " << e.what() << std::endl;
|
||||
return -2;
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "Unknown exception in GetFilePlayerCVImage" << std::endl;
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) int StartFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** 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 file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int ReconnectFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** 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 file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int StopFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** 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 file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int IsFileCVPlayerPaused(ANSCENTER::ANSFILEPLAYER_CV** Handle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
bool result = (*Handle)->IsPaused();
|
||||
if (result) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error checking if file player is paused: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int IsFileCVPlayerRunning(ANSCENTER::ANSFILEPLAYER_CV** Handle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
bool result = (*Handle)->IsPlaying();
|
||||
if (result) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error checking if file player is running: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int IsFileCVPlayerRecording(ANSCENTER::ANSFILEPLAYER_CV** Handle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return -1;
|
||||
try {
|
||||
bool result = (*Handle)->IsRecording();
|
||||
if (result) return 1;
|
||||
else return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error checking if file player is recording: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) void SetFileCVPlayerAudioVolume(ANSCENTER::ANSFILEPLAYER_CV** Handle, int volume)
|
||||
{
|
||||
if (Handle == nullptr || *Handle == nullptr) return;
|
||||
try {
|
||||
(*Handle)->SetAudioVolume(volume);
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
extern "C" __declspec(dllexport) void EnableFileCVPlayerAudioVolume(ANSCENTER::ANSFILEPLAYER_CV** Handle, int status)
|
||||
{
|
||||
if (Handle == nullptr || *Handle == nullptr) return;
|
||||
try {
|
||||
bool audioStatus = false;
|
||||
if (status == 1)audioStatus = true;
|
||||
(*Handle)->EnableAudio(audioStatus);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error enabling audio for file player: " << e.what() << std::endl;
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
extern "C" __declspec(dllexport) void SetFileCVPlayerImageRotation(ANSCENTER::ANSFILEPLAYER_CV** Handle, double rotationAngle) {
|
||||
if (Handle == nullptr || *Handle == nullptr) return;
|
||||
try {
|
||||
(*Handle)->SetImageRotate(rotationAngle);
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int SetBBoxFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** 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 (const std::exception& e) {
|
||||
std::cerr << "Error setting bounding box for file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
extern "C" __declspec(dllexport) int SetCropFlagFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** 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 (const std::exception& e) {
|
||||
std::cerr << "Error setting crop flag for file player: " << e.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
115
modules/ANSCV/ANSFilePlayer_CV.h
Normal file
115
modules/ANSCV/ANSFilePlayer_CV.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef ANSFILEPLAYER_H
|
||||
#define ANSFILEPLAYER_H
|
||||
#define ANSFILEPLAYER_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#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 "file_player.h"
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <turbojpeg.h>
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
class ANSFILEPLAYER_API ANSFILEPLAYER_CV
|
||||
{
|
||||
protected:
|
||||
cv::VideoCapture cap;
|
||||
int _resWidth;
|
||||
int _resHeight;
|
||||
int64_t _totalFrames;
|
||||
int64_t _currentFrame;
|
||||
int64_t _previousPTS;
|
||||
double _fps;
|
||||
bool _isPlaying;
|
||||
std::string _lastJpegImage;
|
||||
//int64_t _previousPTS;
|
||||
cv::Mat _previousImage;
|
||||
bool m_bPaused;
|
||||
int _imageRotateDeg = 0;
|
||||
std::string _url;
|
||||
cv::Rect _bbox;
|
||||
bool _crop;
|
||||
std::recursive_mutex _mutex;
|
||||
std::shared_ptr<std::atomic<bool>> _stopThread;
|
||||
std::shared_ptr<std::thread> _timerThread;
|
||||
std::mutex _cvMutex;
|
||||
std::condition_variable _cv;
|
||||
//EngineType engineType;
|
||||
//// Initialize nvJPEG structures
|
||||
//nvjpegHandle_t nv_handle;
|
||||
//nvjpegEncoderState_t nv_enc_state;
|
||||
//nvjpegEncoderParams_t nv_enc_params;
|
||||
//cudaStream_t stream;
|
||||
tjhandle _jpegCompressor;
|
||||
|
||||
public:
|
||||
ANSFILEPLAYER_CV();
|
||||
~ANSFILEPLAYER_CV() noexcept;
|
||||
[[nodiscard]] bool Init(std::string licenseKey, std::string url);
|
||||
[[nodiscard]] bool Setup();
|
||||
[[nodiscard]] bool Reconnect();
|
||||
[[nodiscard]] bool Start();
|
||||
[[nodiscard]] bool IsPaused();
|
||||
[[nodiscard]] bool IsPlaying();
|
||||
[[nodiscard]] bool IsRecording();
|
||||
void SetBBox(cv::Rect bbox);
|
||||
void SetCrop(bool crop);
|
||||
[[nodiscard]] bool Stop();
|
||||
void Destroy();
|
||||
void EnableAudio(bool status);
|
||||
void SetAudioVolume(int volume);
|
||||
void SetImageRotate(int mode) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_imageRotateDeg = mode;
|
||||
if (mode > 360) _imageRotateDeg = 360;
|
||||
};
|
||||
[[nodiscard]] cv::Mat GetImage(int& width, int& height, int64_t& pts);
|
||||
[[nodiscard]] std::string MatToBinaryData(const cv::Mat& image);
|
||||
[[nodiscard]] std::string GetJpegStringImage(int& width, int& height, int64_t& pts);
|
||||
public:
|
||||
void CheckLicense();
|
||||
SPDLogger& _logger = SPDLogger::GetInstance("ANSFilePlayer_CV", false);
|
||||
bool _licenseValid{ false };
|
||||
//std::once_flag licenseOnceFlag;
|
||||
std::string _licenseKey;
|
||||
private:
|
||||
std::string encodeJpegString(const cv::Mat& img, int quality = 80);
|
||||
//std::string encodeMatToJpegWithNvJPEG(const cv::Mat& inputMat, int quality = 80);
|
||||
//void uploadPlanarBGRToGPU(const cv::Mat& inputMat, unsigned char** data);
|
||||
void StartTimer(std::shared_ptr<std::atomic<bool>> stopFlag, int interval_ms);
|
||||
void StopTimerThread();
|
||||
};
|
||||
}
|
||||
extern "C" __declspec(dllexport) int CreateANSFileCVPlayerHandle(ANSCENTER::ANSFILEPLAYER_CV** Handle, const char* licenseKey, const char* url);
|
||||
extern "C" __declspec(dllexport) int ReleaseANSFileCVPlayerHandle(ANSCENTER::ANSFILEPLAYER_CV** Handle);
|
||||
extern "C" __declspec(dllexport) int GetFileCVPlayerImage(ANSCENTER::ANSFILEPLAYER_CV** Handle, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetFileCVPlayerStrImage(ANSCENTER::ANSFILEPLAYER_CV** Handle, int& width, int& height, int64_t& timeStamp, std::string& jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetFileCVPlayerCVImage(ANSCENTER::ANSFILEPLAYER_CV** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image);
|
||||
extern "C" __declspec(dllexport) int StartFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** Handle);
|
||||
extern "C" __declspec(dllexport) int ReconnectFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** Handle);
|
||||
extern "C" __declspec(dllexport) int StopFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** Handle);
|
||||
extern "C" __declspec(dllexport) int IsFileCVPlayerPaused(ANSCENTER::ANSFILEPLAYER_CV** Handle);
|
||||
extern "C" __declspec(dllexport) int IsFileCVPlayerRunning(ANSCENTER::ANSFILEPLAYER_CV** Handle);
|
||||
extern "C" __declspec(dllexport) int IsFileCVPlayerRecording(ANSCENTER::ANSFILEPLAYER_CV** Handle);
|
||||
extern "C" __declspec(dllexport) void SetFileCVPlayerAudioVolume(ANSCENTER::ANSFILEPLAYER_CV** Handle, int volume);
|
||||
extern "C" __declspec(dllexport) void EnableFileCVPlayerAudioVolume(ANSCENTER::ANSFILEPLAYER_CV** Handle, int status);
|
||||
extern "C" __declspec(dllexport) void SetFileCVPlayerImageRotation(ANSCENTER::ANSFILEPLAYER_CV** Handle, double rotationAngle);
|
||||
extern "C" __declspec(dllexport) int SetBBoxFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** Handle, int x, int y, int width, int height);
|
||||
extern "C" __declspec(dllexport) int SetCropFlagFileCVPlayer(ANSCENTER::ANSFILEPLAYER_CV** Handle, int cropFlag);
|
||||
|
||||
|
||||
#endif
|
||||
212
modules/ANSCV/ANSGpuFrameOps.h
Normal file
212
modules/ANSCV/ANSGpuFrameOps.h
Normal file
@@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
// ANSGpuFrameOps.h — FFmpeg-aware convenience functions for ANSGpuFrameRegistry.
|
||||
//
|
||||
// This header requires FFmpeg headers (libavutil/frame.h) and provides
|
||||
// typed attach/invalidate/remove operations that handle av_frame_clone/free.
|
||||
//
|
||||
// NEW DESIGN: Instead of storing AVFrame* references (which lock NVDEC surfaces),
|
||||
// we snapshot the CPU NV12 planes into malloc'd buffers and release the AVFrames
|
||||
// immediately. This prevents decoder surface pool exhaustion when many clones
|
||||
// hold references to the same frame.
|
||||
//
|
||||
// Include this in ANSCV/ANSRTSP (which link FFmpeg). For projects without
|
||||
// FFmpeg (ANSODEngine), include ANSGpuFrameRegistry.h directly and use
|
||||
// gpu_frame_lookup() + the GpuFrameData plane pointers.
|
||||
|
||||
#include "ANSGpuFrameRegistry.h"
|
||||
|
||||
extern "C" {
|
||||
#include "libavutil/frame.h"
|
||||
}
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace anscv_gpu_ops {
|
||||
namespace detail {
|
||||
|
||||
// Snapshot NV12 Y and UV planes from an AVFrame into malloc'd buffers.
|
||||
// Returns true on success. Caller owns the output buffers.
|
||||
inline bool snapshotNV12Planes(const AVFrame* nv12,
|
||||
uint8_t*& outY, int& outYLinesize,
|
||||
uint8_t*& outUV, int& outUVLinesize,
|
||||
int& outWidth, int& outHeight) {
|
||||
if (!nv12 || !nv12->data[0] || !nv12->data[1])
|
||||
return false;
|
||||
|
||||
outWidth = nv12->width;
|
||||
outHeight = nv12->height;
|
||||
outYLinesize = nv12->width; // Packed (no alignment padding)
|
||||
outUVLinesize = nv12->width; // UV interleaved: width bytes per row
|
||||
|
||||
size_t yBytes = static_cast<size_t>(outYLinesize) * outHeight;
|
||||
size_t uvBytes = static_cast<size_t>(outUVLinesize) * (outHeight / 2);
|
||||
|
||||
outY = static_cast<uint8_t*>(std::malloc(yBytes));
|
||||
outUV = static_cast<uint8_t*>(std::malloc(uvBytes));
|
||||
|
||||
if (!outY || !outUV) {
|
||||
std::free(outY);
|
||||
std::free(outUV);
|
||||
outY = nullptr;
|
||||
outUV = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy line-by-line (source may have padding via linesize > width)
|
||||
const int srcYLinesize = nv12->linesize[0];
|
||||
const int srcUVLinesize = nv12->linesize[1];
|
||||
|
||||
for (int row = 0; row < outHeight; ++row) {
|
||||
std::memcpy(outY + row * outYLinesize,
|
||||
nv12->data[0] + row * srcYLinesize,
|
||||
outWidth);
|
||||
}
|
||||
for (int row = 0; row < outHeight / 2; ++row) {
|
||||
std::memcpy(outUV + row * outUVLinesize,
|
||||
nv12->data[1] + row * srcUVLinesize,
|
||||
outWidth);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace anscv_gpu_ops
|
||||
|
||||
// Attach NV12/YUV frame keyed by cv::Mat* pointer.
|
||||
// Snapshots CPU NV12 planes into owned malloc'd buffers, then releases the AVFrame.
|
||||
// TAKES OWNERSHIP of nv12 — caller must NOT av_frame_free after this call.
|
||||
inline void gpu_frame_attach(cv::Mat* mat, AVFrame* nv12, int gpuIdx, int64_t pts) {
|
||||
if (!mat || !nv12) return;
|
||||
|
||||
GpuFrameData data{};
|
||||
data.gpuIndex = gpuIdx;
|
||||
data.pts = pts;
|
||||
data.pixelFormat = nv12->format;
|
||||
data.width = nv12->width;
|
||||
data.height = nv12->height;
|
||||
|
||||
// Snapshot NV12 planes to owned buffers
|
||||
bool ok = anscv_gpu_ops::detail::snapshotNV12Planes(
|
||||
nv12,
|
||||
data.cpuYPlane, data.cpuYLinesize,
|
||||
data.cpuUvPlane, data.cpuUvLinesize,
|
||||
data.width, data.height);
|
||||
|
||||
// Keep legacy pointers for backward compat during transition
|
||||
data.yPlane = data.cpuYPlane;
|
||||
data.uvPlane = data.cpuUvPlane;
|
||||
data.yLinesize = data.cpuYLinesize;
|
||||
data.uvLinesize = data.cpuUvLinesize;
|
||||
|
||||
// Store AVFrame for legacy cleanup (will be freed by drain_pending)
|
||||
data.avframe = nv12;
|
||||
|
||||
void* old = ANSGpuFrameRegistry::instance().attach(mat, std::move(data));
|
||||
if (old) {
|
||||
AVFrame* oldFrame = static_cast<AVFrame*>(old);
|
||||
av_frame_free(&oldFrame);
|
||||
}
|
||||
|
||||
// Free stale entries evicted by TTL or previous attach
|
||||
auto pending = ANSGpuFrameRegistry::instance().drain_pending();
|
||||
for (void* p : pending) {
|
||||
AVFrame* stale = static_cast<AVFrame*>(p);
|
||||
av_frame_free(&stale);
|
||||
}
|
||||
}
|
||||
|
||||
// Attach CUDA HW frame — keeps CUDA device pointers for zero-copy inference.
|
||||
// TAKES OWNERSHIP of cudaFrame AND cpuNV12 — caller must NOT av_frame_free after.
|
||||
//
|
||||
// Primary path: yPlane/uvPlane point to CUDA device pointers from the cloned
|
||||
// AVFrame (data[0]/data[1]). The cloned AVFrame keeps the NVDEC surface alive
|
||||
// until gpu_frame_remove() is called after inference. With 4 cameras each
|
||||
// holding ~1 surface, this uses 4 of NVDEC's 25-32 surface pool — safe.
|
||||
//
|
||||
// Fallback: cpuYPlane/cpuUvPlane hold CPU-side NV12 snapshot for cross-GPU
|
||||
// inference (when decode GPU != inference GPU, CUDA device ptrs aren't
|
||||
// accessible from another GPU context).
|
||||
inline void gpu_frame_attach_cuda(cv::Mat* mat, AVFrame* cudaFrame, int gpuIdx, int64_t pts,
|
||||
AVFrame* cpuNV12 = nullptr) {
|
||||
if (!mat || !cudaFrame) return;
|
||||
|
||||
GpuFrameData data{};
|
||||
data.gpuIndex = gpuIdx;
|
||||
data.pts = pts;
|
||||
data.width = cudaFrame->width;
|
||||
data.height = cudaFrame->height;
|
||||
data.pixelFormat = 23; // AV_PIX_FMT_NV12 — the underlying sw_format
|
||||
|
||||
// Primary: CUDA device pointers from NVDEC (zero-copy on same GPU)
|
||||
data.isCudaDevicePtr = true;
|
||||
data.yPlane = cudaFrame->data[0]; // CUDA device ptr: Y plane
|
||||
data.uvPlane = cudaFrame->data[1]; // CUDA device ptr: UV plane
|
||||
data.yLinesize = cudaFrame->linesize[0];
|
||||
data.uvLinesize = cudaFrame->linesize[1];
|
||||
|
||||
// Fallback: snapshot CPU NV12 for cross-GPU inference
|
||||
if (cpuNV12) {
|
||||
anscv_gpu_ops::detail::snapshotNV12Planes(
|
||||
cpuNV12,
|
||||
data.cpuYPlane, data.cpuYLinesize,
|
||||
data.cpuUvPlane, data.cpuUvLinesize,
|
||||
data.width, data.height);
|
||||
}
|
||||
|
||||
// Store AVFrames for cleanup (cudaFrame keeps NVDEC surface alive)
|
||||
data.avframe = cudaFrame;
|
||||
data.cpuAvframe = cpuNV12;
|
||||
|
||||
void* old = ANSGpuFrameRegistry::instance().attach(mat, std::move(data));
|
||||
if (old) {
|
||||
AVFrame* oldFrame = static_cast<AVFrame*>(old);
|
||||
av_frame_free(&oldFrame);
|
||||
}
|
||||
|
||||
auto pending = ANSGpuFrameRegistry::instance().drain_pending();
|
||||
for (void* p : pending) {
|
||||
AVFrame* stale = static_cast<AVFrame*>(p);
|
||||
av_frame_free(&stale);
|
||||
}
|
||||
}
|
||||
|
||||
// Release entry by cv::Mat* and free any returned AVFrames. Safe if not in map (no-op).
|
||||
inline void gpu_frame_remove(cv::Mat* mat) {
|
||||
if (!mat) return;
|
||||
|
||||
ANSGpuFrameRegistry::instance().release(mat);
|
||||
|
||||
// Free any AVFrames that became pending from this release or prior eviction
|
||||
auto pending = ANSGpuFrameRegistry::instance().drain_pending();
|
||||
for (void* p : pending) {
|
||||
AVFrame* stale = static_cast<AVFrame*>(p);
|
||||
av_frame_free(&stale);
|
||||
}
|
||||
|
||||
// Free any GPU device pointers that became pending
|
||||
auto gpuPending = gpu_frame_drain_gpu_pending();
|
||||
// NOTE: cudaFree requires CUDA context — caller must be on a CUDA-capable thread.
|
||||
// If not, these will leak. In practice, gpu_frame_remove is called from ANSCV
|
||||
// camera threads which do have CUDA context.
|
||||
// For safety, we skip cudaFree here and let NV12PreprocessHelper handle it.
|
||||
// The GPU pointers are tracked in the budget and will be accounted for.
|
||||
(void)gpuPending;
|
||||
}
|
||||
|
||||
// Alias for remove — used in ANSCV mutating functions to drop stale GPU data.
|
||||
inline void gpu_frame_invalidate(cv::Mat* mat) {
|
||||
gpu_frame_remove(mat);
|
||||
}
|
||||
|
||||
// Run TTL eviction + drain pending. Call periodically from camera threads.
|
||||
inline void gpu_frame_evict_stale() {
|
||||
ANSGpuFrameRegistry::instance().evictStaleFrames();
|
||||
|
||||
auto pending = ANSGpuFrameRegistry::instance().drain_pending();
|
||||
for (void* p : pending) {
|
||||
AVFrame* stale = static_cast<AVFrame*>(p);
|
||||
av_frame_free(&stale);
|
||||
}
|
||||
}
|
||||
25
modules/ANSCV/ANSGpuFrameRegistry.cpp
Normal file
25
modules/ANSCV/ANSGpuFrameRegistry.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// ANSGpuFrameRegistry.cpp — Process-wide singleton, compiled into ANSCV.dll.
|
||||
//
|
||||
// On Windows, header-only singletons (static local in inline function) create
|
||||
// separate instances in each DLL. Since gpu_frame_attach() runs in ANSCV.dll
|
||||
// and gpu_frame_lookup() runs in ANSODEngine.dll, we need a single shared
|
||||
// instance. This file:
|
||||
// 1. Defines resolveProcessWide() which owns the canonical singleton.
|
||||
// 2. Exports a C function so other DLLs can find it via GetProcAddress.
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#include "ANSGpuFrameRegistry.h"
|
||||
|
||||
// ANSCV.dll owns the process-wide singleton.
|
||||
ANSGpuFrameRegistry* ANSGpuFrameRegistry::resolveProcessWide() {
|
||||
static ANSGpuFrameRegistry reg;
|
||||
return ®
|
||||
}
|
||||
|
||||
// Exported so other DLLs (ANSODEngine, etc.) can find this instance at runtime.
|
||||
extern "C" __declspec(dllexport)
|
||||
ANSGpuFrameRegistry* ANSGpuFrameRegistry_GetInstance() {
|
||||
return &ANSGpuFrameRegistry::instance();
|
||||
}
|
||||
1175
modules/ANSCV/ANSMJPEG.cpp
Normal file
1175
modules/ANSCV/ANSMJPEG.cpp
Normal file
File diff suppressed because it is too large
Load Diff
111
modules/ANSCV/ANSMJPEG.h
Normal file
111
modules/ANSCV/ANSMJPEG.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef ANSMJPEG_H
|
||||
#define ANSMJPEG_H
|
||||
#define ANSMJPEG_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#include "sys_inc.h"
|
||||
#include "http_mjpeg_cln.h"
|
||||
#include "hqueue.h"
|
||||
#include "http.h"
|
||||
#include "http_parse.h"
|
||||
#include "video_decoder.h"
|
||||
#include "audio_decoder.h"
|
||||
#include "http_mjpeg_player.h"
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
class ANSMJPEG_API ANSMJPEGClient
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<CHttpMjpegPlayer> _playerClient = std::make_unique<CHttpMjpegPlayer>();
|
||||
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;
|
||||
std::recursive_mutex _mutex;
|
||||
public:
|
||||
ANSMJPEGClient();
|
||||
~ANSMJPEGClient() 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 Pause();
|
||||
[[nodiscard]] bool IsPaused();
|
||||
[[nodiscard]] bool IsPlaying();
|
||||
[[nodiscard]] bool IsRecording();
|
||||
[[nodiscard]] bool Stop();
|
||||
void Destroy();
|
||||
void EnableAudio(bool status);
|
||||
void SetAudioVolume(int volume);
|
||||
bool areImagesIdentical(const cv::Mat& img1, const cv::Mat& img2);
|
||||
[[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);
|
||||
void SetImageRotate(int mode) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_imageRotateDeg = mode;
|
||||
if (mode > 360) _imageRotateDeg = 360;
|
||||
};
|
||||
void SetBBox(cv::Rect bbox);
|
||||
void SetCrop(bool crop);
|
||||
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)
|
||||
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("ANSMJPEG", false);
|
||||
bool _licenseValid{ false };
|
||||
std::string _licenseKey;
|
||||
//std::once_flag licenseOnceFlag;
|
||||
|
||||
};
|
||||
}
|
||||
extern "C" __declspec(dllexport) int CreateANSMJPEGHandle(ANSCENTER::ANSMJPEGClient * *Handle, const char* licenseKey, const char* username, const char* password, const char* url);
|
||||
extern "C" __declspec(dllexport) int ReleaseANSMJPEGHandle(ANSCENTER::ANSMJPEGClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int GetMJPEGImage(ANSCENTER::ANSMJPEGClient * *Handle, int& width, int& height, int64_t & timeStamp, LStrHandle jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetMJPEGStrImage(ANSCENTER::ANSMJPEGClient * *Handle, int& width, int& height, int64_t & timeStamp, std::string & jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetMJPEGCVImage(ANSCENTER::ANSMJPEGClient** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image);
|
||||
|
||||
extern "C" __declspec(dllexport) int StartMJPEG(ANSCENTER::ANSMJPEGClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int ReconnectMJPEG(ANSCENTER::ANSMJPEGClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int StopMJPEG(ANSCENTER::ANSMJPEGClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int PauseMJPEG(ANSCENTER::ANSMJPEGClient** Handle);
|
||||
|
||||
extern "C" __declspec(dllexport) int IsMJPEGPaused(ANSCENTER::ANSMJPEGClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsMJPEGRunning(ANSCENTER::ANSMJPEGClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsMJPEGRecording(ANSCENTER::ANSMJPEGClient * *Handle);
|
||||
extern "C" __declspec(dllexport) void SetMJPEGAudioVolume(ANSCENTER::ANSMJPEGClient * *Handle, int volume);
|
||||
extern "C" __declspec(dllexport) void EnableMJPEGAudioVolume(ANSCENTER::ANSMJPEGClient * *Handle, int status);
|
||||
extern "C" __declspec(dllexport) void SetMJPEGImageRotation(ANSCENTER::ANSMJPEGClient * *Handle, double rotationAngle);
|
||||
extern "C" __declspec(dllexport) int SetBBoxMJPEG(ANSCENTER::ANSMJPEGClient** Handle, int x, int y, int width, int height);
|
||||
extern "C" __declspec(dllexport) int SetCropFlagMJPEG(ANSCENTER::ANSMJPEGClient** Handle, int cropFlag);
|
||||
extern "C" __declspec(dllexport) void SetMJPEGMaxHWDecoders(int maxDecoders);
|
||||
extern "C" __declspec(dllexport) int AutoConfigureMJPEGHWDecoders(int maxPerGpuOverride);
|
||||
extern "C" __declspec(dllexport) void SetMJPEGHWDecoding(ANSCENTER::ANSMJPEGClient** Handle, int hwMode, int preferredGpu = -1);
|
||||
extern "C" __declspec(dllexport) int IsMJPEGHWDecodingActive(ANSCENTER::ANSMJPEGClient** Handle);
|
||||
extern "C" __declspec(dllexport) int GetMJPEGHWDecodingGpuIndex(ANSCENTER::ANSMJPEGClient** Handle);
|
||||
extern "C" __declspec(dllexport) void SetMJPEGImageQuality(ANSCENTER::ANSMJPEGClient** Handle, int mode);
|
||||
extern "C" __declspec(dllexport) void SetMJPEGDisplayResolution(ANSCENTER::ANSMJPEGClient** Handle, int width, int height);
|
||||
#endif
|
||||
116
modules/ANSCV/ANSMatRegistry.h
Normal file
116
modules/ANSCV/ANSMatRegistry.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
// ANSMatRegistry.h — Thread-safe cv::Mat* pointer registry.
|
||||
// Prevents double-free crashes when LabVIEW calls ReleaseImage on a pointer
|
||||
// that a stream source (RTSP, VideoPlayer, etc.) has already freed and replaced.
|
||||
//
|
||||
// Every cv::Mat* allocated through these functions is tracked in a global set.
|
||||
// Deletion only proceeds if the pointer is still in the set, guaranteeing each
|
||||
// pointer is freed exactly once regardless of which thread calls delete first.
|
||||
//
|
||||
// Usage:
|
||||
// Allocate: cv::Mat* p = anscv_mat_new(std::move(img));
|
||||
// Free: anscv_mat_delete(&p); // safe, no-op if already freed
|
||||
// Swap: anscv_mat_replace(&p, std::move(newImg)); // atomic old-delete + new-alloc
|
||||
|
||||
#include <opencv2/core/mat.hpp>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
#include "ANSGpuFrameOps.h"
|
||||
|
||||
namespace anscv {
|
||||
namespace detail {
|
||||
|
||||
inline std::mutex& registry_mutex() {
|
||||
static std::mutex m;
|
||||
return m;
|
||||
}
|
||||
|
||||
inline std::unordered_set<cv::Mat*>& registry() {
|
||||
static std::unordered_set<cv::Mat*> s;
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace anscv
|
||||
|
||||
// Allocate a new cv::Mat on the heap and register it.
|
||||
inline cv::Mat* anscv_mat_new(cv::Mat&& src) {
|
||||
cv::Mat* p = new cv::Mat(std::move(src));
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(anscv::detail::registry_mutex());
|
||||
anscv::detail::registry().insert(p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
inline cv::Mat* anscv_mat_new(const cv::Mat& src) {
|
||||
cv::Mat* p = new cv::Mat(src);
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(anscv::detail::registry_mutex());
|
||||
anscv::detail::registry().insert(p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// Delete a cv::Mat* only if it is still in the registry.
|
||||
// Returns true if deleted, false if pointer was null or already freed.
|
||||
// Thread-safe: the pointer is removed from the registry AND nulled AND deleted
|
||||
// all inside the lock, so no other thread can race on the same pointer.
|
||||
inline bool anscv_mat_delete(cv::Mat** pp) {
|
||||
if (!pp || !(*pp))
|
||||
return false;
|
||||
cv::Mat* toDelete = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(anscv::detail::registry_mutex());
|
||||
// Re-check under lock — another thread may have nulled *pp
|
||||
if (!*pp)
|
||||
return false;
|
||||
auto& reg = anscv::detail::registry();
|
||||
auto it = reg.find(*pp);
|
||||
if (it == reg.end()) {
|
||||
// Not in registry — already freed by another thread.
|
||||
*pp = nullptr;
|
||||
return false;
|
||||
}
|
||||
toDelete = *pp;
|
||||
reg.erase(it);
|
||||
*pp = nullptr; // Null the caller's pointer while still under lock
|
||||
}
|
||||
// Release GPU frame data (refcount--, frees NV12+GPU cache when refcount=0)
|
||||
gpu_frame_remove(toDelete);
|
||||
// Safe to delete outside lock: pointer is removed from registry and
|
||||
// *pp is nullptr, so no other thread can reach toDelete.
|
||||
delete toDelete;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Atomic replace: delete old image (if still registered), allocate new, assign to *pp.
|
||||
// The old pointer is only deleted if it is in the registry (prevents double-free
|
||||
// when LabVIEW already freed it via anscv_mat_delete).
|
||||
inline void anscv_mat_replace(cv::Mat** pp, cv::Mat&& newImg) {
|
||||
cv::Mat* newPtr = new cv::Mat(std::move(newImg));
|
||||
cv::Mat* toDelete = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(anscv::detail::registry_mutex());
|
||||
auto& reg = anscv::detail::registry();
|
||||
if (pp && *pp) {
|
||||
auto it = reg.find(*pp);
|
||||
if (it != reg.end()) {
|
||||
toDelete = *pp;
|
||||
reg.erase(it);
|
||||
}
|
||||
// If not in registry, another thread already freed it — skip delete
|
||||
}
|
||||
*pp = newPtr;
|
||||
reg.insert(newPtr);
|
||||
}
|
||||
if (toDelete) {
|
||||
// Release GPU frame ref for old Mat (safe with refcounting —
|
||||
// only frees NV12/GPU cache when refcount reaches 0)
|
||||
gpu_frame_remove(toDelete);
|
||||
delete toDelete;
|
||||
}
|
||||
|
||||
// Periodically run TTL eviction (piggybacked on camera thread activity)
|
||||
gpu_frame_evict_stale();
|
||||
}
|
||||
5106
modules/ANSCV/ANSOpenCV.cpp
Normal file
5106
modules/ANSCV/ANSOpenCV.cpp
Normal file
File diff suppressed because it is too large
Load Diff
164
modules/ANSCV/ANSOpenCV.h
Normal file
164
modules/ANSCV/ANSOpenCV.h
Normal file
@@ -0,0 +1,164 @@
|
||||
#ifndef ANSOPENCV_H
|
||||
#define ANSOPENCV_H
|
||||
#define ANSOPENCV_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
#define MUTEX_TIMEOUT_MS 45000
|
||||
namespace fs = std::filesystem;
|
||||
namespace ANSCENTER
|
||||
{
|
||||
std::string CompressJpegToString(const cv::Mat& image, int quality);
|
||||
struct DetectionObject
|
||||
{
|
||||
int classId{ 0 };
|
||||
int trackId{ 0 };
|
||||
std::string className{};
|
||||
float confidence{ 0.0 };
|
||||
cv::Rect box{};
|
||||
cv::Mat mask{}; //Json string mask ="point1.x,point1.y,...."
|
||||
std::vector<float> kps{}; // Pose exsimate keypoint
|
||||
std::string extraInfo; // More information such as facial recognition
|
||||
};
|
||||
|
||||
class TurboJpegCompressor {
|
||||
public:
|
||||
TurboJpegCompressor();
|
||||
~TurboJpegCompressor() noexcept;
|
||||
// Delete copy constructor and assignment operator
|
||||
TurboJpegCompressor(const TurboJpegCompressor&) = delete;
|
||||
TurboJpegCompressor& operator=(const TurboJpegCompressor&) = delete;
|
||||
// Your original logic with minimal optimizations
|
||||
[[nodiscard]] std::string compress(const cv::Mat& image, int quality);
|
||||
private:
|
||||
void* _handle = nullptr;
|
||||
unsigned char* _buffer = nullptr;
|
||||
unsigned long _bufferSize = 0;
|
||||
};
|
||||
/// <summary>
|
||||
/// // ANSOPENCV class provides various image processing functionalities using OpenCV and ANS Center SDK.
|
||||
/// </summary>
|
||||
class ANSOPENCV_API ANSOPENCV
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] bool Init(std::string licenseKey);
|
||||
void ImageResize(const cv::Mat& inputFrame, int width, int height, cv::Mat& outputFrame);
|
||||
void ImageResizeWithRatio(const cv::Mat& inputFrame, int width, cv::Mat& outputFrame);
|
||||
[[nodiscard]] cv::Mat BlurObjects(const cv::Mat& image, const std::vector<cv::Rect>& objects);
|
||||
[[nodiscard]] cv::Mat BlurBackground(const cv::Mat& image, const std::vector<cv::Rect>& objects);
|
||||
[[nodiscard]] cv::Mat ToGray(const cv::Mat& image);
|
||||
[[nodiscard]] cv::Mat ImageDenoise(const cv::Mat& image);
|
||||
[[nodiscard]] cv::Mat ImageRepair(const cv::Mat& image);
|
||||
[[nodiscard]] cv::Mat ImageCrop(const cv::Mat& image,const cv::Rect& ROI, int originalImageSize = 0);
|
||||
[[nodiscard]] cv::Mat ImageResizeV2(const cv::Mat& image, int resizeWidth, int orginalImageSize = 0);
|
||||
[[nodiscard]] std::string QRDecoder(const cv::Mat& image);
|
||||
[[nodiscard]] std::string QRDecoderWithBBox(const cv::Mat& image, int maxImageSize, const std::vector<cv::Rect>& bBox);
|
||||
|
||||
[[nodiscard]] std::string MatToBase64(const cv::Mat& image);
|
||||
[[nodiscard]] std::string MatToBinaryData(const cv::Mat& image);
|
||||
[[nodiscard]] std::vector<cv::Rect> GetBoundingBoxes(std::string strBBoxes);
|
||||
[[nodiscard]] std::string VectorDetectionToJsonString(const std::vector<DetectionObject>& dets);
|
||||
[[nodiscard]] std::string PatternMatches(cv::Mat& image, cv::Mat& templateImage, double threshold);
|
||||
[[nodiscard]] cv::Mat ImageDarkEnhancement(const cv::Mat& img, double brightnessScaleFactor=1.5);
|
||||
[[nodiscard]] cv::Mat ImageContrastEnhancement(const cv::Mat& img);
|
||||
[[nodiscard]] cv::Mat ImageWhiteBalance(const cv::Mat& img);
|
||||
|
||||
[[nodiscard]] cv::Mat RotateImage(const cv::Mat& image, double angle);
|
||||
[[nodiscard]] cv::Mat FlipImage(const cv::Mat& image, int flipCode);//flipCode = 0: Vertical flip (around the x-axis). flipCode = 1: Horizontal flip (around the y-axis). flipCode = -1: Both axes, flipping the image horizontally and vertically.
|
||||
[[nodiscard]] cv::Mat ShiftImage(const cv::Mat& image, int shiftX, int shiftY);
|
||||
[[nodiscard]] cv::Mat AddGaussianNoise(const cv::Mat& image, double mean, double stddev);
|
||||
[[nodiscard]] cv::Mat AddSaltAndPepperNoise(const cv::Mat& image, double amount);
|
||||
[[nodiscard]] cv::Mat AddSpeckleNoise(const cv::Mat& image, double stddev);
|
||||
|
||||
static void InitCameraNetwork();
|
||||
static void DeinitCameraNetwork();
|
||||
|
||||
static cv::Mat resizeImageToFit(const cv::Mat& inputImage, int maxWidth, int maxHeight, int& newWidth, int& newHeight);
|
||||
static bool resizeImage(cv::Mat& inputImage, int resizeWidth, int orginalImageSize=0);
|
||||
static bool cropImage(cv::Mat& inputImage, const cv::Rect& resizeROI, int originalImageSize=0);
|
||||
static bool ImagesToMP4(const std::string& imageFolder, const std::string& outputVideoPath, int targetDurationSec);
|
||||
|
||||
private:
|
||||
void CheckLicense();
|
||||
double CalculateIoU(const cv::Rect& box1, const cv::Rect& box2);
|
||||
void NonMaximumSuppression(std::vector<DetectionObject>& detectedObjects, double iouThreshold);
|
||||
std::string EncodeJpegString(const cv::Mat& img, int quality);
|
||||
SPDLogger& _logger = SPDLogger::GetInstance("ANSCV", false);
|
||||
std::string _licenseKey;
|
||||
std::recursive_mutex _mutex;
|
||||
|
||||
//std::once_flag licenseOnceFlag; // For one-time license check
|
||||
bool _licenseValid = false;
|
||||
public:
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
extern "C" __declspec(dllexport) int CreateANSCVHandle(ANSCENTER::ANSOPENCV **Handle, const char* licenseKey);
|
||||
extern "C" __declspec(dllexport) int ReleaseANSCVHandle(ANSCENTER::ANSOPENCV **Handle);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageResize(ANSCENTER::ANSOPENCV * *Handle,unsigned char* inputImage, unsigned int bufferLength, int width, int height, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageResizeWithRatio(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, int width,LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageToBase64(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageToGray(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageDenoise(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageRepair(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageAutoWhiteBalance(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageBrightEnhance(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, double brightnessScaleFactor, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageContrastEnhance(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageCrop(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, int x, int y, int width, int height, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_GetImageSize(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle imageSize);
|
||||
extern "C" __declspec(dllexport) int ANSCV_GetImageSizeFromImageFile(ANSCENTER::ANSOPENCV** Handle, const char* imageFilePath, LStrHandle imageSize);
|
||||
|
||||
extern "C" __declspec(dllexport) int ANSCV_BlurObjects(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, const char* strBboxes, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_BlurBackground(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, const char* strBboxes, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_QRDecoder(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, LStrHandle detectedQRText);
|
||||
extern "C" __declspec(dllexport) int ANSCV_QRDecoderCV(ANSCENTER::ANSOPENCV** Handle, const cv::Mat &image, std::string& detectedQRText);
|
||||
|
||||
extern "C" __declspec(dllexport) int ANSCV_PatternMatchs(ANSCENTER::ANSOPENCV * *Handle, unsigned char* inputImage, unsigned int bufferLength, const char* templateFilePath, double theshold, LStrHandle detectedMatchedLocations);
|
||||
|
||||
|
||||
extern "C" __declspec(dllexport) int ANSCV_RotateImage(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double angle, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_FlipImage(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, int flipCode, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ShiftImage(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, int shiftX, int shiftY, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_AddGaussianNoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double mean, double stddev, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_AddSaltAndPepperNoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double amount, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_AddSpeckleNoise(ANSCENTER::ANSOPENCV** Handle, unsigned char* inputImage, unsigned int bufferLength, double stddev, LStrHandle outputImage);
|
||||
|
||||
extern "C" __declspec(dllexport) void ANSCV_InitCameraResource();
|
||||
extern "C" __declspec(dllexport) void ANSCV_FreeCameraResource();
|
||||
extern "C" __declspec(dllexport) int ANSCV_ResizeImage_Static(unsigned char* inputImage, unsigned int bufferLength, int width,int height, int& newWidth, int&newHeight, LStrHandle outputImage);
|
||||
|
||||
|
||||
// Image reference management functions
|
||||
extern "C" __declspec(dllexport) int ANSCV_CloneImage_S(cv::Mat **imageIn, cv::Mat** imageOut);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ReleaseImage_S(cv::Mat** imageIn);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ReSizeImage_S(cv::Mat** imageIn, int width, int originalImageSize = 0);
|
||||
extern "C" __declspec(dllexport) int ANSCV_CropImage_S(cv::Mat** imageIn, int x, int y, int width, int height, int originalImageSize=0);
|
||||
extern "C" __declspec(dllexport) int ANSCV_GetImage_S(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_GetImage_CPP(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, std::string& outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_GetImageAndRemoveImgRef_S(cv::Mat** imageIn, int width, int quality, int& newWidth, int& newHeight, LStrHandle outputImage);
|
||||
extern "C" __declspec(dllexport) int ANSCV_GetImageInfo_S(cv::Mat** imageIn, int &width, int& height);
|
||||
extern "C" __declspec(dllexport) int ANSCV_CreateImageFromJpegString_S(unsigned char* inputImage, unsigned int bufferLength,cv::Mat** image);
|
||||
extern "C" __declspec(dllexport) int ANSCV_CreateImageFromFile_S(const char* imageFilePath, cv::Mat** image);
|
||||
// Pre-process image functions
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageAutoWhiteBalance_S(cv::Mat** imageIn);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageBrightEnhance_S(cv::Mat** imageIn, double brightnessScaleFactor);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageContrastEnhance_S(cv::Mat** imageIn);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageDenoise_S(cv::Mat** imageIn);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageRepair_S(cv::Mat** imageIn);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageToGray_S(cv::Mat** imageIn);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageRotate_S(cv::Mat** imageIn, double angle);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageFlip_S(cv::Mat** imageIn, int flipCode);
|
||||
// Post-process image functions
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageBlurObjects_S(cv::Mat** imageIn, const char* strBboxes);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageBlurBackground_S(cv::Mat** imageIn, const char* strBboxes);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImageQRDecoder_S(cv::Mat** imageIn, int maxImageWidth, const char* strBboxes, LStrHandle detectedQRText);
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImagePatternMatchs_S(cv::Mat** imageIn, const char* templateFilePath, double theshold, LStrHandle detectedMatchedLocations);
|
||||
|
||||
|
||||
extern "C" __declspec(dllexport) int ANSCV_ImagesToMP4_S(const char* imageFolder, const char* outputVideoPath, int targetDurationSec);
|
||||
|
||||
#endif
|
||||
1198
modules/ANSCV/ANSRTMP.cpp
Normal file
1198
modules/ANSCV/ANSRTMP.cpp
Normal file
File diff suppressed because it is too large
Load Diff
110
modules/ANSCV/ANSRTMP.h
Normal file
110
modules/ANSCV/ANSRTMP.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#ifndef ANSRTMP_H
|
||||
#define ANSRTMP_H
|
||||
#define ANSRTMP_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#include "sys_inc.h"
|
||||
#include "rtmp_cln.h"
|
||||
#include "hqueue.h"
|
||||
#include "http.h"
|
||||
#include "http_parse.h"
|
||||
#include "video_decoder.h"
|
||||
#include "audio_decoder.h"
|
||||
#include "rtmp_player.h"
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
class ANSRTMP_API ANSRTMPClient
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<CRtmpPlayer> _playerClient = std::make_unique<CRtmpPlayer>();
|
||||
|
||||
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;
|
||||
std::recursive_mutex _mutex;
|
||||
public:
|
||||
ANSRTMPClient();
|
||||
~ANSRTMPClient() 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 EnableAudio(bool status);
|
||||
void SetAudioVolume(int volume);
|
||||
bool areImagesIdentical(const cv::Mat& img1, const cv::Mat& img2);
|
||||
[[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);
|
||||
void SetImageRotate(int mode) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_imageRotateDeg = mode;
|
||||
if (mode > 360) _imageRotateDeg = 360;
|
||||
};
|
||||
void SetBBox(cv::Rect bbox);
|
||||
void SetCrop(bool crop);
|
||||
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)
|
||||
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("ANSRTMP", false);
|
||||
bool _licenseValid{ false };
|
||||
std::string _licenseKey;
|
||||
//std::once_flag licenseOnceFlag;
|
||||
|
||||
};
|
||||
}
|
||||
extern "C" __declspec(dllexport) int CreateANSRTMPHandle(ANSCENTER::ANSRTMPClient * *Handle, const char* licenseKey, const char* username, const char* password, const char* url);
|
||||
extern "C" __declspec(dllexport) int ReleaseANSRTMPHandle(ANSCENTER::ANSRTMPClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int GetRTMPImage(ANSCENTER::ANSRTMPClient * *Handle, int& width, int& height, int64_t & timeStamp, LStrHandle jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetRTMPtrImage(ANSCENTER::ANSRTMPClient * *Handle, int& width, int& height, int64_t & timeStamp, std::string & jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetRTMPCVImage(ANSCENTER::ANSRTMPClient** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image);
|
||||
extern "C" __declspec(dllexport) int StartRTMP(ANSCENTER::ANSRTMPClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int ReconnectRTMP(ANSCENTER::ANSRTMPClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int StopRTMP(ANSCENTER::ANSRTMPClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int PauseRTMP(ANSCENTER::ANSRTMPClient** Handle);
|
||||
extern "C" __declspec(dllexport) int IsRTMPPaused(ANSCENTER::ANSRTMPClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsRTMPRunning(ANSCENTER::ANSRTMPClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsRTMPRecording(ANSCENTER::ANSRTMPClient * *Handle);
|
||||
extern "C" __declspec(dllexport) void SetRTMPAudioVolume(ANSCENTER::ANSRTMPClient * *Handle, int volume);
|
||||
extern "C" __declspec(dllexport) void EnableRTMPAudioVolume(ANSCENTER::ANSRTMPClient * *Handle, int status);
|
||||
extern "C" __declspec(dllexport) void SetRTMPImageRotation(ANSCENTER::ANSRTMPClient * *Handle, double rotationAngle);
|
||||
extern "C" __declspec(dllexport) int SetBBoxRTMP(ANSCENTER::ANSRTMPClient** Handle, int x, int y, int width, int height);
|
||||
extern "C" __declspec(dllexport) int SetCropFlagRTMP(ANSCENTER::ANSRTMPClient** Handle, int cropFlag);
|
||||
extern "C" __declspec(dllexport) void SetRTMPMaxHWDecoders(int maxDecoders);
|
||||
extern "C" __declspec(dllexport) int AutoConfigureRTMPHWDecoders(int maxPerGpuOverride);
|
||||
extern "C" __declspec(dllexport) void SetRTMPHWDecoding(ANSCENTER::ANSRTMPClient** Handle, int hwMode, int preferredGpu = -1);
|
||||
extern "C" __declspec(dllexport) int IsRTMPHWDecodingActive(ANSCENTER::ANSRTMPClient** Handle);
|
||||
extern "C" __declspec(dllexport) int GetRTMPHWDecodingGpuIndex(ANSCENTER::ANSRTMPClient** Handle);
|
||||
extern "C" __declspec(dllexport) void SetRTMPImageQuality(ANSCENTER::ANSRTMPClient** Handle, int mode);
|
||||
extern "C" __declspec(dllexport) void SetRTMPDisplayResolution(ANSCENTER::ANSRTMPClient** Handle, int width, int height);
|
||||
#endif
|
||||
1512
modules/ANSCV/ANSRTSP.cpp
Normal file
1512
modules/ANSCV/ANSRTSP.cpp
Normal file
File diff suppressed because it is too large
Load Diff
111
modules/ANSCV/ANSRTSP.h
Normal file
111
modules/ANSCV/ANSRTSP.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef ANSRTSP_H
|
||||
#define ANSRTSP_H
|
||||
#define ANSRTSP_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#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 <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
class ANSRTSP_API ANSRTSPClient
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<CRtspPlayer> _playerClient = std::make_unique<CRtspPlayer>();
|
||||
|
||||
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;
|
||||
std::recursive_mutex _mutex;
|
||||
public:
|
||||
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<std::recursive_mutex> 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)
|
||||
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);
|
||||
#endif
|
||||
1221
modules/ANSCV/ANSSRT.cpp
Normal file
1221
modules/ANSCV/ANSSRT.cpp
Normal file
File diff suppressed because it is too large
Load Diff
110
modules/ANSCV/ANSSRT.h
Normal file
110
modules/ANSCV/ANSSRT.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#ifndef ANSSRT_H
|
||||
#define ANSSRT_H
|
||||
#define ANSSRT_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#include "sys_inc.h"
|
||||
#include "srt_cln.h"
|
||||
#include "hqueue.h"
|
||||
#include "http.h"
|
||||
#include "http_parse.h"
|
||||
#include "video_decoder.h"
|
||||
#include "audio_decoder.h"
|
||||
#include "srt_player.h"
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
class ANSSRT_API ANSSRTClient
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<CSrtPlayer> _playerClient = std::make_unique<CSrtPlayer>();
|
||||
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;
|
||||
std::recursive_mutex _mutex;
|
||||
public:
|
||||
ANSSRTClient();
|
||||
~ANSSRTClient() 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<std::recursive_mutex> 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)
|
||||
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("ANSSRT", false);
|
||||
bool _licenseValid{ false };
|
||||
std::string _licenseKey;
|
||||
//std::once_flag licenseOnceFlag;
|
||||
|
||||
};
|
||||
}
|
||||
extern "C" __declspec(dllexport) int CreateANSSRTHandle(ANSCENTER::ANSSRTClient * *Handle, const char* licenseKey, const char* username, const char* password, const char* url);
|
||||
extern "C" __declspec(dllexport) int ReleaseANSSRTHandle(ANSCENTER::ANSSRTClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int GetSRTImage(ANSCENTER::ANSSRTClient * *Handle, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetSRTStrImage(ANSCENTER::ANSSRTClient * *Handle, int& width, int& height, int64_t & timeStamp, std::string & jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetSRTCVImage(ANSCENTER::ANSSRTClient** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image);
|
||||
|
||||
extern "C" __declspec(dllexport) int StartSRT(ANSCENTER::ANSSRTClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int ReconnectSRT(ANSCENTER::ANSSRTClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int StopSRT(ANSCENTER::ANSSRTClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int PauseSRT(ANSCENTER::ANSSRTClient** Handle);
|
||||
extern "C" __declspec(dllexport) int IsSRTPaused(ANSCENTER::ANSSRTClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsSRTRunning(ANSCENTER::ANSSRTClient * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsSRTRecording(ANSCENTER::ANSSRTClient * *Handle);
|
||||
extern "C" __declspec(dllexport) void SetSRTAudioVolume(ANSCENTER::ANSSRTClient * *Handle, int volume);
|
||||
extern "C" __declspec(dllexport) void EnableSRTAudioVolume(ANSCENTER::ANSSRTClient * *Handle, int status);
|
||||
extern "C" __declspec(dllexport) void SetSRTImageRotation(ANSCENTER::ANSSRTClient * *Handle, double rotationAngle);
|
||||
extern "C" __declspec(dllexport) int SetBBoxSRT(ANSCENTER::ANSSRTClient** Handle, int x, int y, int width, int height);
|
||||
extern "C" __declspec(dllexport) int SetCropFlagSRT(ANSCENTER::ANSSRTClient** Handle, int cropFlag);
|
||||
extern "C" __declspec(dllexport) void SetSRTMaxHWDecoders(int maxDecoders);
|
||||
extern "C" __declspec(dllexport) int AutoConfigureSRTHWDecoders(int maxPerGpuOverride);
|
||||
extern "C" __declspec(dllexport) void SetSRTHWDecoding(ANSCENTER::ANSSRTClient** Handle, int hwMode, int preferredGpu = -1);
|
||||
extern "C" __declspec(dllexport) int IsSRTHWDecodingActive(ANSCENTER::ANSSRTClient** Handle);
|
||||
extern "C" __declspec(dllexport) int GetSRTHWDecodingGpuIndex(ANSCENTER::ANSSRTClient** Handle);
|
||||
extern "C" __declspec(dllexport) void SetSRTImageQuality(ANSCENTER::ANSSRTClient** Handle, int mode);
|
||||
extern "C" __declspec(dllexport) void SetSRTDisplayResolution(ANSCENTER::ANSSRTClient** Handle, int width, int height);
|
||||
#endif
|
||||
1158
modules/ANSCV/ANSVideoPlayer.cpp
Normal file
1158
modules/ANSCV/ANSVideoPlayer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
110
modules/ANSCV/ANSVideoPlayer.h
Normal file
110
modules/ANSCV/ANSVideoPlayer.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#ifndef ANSVIDEOPLAYER_H
|
||||
#define ANSVIDEOPLAYER_H
|
||||
#define ANSVIDEOPLAYER_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <turbojpeg.h>
|
||||
#include "file_player.h" // CFilePlayer (FFmpeg HW decode: CUDA/D3D11VA/DXVA2)
|
||||
#include "video_decoder.h" // HW_DECODING_AUTO, HWDecoderPool
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
class ANSVIDEOPLAYER_API ANSVIDEOPLAYER
|
||||
{
|
||||
protected:
|
||||
cv::VideoCapture cap;
|
||||
int _resWidth;
|
||||
int _resHeight;
|
||||
int64_t _totalFrames;
|
||||
int64_t _currentFrame;
|
||||
int64_t _previousPTS;
|
||||
double _fps;
|
||||
bool _isPlaying;
|
||||
std::string _lastJpegImage;
|
||||
cv::Mat _previousImage;
|
||||
bool m_bPaused;
|
||||
int _imageRotateDeg = 0;
|
||||
std::string _url;
|
||||
cv::Rect _bbox;
|
||||
bool _crop;
|
||||
std::recursive_mutex _mutex;
|
||||
std::mutex _cvMutex;
|
||||
std::condition_variable _cv;
|
||||
tjhandle _jpegCompressor;
|
||||
|
||||
public:
|
||||
// --- HW decode state (CFilePlayer composition) ---
|
||||
// Public: accessed by extern "C" GetVideoPlayerCVImage for NV12/CUDA registry attachment
|
||||
std::unique_ptr<CFilePlayer> _hwPlayer; // HW decode path (nullptr = CV fallback)
|
||||
bool _hwDecodeActive = false; // true when _hwPlayer init succeeded
|
||||
int _hwGpuIndex = -1; // GPU index for registry (-1 = CPU only)
|
||||
bool _hwCudaAccel = false; // true = NVIDIA CUDA zero-copy available
|
||||
bool _hwEOF = false; // true when video reached end of file
|
||||
int64_t _hwFrameCount = 0; // frame counter for EOF detection
|
||||
int64_t _hwLastPts = 0; // last video PTS for EOF wrap detection
|
||||
ANSVIDEOPLAYER();
|
||||
~ANSVIDEOPLAYER() noexcept;
|
||||
[[nodiscard]] bool Init(std::string licenseKey, std::string url);
|
||||
[[nodiscard]] bool Setup();
|
||||
[[nodiscard]] bool Reconnect();
|
||||
[[nodiscard]] bool Start();
|
||||
[[nodiscard]] bool IsPaused();
|
||||
[[nodiscard]] bool IsPlaying();
|
||||
[[nodiscard]] bool IsRecording();
|
||||
void SetBBox(cv::Rect bbox);
|
||||
void SetCrop(bool crop);
|
||||
[[nodiscard]] bool Stop();
|
||||
void Destroy();
|
||||
void EnableAudio(bool status);
|
||||
void SetAudioVolume(int volume);
|
||||
void SetImageRotate(int mode) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_imageRotateDeg = mode;
|
||||
if (mode > 360) _imageRotateDeg = 360;
|
||||
};
|
||||
[[nodiscard]] cv::Mat GetImage(int& width, int& height, int64_t& pts);
|
||||
[[nodiscard]] cv::Mat GetInferenceImage(); // Returns full-res frame for AI inference (no resize/rotation)
|
||||
[[nodiscard]] std::string MatToBinaryData(const cv::Mat& image);
|
||||
[[nodiscard]] std::string GetJpegStringImage(int& width, int& height, int64_t& pts);
|
||||
void SetDisplayResolution(int width, int height); // Set display output size; 0,0 = original (no resize)
|
||||
public:
|
||||
void CheckLicense();
|
||||
SPDLogger& _logger = SPDLogger::GetInstance("ANSVideoPlayer", false);
|
||||
bool _licenseValid{ false };
|
||||
//std::once_flag licenseOnceFlag;
|
||||
std::string _licenseKey;
|
||||
cv::Mat _inferenceCloneCurr; // Heap-cloned full-res (keeps data alive for registry)
|
||||
cv::Mat _inferenceClonePrev; // Previous clone (keeps data alive during ANSRTYOLO read)
|
||||
private:
|
||||
std::string encodeJpegString(const cv::Mat& img, int quality = 80);
|
||||
int _displayWidth = 0; // 0 = no resize (return original)
|
||||
int _displayHeight = 0;
|
||||
cv::Mat _inferenceImage; // Full-res frame for AI inference
|
||||
};
|
||||
}
|
||||
extern "C" __declspec(dllexport) int CreateANSVideoPlayerHandle(ANSCENTER::ANSVIDEOPLAYER** Handle, const char* licenseKey, const char* url);
|
||||
extern "C" __declspec(dllexport) int ReleaseANSVideoPlayerHandle(ANSCENTER::ANSVIDEOPLAYER** Handle);
|
||||
extern "C" __declspec(dllexport) int GetVideoPlayerImage(ANSCENTER::ANSVIDEOPLAYER** Handle, int& width, int& height, int64_t& timeStamp, LStrHandle jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetVideoPlayerStrImage(ANSCENTER::ANSVIDEOPLAYER** Handle, int& width, int& height, int64_t& timeStamp, std::string& jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetVideoPlayerCVImage(ANSCENTER::ANSVIDEOPLAYER** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image);
|
||||
extern "C" __declspec(dllexport) int StartVideoPlayer(ANSCENTER::ANSVIDEOPLAYER** Handle);
|
||||
extern "C" __declspec(dllexport) int ReconnectVideoPlayer(ANSCENTER::ANSVIDEOPLAYER** Handle);
|
||||
extern "C" __declspec(dllexport) int StopVideoPlayer(ANSCENTER::ANSVIDEOPLAYER** Handle);
|
||||
extern "C" __declspec(dllexport) int IsVideoPlayerPaused(ANSCENTER::ANSVIDEOPLAYER** Handle);
|
||||
extern "C" __declspec(dllexport) int IsVidedoPlayerRunning(ANSCENTER::ANSVIDEOPLAYER** Handle);
|
||||
extern "C" __declspec(dllexport) int IsVideoPlayerRecording(ANSCENTER::ANSVIDEOPLAYER** Handle);
|
||||
extern "C" __declspec(dllexport) void SetVideoPlayerAudioVolume(ANSCENTER::ANSVIDEOPLAYER** Handle, int volume);
|
||||
extern "C" __declspec(dllexport) void EnableVideoPlayerAudioVolume(ANSCENTER::ANSVIDEOPLAYER** Handle, int status);
|
||||
extern "C" __declspec(dllexport) void SetVideoPlayerImageRotation(ANSCENTER::ANSVIDEOPLAYER** Handle, double rotationAngle);
|
||||
extern "C" __declspec(dllexport) int SetBBoxVideoPlayer(ANSCENTER::ANSVIDEOPLAYER** Handle, int x, int y, int width, int height);
|
||||
extern "C" __declspec(dllexport) int SetCropFlagVideoPlayer(ANSCENTER::ANSVIDEOPLAYER** Handle, int cropFlag);
|
||||
extern "C" __declspec(dllexport) void SetVideoPlayerDisplayResolution(ANSCENTER::ANSVIDEOPLAYER** Handle, int width, int height);
|
||||
#endif
|
||||
1758
modules/ANSCV/ANSWebcam.cpp
Normal file
1758
modules/ANSCV/ANSWebcam.cpp
Normal file
File diff suppressed because it is too large
Load Diff
140
modules/ANSCV/ANSWebcam.h
Normal file
140
modules/ANSCV/ANSWebcam.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#ifndef ANSWEBCAM_H
|
||||
#define ANSWEBCAM_H
|
||||
#define ANSWEBCAM_API __declspec(dllexport)
|
||||
#include <iostream>
|
||||
#include "ANSLicense.h"
|
||||
#include "LabVIEWHeader/extcode.h"
|
||||
#include <vector>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <dshow.h>
|
||||
#include <mutex>
|
||||
#include <nvjpeg.h>
|
||||
#include <cuda_runtime.h>
|
||||
#include <turbojpeg.h>
|
||||
#pragma comment(lib, "strmiids.lib")
|
||||
#pragma comment(lib, "ole32.lib")
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
struct Resolution {
|
||||
int width;
|
||||
int height;
|
||||
|
||||
Resolution(int w, int h) : width(w), height(h) {}
|
||||
|
||||
int getArea() const {
|
||||
return width * height;
|
||||
}
|
||||
|
||||
bool operator<(const Resolution& other) const {
|
||||
return getArea() < other.getArea();
|
||||
}
|
||||
};
|
||||
class ANSWEBCAM_API ANSWEBCAMPlayer
|
||||
{
|
||||
protected:
|
||||
// Add this structure definition in the private section
|
||||
cv::VideoCapture cap;
|
||||
int _deviceId;
|
||||
int _resWidth;
|
||||
int _resHeight;
|
||||
int _savedResWidth = 0; // Saved resolution for stop/start restore
|
||||
int _savedResHeight = 0;
|
||||
bool _enableMaxResolution = false; // false = Full HD default, true = max resolution
|
||||
int64_t _previousPTS;
|
||||
cv::Mat _previousImage;
|
||||
bool m_bPaused;
|
||||
std::string _lastJpegImage;
|
||||
bool _isPlaying;
|
||||
std::vector<std::string> _cameraNames;
|
||||
int _imageRotateDeg = 0;
|
||||
std::recursive_mutex _mutex;
|
||||
cv::Rect _bbox;
|
||||
bool _crop;
|
||||
EngineType engineType;
|
||||
// Initialize nvJPEG structures
|
||||
nvjpegHandle_t nv_handle;
|
||||
nvjpegEncoderState_t nv_enc_state;
|
||||
nvjpegEncoderParams_t nv_enc_params;
|
||||
cudaStream_t stream;
|
||||
tjhandle _jpegCompressor;
|
||||
public:
|
||||
ANSWEBCAMPlayer();
|
||||
~ANSWEBCAMPlayer() noexcept;
|
||||
[[nodiscard]] bool Init(std::string licenseKey, std::string url);
|
||||
[[nodiscard]] bool Setup();
|
||||
[[nodiscard]] bool Reconnect();
|
||||
[[nodiscard]] bool Start();
|
||||
[[nodiscard]] bool IsPaused();
|
||||
[[nodiscard]] bool IsPlaying();
|
||||
[[nodiscard]] bool IsRecording();
|
||||
[[nodiscard]] bool Stop();
|
||||
void Destroy();
|
||||
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 GetJpegStringImage(int& width, int& height, int64_t& pts);
|
||||
[[nodiscard]] static std::string VectorToCommaSeparatedString(const std::vector<std::string>& inputVector);
|
||||
[[nodiscard]] static std::vector<std::string> ScanWebcamDevices();
|
||||
[[nodiscard]] static std::string GetSupportedResolutions(int deviceId);
|
||||
static std::mutex mtx;
|
||||
void SetImageRotate(int mode) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_mutex);
|
||||
_imageRotateDeg = mode;
|
||||
if (mode > 360) _imageRotateDeg = 360;
|
||||
};
|
||||
void SetBBox(cv::Rect bbox);
|
||||
void SetCrop(bool crop);
|
||||
void SetDisplayResolution(int width, int height); // Set display output size; 0,0 = original (no resize)
|
||||
[[nodiscard]] cv::Mat GetInferenceImage(); // Returns full-res frame for AI inference (no resize)
|
||||
cv::Mat _inferenceCloneCurr; // Heap-cloned full-res (keeps data alive for registry)
|
||||
cv::Mat _inferenceClonePrev; // Previous clone (keeps data alive during ANSRTYOLO read)
|
||||
public:
|
||||
void CheckLicense();
|
||||
SPDLogger& _logger = SPDLogger::GetInstance("ANSWebcamPlayer", false);
|
||||
bool _licenseValid{ false };
|
||||
std::string _licenseKey;
|
||||
|
||||
// Resolution management methods
|
||||
std::vector<Resolution> GetAvailableResolutions(int deviceId);
|
||||
bool SetBestResolution(int deviceId);
|
||||
bool SetPreferredResolution(int deviceId);
|
||||
bool SetResolution(int width, int height);
|
||||
void SetEnableMaxResolution(bool enable);
|
||||
std::string GetAvailableResolutionsString(int deviceId);
|
||||
private:
|
||||
std::string encodeJpegString(const cv::Mat& img, int quality = 80);
|
||||
std::string encodeMatToJpegWithNvJPEG(const cv::Mat& inputMat, int quality = 80);
|
||||
void uploadPlanarBGRToGPU(const cv::Mat& inputMat, unsigned char** data);
|
||||
int _displayWidth = 0; // 0 = no resize (return original)
|
||||
int _displayHeight = 0;
|
||||
cv::Mat _inferenceImage; // Full-res frame for AI inference
|
||||
};
|
||||
std::mutex ANSWEBCAMPlayer::mtx; // Definition of the static mutex
|
||||
}
|
||||
extern "C" __declspec(dllexport) int CreateANSWebcamPlayerHandle(ANSCENTER::ANSWEBCAMPlayer * *Handle, const char* licenseKey, const char* url);
|
||||
extern "C" __declspec(dllexport) int CreateANSWebcamPlayerWithMaxResoHandle(ANSCENTER::ANSWEBCAMPlayer * *Handle, const char* licenseKey, const char* url);
|
||||
extern "C" __declspec(dllexport) int ReleaseANSWebcamPlayerHandle(ANSCENTER::ANSWEBCAMPlayer * *Handle);
|
||||
extern "C" __declspec(dllexport) int ScanANSWebcamPlayer(LStrHandle cameraNames);
|
||||
extern "C" __declspec(dllexport) int ScanStrANSWebcamPlayer(std::vector<std::string> &cameraNameList);
|
||||
extern "C" __declspec(dllexport) int GetWebcamImage(ANSCENTER::ANSWEBCAMPlayer * *Handle, int& width, int& height, int64_t & timeStamp, LStrHandle jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetWebcamStrImage(ANSCENTER::ANSWEBCAMPlayer * *Handle, int& width, int& height, int64_t & timeStamp, std::string & jpegImage);
|
||||
extern "C" __declspec(dllexport) int GetWebcamCVImage(ANSCENTER::ANSWEBCAMPlayer** Handle, int& width, int& height, int64_t& timeStamp, cv::Mat** image);
|
||||
extern "C" __declspec(dllexport) int StartWebcamPlayer(ANSCENTER::ANSWEBCAMPlayer * *Handle);
|
||||
extern "C" __declspec(dllexport) int ReconnectWebcamPlayer(ANSCENTER::ANSWEBCAMPlayer * *Handle);
|
||||
extern "C" __declspec(dllexport) int StopWebcamPlayer(ANSCENTER::ANSWEBCAMPlayer * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsWebcamPlayerPaused(ANSCENTER::ANSWEBCAMPlayer * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsWebcamPlayerRunning(ANSCENTER::ANSWEBCAMPlayer * *Handle);
|
||||
extern "C" __declspec(dllexport) int IsWebcamPlayerRecording(ANSCENTER::ANSWEBCAMPlayer * *Handle);
|
||||
extern "C" __declspec(dllexport) void SetWebcamPlayerAudioVolume(ANSCENTER::ANSWEBCAMPlayer * *Handle, int volume);
|
||||
extern "C" __declspec(dllexport) void EnableWebcamPlayerAudioVolume(ANSCENTER::ANSWEBCAMPlayer * *Handle, int status);
|
||||
extern "C" __declspec(dllexport) void SetWebcamImageRotation(ANSCENTER::ANSWEBCAMPlayer * *Handle, double rotationAngle);
|
||||
extern "C" __declspec(dllexport) int ScanSupportedResolutions(std::string cameraName,std::vector<std::string> cameraNameList, std::string &supportedResolution);
|
||||
extern "C" __declspec(dllexport) int SetBBoxANSWebcamPlayer(ANSCENTER::ANSWEBCAMPlayer** Handle, int x, int y, int width, int height);
|
||||
extern "C" __declspec(dllexport) int SetCropFlagANSWebcamPlayer(ANSCENTER::ANSWEBCAMPlayer** Handle, int cropFlag);
|
||||
extern "C" __declspec(dllexport) void SetWebcamDisplayResolution(ANSCENTER::ANSWEBCAMPlayer** Handle, int width, int height);
|
||||
|
||||
#endif
|
||||
121
modules/ANSCV/CMakeLists.txt
Normal file
121
modules/ANSCV/CMakeLists.txt
Normal file
@@ -0,0 +1,121 @@
|
||||
# ANSCV — Computer Vision / Media DLL (video capture, RTSP, FFmpeg)
|
||||
file(GLOB ANSCV_HEADERS "*.h")
|
||||
file(GLOB ANSCV_SOURCES "*.cpp")
|
||||
# Exclude old VideoPlayer.cpp — superseded by MediaClient/media/video_player.cpp
|
||||
list(REMOVE_ITEM ANSCV_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayer.cpp")
|
||||
|
||||
# MediaClient sources compiled into ANSCV (from original vcxproj)
|
||||
set(MEDIACLIENT_SOURCES
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/audio_capture.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/audio_capture_win.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/audio_decoder.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/audio_encoder.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/audio_play.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/audio_play_win.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/avcodec_mutex.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/avi_write.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/file_player.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/http_flv_player.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/http_mjpeg_player.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/media_codec.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/media_parse.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/media_util.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/rtmp_player.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/rtsp_player.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/srt_player.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/video_decoder.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media/video_player.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtp/h264_rtp_rx.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtp/h264_util.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtp/h265_rtp_rx.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtp/h265_util.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtp/mjpeg_rtp_rx.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtp/mjpeg_tables.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtp/ts_parser.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtsp/rtsp_backchannel.cpp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtsp/rtsp_cln.cpp
|
||||
)
|
||||
|
||||
add_library(ANSCV SHARED ${ANSCV_HEADERS} ${ANSCV_SOURCES} ${MEDIACLIENT_SOURCES})
|
||||
|
||||
# MediaClient sources skip precompiled headers (they don't include ANSCV's pch.h)
|
||||
set_source_files_properties(${MEDIACLIENT_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||
|
||||
target_include_directories(ANSCV PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
# MediaClient includes (referenced from original ANLS)
|
||||
target_include_directories(ANSCV PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/MediaClient
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/media
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtsp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/http
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/directx
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/directx/include
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/ffmpeg/include
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/openssl/include
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/libsrt/include
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/bm
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/rtmp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/librtmp
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/srt
|
||||
${SHARED_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(ANSCV
|
||||
PRIVATE ANSLicensingSystem
|
||||
PRIVATE labview
|
||||
PRIVATE spdlog_dep
|
||||
PRIVATE ANSLibsLoader
|
||||
PRIVATE opencv
|
||||
PRIVATE ffmpeg
|
||||
PRIVATE turbojpeg
|
||||
PRIVATE zxing
|
||||
PRIVATE zlib_dep
|
||||
PRIVATE CUDA::cudart_static
|
||||
PRIVATE CUDA::cublasLt
|
||||
PRIVATE CUDA::nvjpeg
|
||||
)
|
||||
|
||||
# Platform-specific libs
|
||||
if(WIN32)
|
||||
target_link_directories(ANSCV PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/directx/lib/x64
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/openssl/lib/x64
|
||||
${CMAKE_SOURCE_DIR}/MediaClient/libsrt/lib/x64
|
||||
)
|
||||
# Prebuilt MediaClient protocol libraries (built from MediaClient VS project)
|
||||
target_link_directories(ANSCV PRIVATE "${ANLS_ROOT}/MediaClient/x64/Release")
|
||||
target_link_libraries(ANSCV PRIVATE
|
||||
RtspClientLibrary HttpFlvClientLibrary HttpMjpegClientLibrary
|
||||
RtmpClientLibrary SrtClientLibrary
|
||||
d3d9 d3dx9 dxva2
|
||||
libcrypto libssl srt
|
||||
${WIN_COMMON_LIBS}
|
||||
)
|
||||
else()
|
||||
target_link_libraries(ANSCV PRIVATE ${UNIX_COMMON_LIBS})
|
||||
endif()
|
||||
|
||||
target_compile_definitions(ANSCV PRIVATE
|
||||
ANSCV_EXPORTS
|
||||
_USRDLL
|
||||
HT_STATIC
|
||||
HTTPS
|
||||
BACKCHANNEL
|
||||
METADATA
|
||||
REPLAY
|
||||
OVER_HTTP
|
||||
OVER_WEBSOCKET
|
||||
NOMINMAX
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(ANSCV PRIVATE UNICODE _UNICODE __WINDOWS_OS__)
|
||||
else()
|
||||
target_compile_definitions(ANSCV PRIVATE __LINUX_OS__)
|
||||
endif()
|
||||
|
||||
target_precompile_headers(ANSCV PRIVATE pch.h)
|
||||
1093
modules/ANSCV/VideoPlayer.cpp
Normal file
1093
modules/ANSCV/VideoPlayer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
199
modules/ANSCV/VideoPlayer.h
Normal file
199
modules/ANSCV/VideoPlayer.h
Normal file
@@ -0,0 +1,199 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef VIDEO_PLAYER_H
|
||||
#define VIDEO_PLAYER_H
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "hqueue.h"
|
||||
#include "video_decoder.h"
|
||||
#include "audio_decoder.h"
|
||||
#include "audio_play.h"
|
||||
#include "avi_write.h"
|
||||
#include "media_util.h"
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32 SyncTimestamp;
|
||||
struct timeval SyncTime;
|
||||
} HTCLOCK;
|
||||
|
||||
typedef std::list<AVFrame*> FRAMELIST;
|
||||
|
||||
class CVideoPlayer
|
||||
{
|
||||
|
||||
public:
|
||||
CVideoPlayer();
|
||||
virtual ~CVideoPlayer();
|
||||
virtual BOOL open(std::string fileName);
|
||||
virtual BOOL play() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual BOOL pause() = 0;
|
||||
virtual void close();
|
||||
virtual BOOL seek(int pos) { return FALSE; }
|
||||
virtual BOOL isPlaying() { return m_bPlaying; }
|
||||
virtual BOOL isPaused() { return m_bPaused; }
|
||||
virtual void setVolume(int volume);
|
||||
virtual void snapshot(int videofmt);
|
||||
virtual BOOL record(std::string baseName);
|
||||
virtual void stopRecord();
|
||||
virtual BOOL isRecording() { return m_bRecording; }
|
||||
virtual BOOL onRecord() { return FALSE; }
|
||||
virtual int getVideoClock() { return 1000; }
|
||||
virtual int getAudioClock() { return 1000; }
|
||||
virtual void setWindowSize(int size);
|
||||
virtual int64 getElapse() { return 0; }
|
||||
virtual int64 getDuration() { return 0; }
|
||||
virtual int getVideoCodec() { return m_nVideoCodec; }
|
||||
virtual int getAudioCodec() { return m_nAudioCodec; }
|
||||
|
||||
virtual void setAuthInfo(std::string acct, std::string pass) { m_acct = acct; m_pass = pass; }
|
||||
virtual void setRenderMode(int mode) { m_nRenderMode = mode; }
|
||||
virtual void setHWDecoding(int mode) { m_nHWDecoding = mode; }
|
||||
virtual void setRtpMulticast(BOOL flag) {}
|
||||
virtual void setRtpOverUdp(BOOL flag) {}
|
||||
|
||||
#ifdef OVER_HTTP
|
||||
virtual void setRtspOverHttp(int flag, int port) {}
|
||||
#endif
|
||||
|
||||
#ifdef OVER_WEBSOCKET
|
||||
virtual void setRtspOverWs(int flag, int port) {}
|
||||
#endif
|
||||
|
||||
#ifdef BACKCHANNEL
|
||||
virtual int getBCFlag() { return 0; }
|
||||
virtual void setBCFlag(int flag) {}
|
||||
virtual int getBCDataFlag() { return 0; }
|
||||
virtual void setBCDataFlag(int flag) {}
|
||||
virtual void setAudioDevice(int index) {}
|
||||
#endif
|
||||
|
||||
#ifdef REPLAY
|
||||
virtual int getReplayFlag() { return 0; }
|
||||
virtual void setReplayFlag(int flag) {}
|
||||
virtual void setScale(double scale) {}
|
||||
virtual void setRateControlFlag(int flag) {}
|
||||
virtual void setImmediateFlag(int flag) {}
|
||||
virtual void setFramesFlag(int flag, int interval) {}
|
||||
virtual void setReplayRange(time_t start, time_t end) {}
|
||||
#endif
|
||||
|
||||
BOOL openVideo(int codec, uint8* extradata = NULL, int extradata_size = 0);
|
||||
BOOL openVideo(enum AVCodecID codec, uint8* extradata = NULL, int extradata_size = 0);
|
||||
void closeVideo();
|
||||
BOOL openAudio(int codec, int samplerate, int channels, int bitpersample = 0);
|
||||
BOOL openAudio(enum AVCodecID codec, int samplerate, int channels, int bitpersample = 0);
|
||||
void closeAudio();
|
||||
|
||||
void playVideo(uint8* data, int len, uint32 ts, uint16 seq);
|
||||
void playAudio(uint8* data, int len, uint32 ts, uint16 seq);
|
||||
|
||||
void recordVideo(uint8* data, int len, uint32 ts, uint16 seq);
|
||||
void recordAudio(uint8* data, int len, uint32 ts, uint16 seq);
|
||||
|
||||
int getVideoWidth();
|
||||
int getVideoHeight();
|
||||
double getFrameRate();
|
||||
int getSampleRate() { return m_nSampleRate; }
|
||||
int getChannel() { return m_nChannel; }
|
||||
|
||||
void onVideoFrame(AVFrame* frame);
|
||||
void onAudioFrame(AVFrame* frame);
|
||||
void audioPlayThread();
|
||||
void videoPlayThread();
|
||||
|
||||
//signals:
|
||||
// void notify(int);
|
||||
// void snapshoted(AVFrame*);
|
||||
|
||||
protected:
|
||||
void updateClock(HTCLOCK* clock, uint32 ts, int frequency);
|
||||
BOOL initFrame(AVFrame*& frame, int width, int height, AVPixelFormat pixfmt);
|
||||
//BOOL initVideoRender(int width, int height);
|
||||
BOOL doSnapshot(AVFrame* srcframe);
|
||||
AVFrame* convertFrame(AVFrame* srcframe, AVFrame* dstframe, BOOL updown);
|
||||
void recordVideoEx(uint8* data, int len, uint32 ts, uint16 seq);
|
||||
BOOL recordSwitchCheck();
|
||||
void recordFileSwitch();
|
||||
|
||||
protected:
|
||||
BOOL m_bVideoInited;
|
||||
BOOL m_bAudioInited;
|
||||
CVideoDecoder* m_pVideoDecoder;
|
||||
CAudioDecoder* m_pAudioDecoder;
|
||||
//CVideoRender* m_pVideoRender;
|
||||
CAudioPlay* m_pAudioPlay;
|
||||
|
||||
BOOL m_bPlaying;
|
||||
BOOL m_bPaused;
|
||||
|
||||
std::string m_acct;
|
||||
std::string m_pass;
|
||||
std::string m_sFileName;
|
||||
std::string m_sBaseName;
|
||||
//WId m_nVideoWnd;
|
||||
int m_size;
|
||||
BOOL m_bSizeChanged;
|
||||
int m_nRenderMode;
|
||||
int m_nHWDecoding;
|
||||
AVPixelFormat m_nDstVideoFmt;
|
||||
BOOL m_bUpdown;
|
||||
BOOL m_bSnapshot;
|
||||
int m_nSnapVideoFmt;
|
||||
|
||||
H26XParamSets m_h26XParamSets;
|
||||
|
||||
int m_nVideoCodec;
|
||||
int m_nAudioCodec;
|
||||
int m_nSampleRate;
|
||||
int m_nChannel;
|
||||
int m_nBitPerSample;
|
||||
|
||||
AVFrame* m_pSnapFrame;
|
||||
AVFrame* m_pRenderFrame;
|
||||
|
||||
BOOL m_bRecording;
|
||||
BOOL m_bNalFlag;
|
||||
AVICTX* m_pAviCtx;
|
||||
void* m_pRecordMutex;
|
||||
|
||||
HTCLOCK m_audioClock;
|
||||
void* m_pAudioListMutex;
|
||||
FRAMELIST m_audioFrameList;
|
||||
BOOL m_audioPlayFlag;
|
||||
pthread_t m_audioPlayThread;
|
||||
|
||||
HTCLOCK m_videoClock;
|
||||
void* m_pVideoListMutex;
|
||||
FRAMELIST m_videoFrameList;
|
||||
BOOL m_videoPlayFlag;
|
||||
pthread_t m_videoPlayThread;
|
||||
|
||||
uint64 m_nLastAudioPts;
|
||||
time_t m_lastAudioTS;
|
||||
};
|
||||
|
||||
#endif // end of VIDEO_PLAYER_H
|
||||
|
||||
|
||||
|
||||
106
modules/ANSCV/dllmain.cpp
Normal file
106
modules/ANSCV/dllmain.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// dllmain.cpp : Defines the entry point for the DLL application.
|
||||
#include "pch.h"
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
|
||||
// ── Global handle registry for shutdown cleanup ──────────────────────────
|
||||
// Streaming handle creators register a stop-callback on creation.
|
||||
// DLL_PROCESS_DETACH invokes all callbacks so threads exit before DLL unmaps.
|
||||
|
||||
static std::mutex& shutdownMutex() {
|
||||
static std::mutex m;
|
||||
return m;
|
||||
}
|
||||
|
||||
static std::unordered_map<void*, std::function<void()>>& shutdownRegistry() {
|
||||
static std::unordered_map<void*, std::function<void()>> r;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Called by Create*Handle functions to register a cleanup callback
|
||||
extern "C" void anscv_register_handle(void* handle, void(*stopFn)(void*)) {
|
||||
if (!handle || !stopFn) return;
|
||||
std::lock_guard<std::mutex> lk(shutdownMutex());
|
||||
shutdownRegistry()[handle] = [=]() { stopFn(handle); };
|
||||
}
|
||||
|
||||
// Called by Release*Handle functions to unregister (handle properly cleaned up)
|
||||
extern "C" void anscv_unregister_handle(void* handle) {
|
||||
if (!handle) return;
|
||||
std::lock_guard<std::mutex> lk(shutdownMutex());
|
||||
shutdownRegistry().erase(handle);
|
||||
}
|
||||
|
||||
// Stop all leaked handles
|
||||
static void stopAllHandles() {
|
||||
std::unordered_map<void*, std::function<void()>> copy;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(shutdownMutex());
|
||||
copy.swap(shutdownRegistry());
|
||||
}
|
||||
for (auto& [ptr, fn] : copy) {
|
||||
try { fn(); } catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
) noexcept
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
// Pin the DLL so it is never unmapped from the process address space.
|
||||
// ANSCV creates background threads (RTSP receive, timers) that may
|
||||
// still be running when LabVIEW's CLR/COM shutdown unloads the DLL.
|
||||
// If the DLL is unmapped while threads are executing its code, the
|
||||
// threads crash with an access violation at <Unloaded_ANSCV.dll>.
|
||||
// Pinning keeps the code pages mapped; the OS kills all threads when
|
||||
// the process exits, so this is safe and is Microsoft's recommended
|
||||
// pattern for DLLs that own threads.
|
||||
{
|
||||
HMODULE hSelf = nullptr;
|
||||
GetModuleHandleExW(
|
||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_PIN,
|
||||
reinterpret_cast<LPCWSTR>(&DllMain),
|
||||
&hSelf);
|
||||
}
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
// CRITICAL: Do NOT call stopAllHandles() here.
|
||||
//
|
||||
// DllMain holds the OS loader lock (LdrpLoaderLock). Destroying
|
||||
// streaming handles (CRtspPlayer → CVideoPlayer → CWAudioPlay)
|
||||
// calls dsound!CThread::Terminate which waits for the DirectSound
|
||||
// admin thread to exit. That thread needs the loader lock to shut
|
||||
// down → classic deadlock. (Confirmed by crash dump analysis:
|
||||
// Thread 0 holds LdrpLoaderLock and waits for dsound thread 331,
|
||||
// which is blocked on LdrpLoaderLock.)
|
||||
//
|
||||
// If lpReserved is non-NULL the process is terminating — the OS
|
||||
// will tear down all threads and reclaim resources; cleanup is
|
||||
// unnecessary and dangerous. If lpReserved is NULL (dynamic
|
||||
// FreeLibrary), LabVIEW should have called Release*Handle first.
|
||||
// Log leaked handles for diagnostics but do NOT stop them here.
|
||||
if (lpReserved == nullptr) {
|
||||
// Dynamic unload — warn about leaked handles (non-blocking)
|
||||
try {
|
||||
std::lock_guard<std::mutex> lk(shutdownMutex());
|
||||
if (!shutdownRegistry().empty()) {
|
||||
std::cerr << "[ANSCV] WARNING: " << shutdownRegistry().size()
|
||||
<< " streaming handle(s) leaked at DLL unload."
|
||||
<< " Call Release*Handle before FreeLibrary." << std::endl;
|
||||
}
|
||||
} catch (...) {}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
7
modules/ANSCV/framework.h
Normal file
7
modules/ANSCV/framework.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#define NOMINMAX // Prevent windows.h from defining min/max macros
|
||||
// which break std::min / std::max (C2589)
|
||||
// Windows Header Files
|
||||
#include <windows.h>
|
||||
5
modules/ANSCV/pch.cpp
Normal file
5
modules/ANSCV/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// pch.cpp: source file corresponding to the pre-compiled header
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
|
||||
25
modules/ANSCV/pch.h
Normal file
25
modules/ANSCV/pch.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// pch.h: This is a precompiled header file.
|
||||
// Files listed below are compiled only once, improving build performance for future builds.
|
||||
// This also affects IntelliSense performance, including code completion and many code browsing features.
|
||||
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
|
||||
// Do not add files here that you will be updating frequently as this negates the performance advantage.
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
// add headers that you want to pre-compile here
|
||||
#include "framework.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
// Legacy code uses bare min/max (without std:: prefix).
|
||||
// NOMINMAX prevents windows.h from defining min/max macros,
|
||||
// so pull them in from <algorithm> instead.
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
#endif //PCH_H
|
||||
Reference in New Issue
Block a user