From c625898f61d2e969ffb46152b569138a890590f5 Mon Sep 17 00:00:00 2001 From: Tuan Nghia Nguyen Date: Thu, 23 Apr 2026 08:08:34 +1000 Subject: [PATCH] Fix Video Player that support generate black image when finish playing video (reach the end of video) --- .claude/settings.local.json | 5 ++++- modules/ANSCV/ANSVideoPlayer.cpp | 18 +++++++++++------- modules/ANSCV/ANSVideoPlayer.h | 3 ++- tests/ANSCV-UnitTest/ANSCV-UnitTest.cpp | 15 +++++++-------- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 600ad8c..17f3181 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -92,7 +92,10 @@ "Bash(git submodule *)", "Bash(git rm *)", "Bash(rm -rf .git/modules/3rdparty/libyuv)", - "Bash(git add *)" + "Bash(git add *)", + "Read(//c/ProgramData/Jh7O7nUe7vS/Models/EngineModels/B-IN_ANS_VehicleDetection_v2.0_67345015/**)", + "Bash(xxd)", + "Bash(icacls \"C:\\\\ProgramData\\\\Jh7O7nUe7vS\\\\Models\\\\EngineModels\\\\B-IN_ANS_VehicleDetection_v2.0_67345015\\\\train_last.onnx\")" ] } } diff --git a/modules/ANSCV/ANSVideoPlayer.cpp b/modules/ANSCV/ANSVideoPlayer.cpp index b7f492c..45ba697 100644 --- a/modules/ANSCV/ANSVideoPlayer.cpp +++ b/modules/ANSCV/ANSVideoPlayer.cpp @@ -253,7 +253,7 @@ namespace ANSCENTER { _hwPlayer->play(); // starts read/video/audio threads _hwEOF = false; _hwFrameCount = 0; - _hwLastPts = 0; + _hwLastElapseMs = 0; _isPlaying = true; // Wait for first frame outside the mutex to let decode threads run @@ -311,7 +311,7 @@ namespace ANSCENTER { _hwCudaAccel = false; _hwEOF = false; _hwFrameCount = 0; - _hwLastPts = 0; + _hwLastElapseMs = 0; } else { // --- cv::VideoCapture fallback --- @@ -525,10 +525,14 @@ namespace ANSCENTER { __FILE__, __LINE__); } - // EOF detection: CFilePlayer auto-loops, but ANSVideoPlayer should stop. - // Detect when PTS wraps backwards (CFilePlayer seeked to start for looping). - if (_hwFrameCount > 10 && _hwLastPts > 0 && imgPts < _hwLastPts - 1000) { - // Position wrapped back to start → video reached end + // EOF detection: CFilePlayer auto-loops via av_seek_frame on AVERROR_EOF + // (file_player.cpp:434), so ANSVideoPlayer must detect the wrap itself. + // imgPts from getImage() is a monotonic call-counter, not a real PTS — + // use getElapse() (ms of real video position, updated from packet.pts + // in file_player.cpp:608). When the file loops, elapse drops back to ~0. + const int64_t elapseMs = _hwPlayer->getElapse(); + if (_hwFrameCount > 10 && _hwLastElapseMs > 0 && + elapseMs + 500 < _hwLastElapseMs) { _hwEOF = true; if (_resHeight <= 0 || _resWidth <= 0) { _resHeight = imgH; _resWidth = imgW; } _previousImage = cv::Mat(_resHeight, _resWidth, CV_8UC3, cv::Scalar(0, 0, 0)); @@ -540,7 +544,7 @@ namespace ANSCENTER { return _previousImage; } - _hwLastPts = imgPts; // track for EOF detection (PTS wrap) + _hwLastElapseMs = elapseMs; cv::Mat result = frame; // CFilePlayer returns owned Mat (already cloned internally) diff --git a/modules/ANSCV/ANSVideoPlayer.h b/modules/ANSCV/ANSVideoPlayer.h index 143ba63..132b068 100644 --- a/modules/ANSCV/ANSVideoPlayer.h +++ b/modules/ANSCV/ANSVideoPlayer.h @@ -17,6 +17,7 @@ namespace ANSCENTER { + // This class only allow to run 1 (to the end of video) then it will generate blank dark image class ANSVIDEOPLAYER_API ANSVIDEOPLAYER { protected: @@ -49,7 +50,7 @@ namespace ANSCENTER 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 + int64_t _hwLastElapseMs = 0; // last CFilePlayer elapse (ms); EOF detected when it wraps backwards ANSVIDEOPLAYER(); ~ANSVIDEOPLAYER() noexcept; [[nodiscard]] bool Init(std::string licenseKey, std::string url); diff --git a/tests/ANSCV-UnitTest/ANSCV-UnitTest.cpp b/tests/ANSCV-UnitTest/ANSCV-UnitTest.cpp index 42e9bd5..8e0b87f 100644 --- a/tests/ANSCV-UnitTest/ANSCV-UnitTest.cpp +++ b/tests/ANSCV-UnitTest/ANSCV-UnitTest.cpp @@ -1491,11 +1491,11 @@ int VideoPlayerClientTest() { while (true) { index++; std::cout << "Index=" << index << std::endl; - if ((index == 200) || (index == 800) || (index == 1200)) { StopVideoPlayer(&videoClient); } + /*if ((index == 200) || (index == 800) || (index == 1200)) { StopVideoPlayer(&videoClient); } if ((index == 400) || (index == 1000) || (index == 1500)) { StartVideoPlayer(&videoClient); } if ((index == 1800) || (index == 2200) || (index == 2500)) { StopVideoPlayer(&videoClient); } if ((index == 2000) || (index == 2300) || (index == 2700)) { StartVideoPlayer(&videoClient); } - if (index > 20000) break; + if (index > 20000) break;*/ auto start = std::chrono::system_clock::now(); cv::Mat* image = nullptr; // ✅ Use a pointer to hold the allocated image GetVideoPlayerCVImage(&videoClient, width, height, pts,&image); @@ -1574,11 +1574,11 @@ int FilePlayerClientCVTest() { while (true) { index++; std::cout << "Index=" << index << std::endl; - if ((index == 200) || (index == 800) || (index == 1200)) { StopFilePlayer(&filePlayerClient); } + /*if ((index == 200) || (index == 800) || (index == 1200)) { StopFilePlayer(&filePlayerClient); } if ((index == 400) || (index == 1000) || (index == 1500)) { StartFilePlayer(&filePlayerClient); } if ((index == 1800) || (index == 2200) || (index == 2500)) { StopFilePlayer(&filePlayerClient); } if ((index == 2000) || (index == 2300) || (index == 2700)) { StartFilePlayer(&filePlayerClient); } - if (index > 20000) break; + if (index > 20000) break;*/ auto start = std::chrono::system_clock::now(); cv::Mat* image = nullptr; GetFilePlayerCVImage(&filePlayerClient, width, height, pts, &image); @@ -1645,17 +1645,16 @@ int main() // so this works without the unit test having to link FFmpeg itself. //ANSCV_PrintFFmpegLicense_S(); //FilePlayerClientDoubleDestroy(); - //FilePlayerClientCVTest(); + FilePlayerClientCVTest(); + // VideoPlayerClientDoubleDestroy(); //VideoPlayerClientTest(); - //VideoPlayerClientDoubleDestroy(); - // VideoPlayerClientTest(); //OpenCVFunctionTest(); //GenerateVideo(); //VideoTestClient(); // TestGetImage(); //PureOpenCV(); // RSTPTestClient(); - RSTPTestCVClient(); + //RSTPTestCVClient(); //TestCreateImageFromJpegStringFile(); //TestCreateImageFromFile(); //for (int i = 0; i < 100; i++) {