Fix double stop in ANSVideoPlayer

This commit is contained in:
2026-04-22 10:10:16 +10:00
parent 97d814936d
commit 57cc8e0a56
14 changed files with 492 additions and 70 deletions

View File

@@ -46,8 +46,11 @@ std::atomic<int64_t> g_queueClones{0};
std::atomic<int64_t> g_queueFrees{0};
std::atomic<int64_t> g_nv12Clones{0};
std::atomic<int64_t> g_nv12Frees{0};
std::atomic<int64_t> g_nv12Escapes{0};
std::atomic<int64_t> g_cudaHWClones{0};
std::atomic<int64_t> g_cudaHWFrees{0};
std::atomic<int64_t> g_cudaHWEscapes{0};
std::atomic<int64_t> g_avframePendingReturns{0};
extern std::atomic<int64_t> g_contiguousAllocs;
extern std::atomic<int64_t> g_contiguousFrees;
@@ -1621,7 +1624,9 @@ AVFrame* CVideoPlayer::getNV12Frame() {
// (Previously used ownership transfer — only the first caller got NV12,
// and the second caller fell back to BGR.)
std::lock_guard<std::recursive_mutex> lock(_mutex);
return m_currentNV12Frame ? av_frame_clone(m_currentNV12Frame) : nullptr;
AVFrame* clone = m_currentNV12Frame ? av_frame_clone(m_currentNV12Frame) : nullptr;
if (clone) g_nv12Escapes.fetch_add(1, std::memory_order_relaxed);
return clone;
}
AVFrame* CVideoPlayer::getCudaHWFrame() {
@@ -1632,7 +1637,9 @@ AVFrame* CVideoPlayer::getCudaHWFrame() {
// extra_hw_frames=2 in the decoder provides surface pool headroom
// for the 3 concurrent clones (decoder + player + registry).
std::lock_guard<std::recursive_mutex> lock(_mutex);
return m_currentCudaHWFrame ? av_frame_clone(m_currentCudaHWFrame) : nullptr;
AVFrame* clone = m_currentCudaHWFrame ? av_frame_clone(m_currentCudaHWFrame) : nullptr;
if (clone) g_cudaHWEscapes.fetch_add(1, std::memory_order_relaxed);
return clone;
}
bool CVideoPlayer::isCudaHWAccel() const {
@@ -2516,20 +2523,29 @@ cv::Mat CVideoPlayer::getImage(int& width, int& height, int64_t& pts) {
const int64_t qF = g_queueFrees.load(std::memory_order_relaxed);
const int64_t nvA = g_nv12Clones.load(std::memory_order_relaxed);
const int64_t nvF = g_nv12Frees.load(std::memory_order_relaxed);
const int64_t nvE = g_nv12Escapes.load(std::memory_order_relaxed);
const int64_t cuA = g_cudaHWClones.load(std::memory_order_relaxed);
const int64_t cuF = g_cudaHWFrees.load(std::memory_order_relaxed);
const int64_t cuE = g_cudaHWEscapes.load(std::memory_order_relaxed);
const int64_t pR = g_avframePendingReturns.load(std::memory_order_relaxed);
const int64_t cgA = g_contiguousAllocs.load(std::memory_order_relaxed);
const int64_t cgF = g_contiguousFrees.load(std::memory_order_relaxed);
const int64_t cgB = g_contiguousBytesInFlight.load(std::memory_order_relaxed);
// escapeBalance = (nv12Esc + cuHWEsc) - pendingReturns.
// Positive growing value = external callers hold clones they
// never returned to the pendingFree drain → escape-path leak.
const int64_t escBal = (nvE + cuE) - pR;
ANS_DBG("MEDIA_Leak",
"queue(C=%lld F=%lld net=%lld depth=%zu) "
"nv12(C=%lld F=%lld net=%lld) "
"cudaHW(C=%lld F=%lld net=%lld) "
"nv12(C=%lld F=%lld net=%lld esc=%lld) "
"cudaHW(C=%lld F=%lld net=%lld esc=%lld) "
"pendingReturns=%lld escBal=%lld "
"contig(A=%lld F=%lld net=%lld bytesMB=%.1f)",
(long long)qA, (long long)qF, (long long)(qA - qF),
g_frameQueue.size(),
(long long)nvA, (long long)nvF, (long long)(nvA - nvF),
(long long)cuA, (long long)cuF, (long long)(cuA - cuF),
(long long)nvA, (long long)nvF, (long long)(nvA - nvF), (long long)nvE,
(long long)cuA, (long long)cuF, (long long)(cuA - cuF), (long long)cuE,
(long long)pR, (long long)escBal,
(long long)cgA, (long long)cgF, (long long)(cgA - cgF),
(double)cgB / (1024.0 * 1024.0));
}

View File

@@ -22,10 +22,17 @@
// Defined in video_player.cpp; also incremented from FrameQueue here.
extern std::atomic<int64_t> g_queueClones; // av_frame_clone from FrameQueue
extern std::atomic<int64_t> g_queueFrees; // av_frame_free from FrameQueue
extern std::atomic<int64_t> g_nv12Clones; // m_currentNV12Frame = av_frame_clone
extern std::atomic<int64_t> g_nv12Frees; // av_frame_free(&m_currentNV12Frame)
extern std::atomic<int64_t> g_cudaHWClones; // m_currentCudaHWFrame = clone
extern std::atomic<int64_t> g_cudaHWFrees; // av_frame_free(&m_currentCudaHWFrame)
extern std::atomic<int64_t> g_nv12Clones; // m_currentNV12Frame = av_frame_clone (INTERNAL replace-on-update)
extern std::atomic<int64_t> g_nv12Frees; // av_frame_free(&m_currentNV12Frame) (INTERNAL)
extern std::atomic<int64_t> g_nv12Escapes; // getNV12Frame() clones handed to callers (EXTERNAL — should be balanced by caller's av_frame_free)
extern std::atomic<int64_t> g_cudaHWClones; // m_currentCudaHWFrame = clone (INTERNAL)
extern std::atomic<int64_t> g_cudaHWFrees; // av_frame_free(&m_currentCudaHWFrame) (INTERNAL)
extern std::atomic<int64_t> g_cudaHWEscapes; // getCudaHWFrame() clones handed to callers (EXTERNAL)
// Inbound side: AVFrames pushed to pendingFree (drained by gpu_frame_evict_stale).
// Incremented in ANSGpuFrameRegistry::pushPendingFree_locked — counts AVFrames that
// callers relinquished back to the media layer for deferred freeing. If escapes grow
// faster than pendingReturns, the escape path is leaking.
extern std::atomic<int64_t> g_avframePendingReturns;
typedef struct
{