摄像头播放后,增加分辨率的显示

This commit is contained in:
2025-12-26 18:55:04 +08:00
parent e98059fd30
commit 108c97924f
7 changed files with 232 additions and 93 deletions

View File

@@ -110,46 +110,68 @@ public class FrameConsumer : IDisposable
/// </summary>
private void RenderLoop()
{
// 创建 OpenCV 显示窗口
Cv2.NamedWindow(_windowName, WindowFlags.Normal);
bool isWindowCreated = false;
try
{
// 阻塞遍历队列:队列空时等待,收到取消信号时退出
foreach (var frame in _frameBuffer.GetConsumingEnumerable(_cts.Token))
// 我们不再使用简单的 foreach 阻塞等待数据,
// 而是改用非阻塞模式或带有超时的读取,以保证 WaitKey 的活性
while (!_cts.Token.IsCancellationRequested)
{
try
// 尝试在 30ms 内获取一帧数据(相当于 33 fps 的响应速度)
if (_frameBuffer.TryTake(out var frame, 30, _cts.Token))
{
// 渲染有效性校验Mat 未释放且不为空
if (frame.InternalMat != null && !frame.InternalMat.IsDisposed)
try
{
// 零拷贝渲染:直接引用 InternalMat无内存复制开销
Cv2.ImShow(_windowName, frame.InternalMat);
// 1ms 等待 UI 事件响应(必须调用,否则窗口无响应)
Cv2.WaitKey(1);
if (frame.InternalMat != null && !frame.InternalMat.IsDisposed)
{
if (!isWindowCreated)
{
Cv2.NamedWindow(_windowName, WindowFlags.Normal);
isWindowCreated = true;
}
Cv2.ImShow(_windowName, frame.InternalMat);
}
}
finally
{
frame.Dispose();
}
}
catch (Exception ex)
// 【核心修复】无论有没有取到帧,都要执行 WaitKey
// 只有这样,窗口在没视频时才能被拖动、最小化或手动点击 X 关闭
if (isWindowCreated)
{
Debug.WriteLine($"[RenderError] 窗口[{_windowName}]渲染失败: {ex.Message}");
}
finally
{
// 至关重要:渲染完成后释放帧引用
// 引用计数归零 → 帧自动回池复用,避免内存泄漏
frame.Dispose();
// 1ms 的等待足以处理 Windows 窗口消息
int key = Cv2.WaitKey(1);
// 如果用户点击了 OpenCV 窗口右上角的 X (部分版本支持)
// 或者按下 ESC可以根据需要在这里处理
if (key == 27) break;
// 检查窗口是否还存在(防止用户手动关掉窗口后报错)
// 如果窗口被手动关闭,我们标记为未创建,下次有流时重新弹窗
try
{
if (Cv2.GetWindowProperty(_windowName, WindowPropertyFlags.Visible) < 1)
{
isWindowCreated = false;
}
}
catch { isWindowCreated = false; }
}
}
}
catch (OperationCanceledException)
{
// 正常取消,无需处理
Debug.WriteLine($"[RenderInfo] 窗口[{_windowName}]渲染循环已取消");
}
catch (OperationCanceledException) { }
catch { }
finally
{
// 销毁 OpenCV 窗口,释放 UI 资源
Cv2.DestroyWindow(_windowName);
if (isWindowCreated)
{
Cv2.DestroyWindow(_windowName);
}
}
}