当前位置: 首页 > article >正文

WINUI——Magewell视频捕捉开发手记

背景

因需要融合视频,并加载患者CT中提取出的气管镜与病变,以便能实时查看气管镜是否在正确位置。

开发环境

硬件:Magewell的USB Capture HDMI Gen 2

IDE:VS2022

FrameWork: .Net6   WINUI

Package: MVVMToolKit     NLog

Magewell Demo源码浅析

看了下Magewell提供的Demo源码(官网下载MWCaptureSDK 3.3.1.1513安装后,在安装目录下即可看到),看了下,其仅提供了WinForm的示例。

以下为获取视频并播放的核心代码:

void audio_play()
{Console.WriteLine("audio_play in");while (m_capturing){CRingBuffer.st_frame_t frame = m_capture.m_audio_buffer.get_frame_to_render();if (frame.buffer_len == 0){Thread.Sleep(5);continue;}LibMWMedia.MWDSoundRendererPushFrame(m_dsound_render, frame.p_buffer, frame.frame_len);}m_capture.m_audio_buffer.stop_render();Console.WriteLine("audio_play out");
}

其中m_d3d_renderer的初始化如下:

 m_d3d_renderer初始化时调用了Handle参数:

 那么也就是说执行

 LibMWMedia.MWDSoundRendererPushFrame(m_dsound_render, frame.p_buffer, frame.frame_len);

就是将每帧的数据显示到Handle句柄所在的Form上。

而在执行 LibMWMedia.MWDSoundRendererPushFrame(m_dsound_render, frame.p_buffer, frame.frame_len)时,相应图像帧(或视频流)的数据就应该是frame.p_buffer()。

在WINUI中仅Window可以获取到句柄,但若用Window进行处理,就需要处理多个Window,这是不推荐的;最好还是以Page来显示会更合适一些。

通过测试,使用视频流来进行开发是可行的,其中一种方法即是将缓存下来的图像帧(YUY2)转化为图片(Bitmap,即YUY2转为BGRA),然后将图片贴到UI上即可,只要能保证大于24帧的帧率,就能达到流畅的视频效果。

下述为YUY2转化为BGRA类:

    /// <summary>/// YUY2转为BGRA格式/// </summary>internal class YUY2ToBGRA{internal static void ConvertYUY2ToBGRA(byte[] inputPtr, byte[] outputPtr){// 假设数据是16位灰度(高字节在前)for (int i = 0, j = 0; i < inputPtr.Length; i += 4, j += 8){// 读取YUY2数据(每4字节包含2个像素)byte y0 = inputPtr[i];byte u = inputPtr[i + 1];byte y1 = inputPtr[i + 2];byte v = inputPtr[i + 3];// 转换为RGB(简化版)YUVToRGB(y0, u, v, out byte r0, out byte g0, out byte b0);YUVToRGB(y1, u, v, out byte r1, out byte g1, out byte b1);// 写入BGRA格式outputPtr[j] = b0;     // BoutputPtr[j + 1] = g0; // GoutputPtr[j + 2] = r0; // RoutputPtr[j + 3] = 255; // AoutputPtr[j + 4] = b1;outputPtr[j + 5] = g1;outputPtr[j + 6] = r1;outputPtr[j + 7] = 255;}}// YUV到RGB转换private static void YUVToRGB(byte y, byte u, byte v, out byte r, out byte g, out byte b){// 标准化YUV值double yD = (y - 16) / 219.0;double uD = (u - 128) / 224.0;double vD = (v - 128) / 224.0;// 转换矩阵(BT.601标准)r = (byte)(255 * Math.Clamp(yD + 1.402 * vD, 0, 1));g = (byte)(255 * Math.Clamp(yD - 0.344 * uD - 0.714 * vD, 0, 1));b = (byte)(255 * Math.Clamp(yD + 1.772 * uD, 0, 1));}}

根据Magewell给出的示例,增加下述视频捕获类:

    /// <summary>/// 视频捕获/// </summary>internal class VedioCapture{Boolean m_capturing = false;protected CMWCapture m_capture = null; protected IntPtr m_d3d_renderer = IntPtr.Zero;private byte[] conversionBuffer; // 用于格式转换的缓冲区// 假设视频格式为YUY2 (1920x1080)private int Width = 1920;private int Height = 1080;//private const int BytesPerPixel = 2; // YUY2是2字节/像素/// <summary>/// 开始捕捉/// </summary>/// <param name="index"></param>/// <returns></returns>internal Boolean Start_capture(Int32 index = 0){if (m_capturing){return true;}m_capture = CMWCapture.mw_capture_factory(index);if (null == m_capture){return false;}if (!m_capture.set_device(index)){return false;}if (!m_capture.start_capture(true, false)){return false;}m_capturing = true;m_capture.get_mw_fourcc(out uint mw_fourcc);m_capture.get_mirror_and_reverse(out bool mirror, out bool reverse);m_capture.get_resolution(out Width, out Height);NlogHelper.Logger.Info($"获取分辨率,Width: {Width}, Height: {Height}");// 更新 UITask.Run(() =>{_ = Video_play();});return true;}/// <summary>/// 视频播放/// </summary>/// <returns></returns>async Task Video_play(){NlogHelper.Logger.Info("video_play in");conversionBuffer = new byte[Width * Height * 4];while (m_capturing){CRingBuffer.st_frame_t frame = m_capture.m_video_buffer.get_frame_to_render();if (frame.buffer_len == 0){continue;}UpdateFrame(frame.p_buffer);await Task.Delay(5);}NlogHelper.Logger.Info("video_play out");}/// <summary>/// 停止捕捉视频/// </summary>internal void Stop_capture(){if (!m_capturing){return;}m_capturing = false;if (m_capture != null){CRingBuffer.st_frame_t out_frame = m_capture.m_video_buffer.get_buffer_by_index(0);Array.Clear(out_frame.p_buffer, 0, out_frame.p_buffer.Length);m_capture.m_video_buffer.stop_render();m_capture.Dispose();m_capture = null;}}public void UpdateFrame(byte[] rawData){if (rawData.Length != Width * Height * 2){NlogHelper.Logger.Info($"rawData.Length {rawData.Length}");return;}// 1. 将YUY2转换为BGRAYUY2ToBGRA.ConvertYUY2ToBGRA(rawData, conversionBuffer);WeakReferenceMessenger.Default.Send(conversionBuffer, "VedioCaptureResult");}}

上述代码中,视频捕捉硬件的默认输出分辨率为1920*1080,若需要修改,可以在安装的Magwell上进行设置:

在VM中调用如下:

   private void StartCapture(){CMWCapture.Init();CMWCapture.RefreshDevices();int m_channel_count = CMWCapture.GetChannelCount();if (m_channel_count == 0){ToUIMessage.SendMessage("GpuEncodeGui", "Can't find capture devices!");}else{VedioCapture = new();VedioCapture.Start_capture();InitSegment();}}

因为CMWCapture为非托管资源,在使用完成时需要手动将资源释放,故在VM中需要手动释放,示例如下:

[RelayCommand]
private void ReleaseResource()
{VedioCapture?.Stop_capture();WeakReferenceMessenger.Default.UnregisterAll(this);
}

VedioCapture为VM初始化时构造的VedioCapture实例。

WINUI中处理获视频流

UI需先引入 xmlns:win2d="using:Microsoft.Graphics.Canvas.UI.Xaml"

<win2d:CanvasSwapChainPanelx:Name="swapChainPanel"Width="1920"Height="1080"VerticalAlignment="Top" />

UI后台代码中接收视频捕获结果如下:

 WeakReferenceMessenger.Default.Register<byte[], string>(this, "VedioCaptureResult", (r, conversionBuffer) =>{UpdateFrame(conversionBuffer);});

上述涉及调用的UpdateFrame方法,以更新每一帧:

 private void UpdateFrame(byte[] conversionBuffer){using (var drawingSession = swapChain.CreateDrawingSession(Colors.Black))using (var bitmap = CanvasBitmap.CreateFromBytes(canvasDevice,conversionBuffer,width,height,DirectXPixelFormat.B8G8R8A8UIntNormalized)){// 3. 绘制到交换链drawingSession.DrawImage(bitmap);}// 呈现到SwapChainPanelswapChain.Present();}

注意:上述使用的了MvvmToolkit中的Messenger,在离开所在Page时需要将Messenger全部释放,如  WeakReferenceMessenger.Default.UnregisterAll(this);  否则会导致Page不能被释放,会导致内存泄漏。

相关文章:

WINUI——Magewell视频捕捉开发手记

背景 因需要融合视频&#xff0c;并加载患者CT中提取出的气管镜与病变&#xff0c;以便能实时查看气管镜是否在正确位置。 开发环境 硬件&#xff1a;Magewell的USB Capture HDMI Gen 2 IDE&#xff1a;VS2022 FrameWork: .Net6 WINUI Package: MVVMToolKit NLog Ma…...

Jetpack Compose 中,DisposableEffect、LaunchedEffect 和 sideEffect 区别和用途

在 Jetpack Compose 中&#xff0c;DisposableEffect、LaunchedEffect 和 sideEffect 都是用于处理副作用&#xff08;Side Effects&#xff09;的 API&#xff0c;但它们的用途和触发时机不同。以下是它们的核心概念和区别&#xff1a; 1. 副作用&#xff08;Side Effect&…...

STM32开发,创建线程栈空间大小判断

1. 使用RTOS提供的API函数&#xff08;以FreeRTOS为例&#xff09; 函数原型&#xff1a;UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask)功能&#xff1a;获取指定任务堆栈中剩余的最小空间&#xff08;以字为单位&#xff0c;非字节&#xff09;。使用步骤&am…...

正则表达式检测文件类型是否为视频或图片

// 配置化文件类型检测&#xff08;集中管理支持的类型&#xff09; const FILE_TYPE_CONFIG {video: {extensions: [mp4, webm, ogg, quicktime], // 可扩展支持更多格式regex: /^video\/(mp4|webm|ogg|quicktime)$/i // 自动生成正则},image: {extensions: [jpeg, jpg, png,…...

Qwen大语言模型里,<CLS>属于特殊的标记:Classification Token

Qwen大语言模型里,<CLS>属于特殊的标记:Classification Token 目录 Qwen大语言模型里,<CLS>属于特殊的标记:Classification Token功能解析工作机制应用场景举例说明技术要点在自然语言处理(NLP)领域 都是<CLS> + <SEP>吗?一、CLS和SEP的作用与常见用法1. **CLS标…...

TDengine 开发指南——无模式写入

简介 在物联网应用中&#xff0c;为了实现自动化管理、业务分析和设备监控等多种功能&#xff0c;通常需要采集大量的数据项。然而&#xff0c;由于应用逻辑的版本升级和设备自身的硬件调整等原因&#xff0c;数据采集项可能会频繁发生变化。为了应对这种挑战&#xff0c;TDen…...

分布式互斥算法

1. 概述&#xff1a;什么是分布式互斥 假设有两个小孩想玩同一个玩具&#xff08;临界资源&#xff09;&#xff0c;但玩具只有一个&#xff0c;必须保证一次只有一个人能够玩。当一个小孩在玩时&#xff0c;另一个小孩只能原地等待&#xff0c;直到玩完才能轮到自己。这就是 …...

第34次CCF-CSP认证真题解析(目标300分做法)

第34次CCF-CSP认证 矩阵重塑&#xff08;其一&#xff09;AC代码及解析矩阵重塑&#xff08;其二&#xff09;AC代码及解析货物调度AC代码及解析 矩阵重塑&#xff08;其一&#xff09; 输入输出及样例&#xff1a; AC代码及解析 1.线性化原矩阵 &#xff1a;由于cin的特性我们…...

video-audio-extractor:视频转换为音频

软件介绍 前几天在网上看见有人分享了一个源码&#xff0c;大概就是py调用的ffmpeg来制作的。 这一次我带来源码版&#xff08;需要py环境才可以运行&#xff09;&#xff0c;开箱即用版本&#xff08;直接即可运行&#xff09; 软件特点 软件功能 视频提取音频&#xff1a…...

rk3588 区分两个相同的usb相机

有时候会插入两个一模一样的usb相机&#xff0c;担心每次启动他们所对应的设备节点 /dev/video* 会变化&#xff0c;所以需要绑定usb口&#xff0c;区分两个相机。把两个相机都插入后&#xff0c;查看usb信息 rootrk3588:/# udevadm info --attribute-walk --name/dev/video0U…...

[概率论基本概念4]什么是无偏估计

关键词&#xff1a;Unbiased Estimation 一、说明 对于无偏和有偏估计&#xff0c;需要了解其叙事背景&#xff0c;是指整体和抽样的关系&#xff0c;也就是说整体的叙事是从理论角度的&#xff0c;而估计器原理是从实践角度说事&#xff1b;为了表明概率理论&#xff08;不可…...

乐观锁与悲观锁的实现和应用

乐观锁与悲观锁&#xff1a;原理、实现与应用详解 在并发编程和数据库操作中&#xff0c;乐观锁和悲观锁是两种重要的并发控制策略&#xff0c;它们在原理、实现方式和应用场景上存在显著差异。下面我们将通过图文结合的方式&#xff0c;深入探讨这两种锁机制。 一、基本概念 1…...

PL/SQLDeveloper中数值类型字段查询后显示为科学计数法的处理方式

PL/SQLDeveloper中数值类型字段查询后显示为科学计数法的处理方式 文章目录 PL/SQLDeveloper中数值类型字段查询后显示为科学计数法的处理方式1. 查询效果2. 处理方式3. 再次查询 1. 查询效果 2. 处理方式 3. 再次查询...

【vue】Uniapp 打包Android 文件选择上传问题详解~

需求 uniapp兼容android app&#xff0c;pc&#xff0c;h5的文件选择并上传功能。 需要支持拍照和相册选择&#xff0c;以及选择其他类型文件上传~ 实践过程和问题 开始使用uni-file-picker组件 以为很顺利&#xff0c;android模拟器测试…… 忽略了平台兼容性提示~&#…...

ASR技术(自动语音识别)深度解析

ASR技术(自动语音识别)深度解析 自动语音识别(Automatic Speech Recognition,ASR)是将人类语音转换为文本的核心技术,以下是其全面解析: 一、技术原理架构 #mermaid-svg-QlJOWpMtlGi9LNeF {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:1…...

图论水题2

div2 361 D. Tree Requests 题意 对于一颗 n n n节点的树&#xff0c;每个节点有一个字母&#xff0c;有 m m m次询问&#xff0c;每次询问求对于顶点 v v v的子树中深度为 h h h的结点能否组成一个回文串$ (1 \leq n \leq m \leq 5 \cdot 10^5) $ 思路 关于 v v v的子树结…...

Ctrl-Crash 助力交通安全:可控生成逼真车祸视频,防患于未然

视频扩散技术虽发展显著&#xff0c;但多数驾驶数据集事故事件少&#xff0c;难以生成逼真车祸图像&#xff0c;而提升交通安全又急需逼真可控的事故模拟。为此&#xff0c;论文提出可控车祸视频生成模型 Ctrl-Crash&#xff0c;它以边界框、碰撞类型、初始图像帧等为条件&…...

网络编程之服务器模型与UDP编程

一、服务器模型 在网络通信中&#xff0c;通常要求一个服务器连接多个客户端 为了处理多个客户端的请求&#xff0c;通常有多种表现形式 1、循环服务器模型 一个服务器可以连接多个客户端&#xff0c;但同一时间只能连接并处理一个客户的请求 socket() 结构体 bind() listen() …...

Transformer-BiLSTM、Transformer、CNN-BiLSTM、BiLSTM、CNN五模型时序预测

Transformer-BiLSTM、Transformer、CNN-BiLSTM、BiLSTM、CNN五模型时序预测 目录 Transformer-BiLSTM、Transformer、CNN-BiLSTM、BiLSTM、CNN五模型时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Transformer-BiLSTM、Transformer、CNN-BiLSTM、BiLSTM、CNN五…...

阿里云服务器安装nginx并配置前端资源路径(前后端部署到一台服务器并成功访问)

​​​运行以下命令&#xff0c;安装Nginx相关依赖。 yum install -y gcc-c yum install -y pcre pcre-devel yum install -y zlib zlib-devel yum install -y openssl openssl-devel 运行wget命令下载Nginx 1.21.6。 您可以通过Nginx开源社区直接获取对应版本的安装包URL&…...

Ubuntu 下开机自动执行命令的方法

Ubuntu 下开机自动执行命令的方法&#xff08;使用 crontab&#xff09; 在日常使用 Ubuntu 或其他 Linux 系统时&#xff0c;我们常常需要让某些程序或脚本在系统启动后自动运行。例如&#xff1a;启动 Clash 代理、初始化服务、定时同步数据等。 本文将介绍一种简单且常用的…...

C++11新增重要标准(下)

前言 一&#xff0c;forward&#xff08;完美转发&#xff09; 二&#xff0c;可变参数模板 三&#xff0c;emplace系列接口 四&#xff0c;新增类功能 五&#xff0c;default与delete 六&#xff0c;lambda表达式 七&#xff0c;包装器 八&#xff0c;bind 在C11中新增…...

【第六篇】 SpringBoot的日志基础操作

简介 日志系统在软件开发中至关重要&#xff0c;用于调试代码、记录运行信息及错误堆栈。本篇文章不仅详细介绍了日志对象的创建及快速使用&#xff0c;还说明了日志持久化的两种配置方式和滚动日志的设置。实际开发需根据场景选择合适的日志级别和存储策略。文章内容若存在错误…...

Pluto论文阅读笔记

主要还是参考了这一篇论文笔记&#xff1a;https://zhuanlan.zhihu.com/p/18319150220 Pluto主要有三个创新点&#xff1a; 横向纵向用lane的query来做将轨迹投回栅格化地图&#xff0c;计算碰撞loss对数据进行正增强和负增强&#xff0c;让正增强的结果也无增强的结果相近&a…...

ubuntu显示器未知

在Ubuntu系统中&#xff0c;当外接显示器被识别为“未知设备”时&#xff0c;可通过以下日志文件进行问题诊断&#xff0c;结合Xorg日志和内核日志综合分析&#xff1a; &#x1f50d; 一、查看Xorg显示服务日志&#xff08;核心&#xff09; Xorg日志记录了图形界面的详细事…...

Faiss向量数据库全面解析:从原理到实战

Faiss向量数据库全面解析&#xff1a;从原理到实战 引言&#xff1a;向量搜索的时代需求 在AI技术爆发的今天&#xff0c;向量数据已成为表示文本、图像、音视频等内容的核心形式。Facebook AI研究院开源的Faiss&#xff08;Facebook AI Similarity Search&#xff09;作为高…...

matlab 2024a ​工具箱Aerospsce Toolbox报错​

Matlab R2024a中Aerospsce Toolbox报错 警告&#xff1a;Aerospace Toolbox and Aerospace Blockset licenses are required in ‘built-in/Spacecraft Dynamics’ 找到安装路径\MATLAB\R2024a\licenses文件夹license_****_R2024a.lic 里面工具箱名称出错&#xff0c;手动修改…...

使用有限计算实现视频生成模型的高效训练

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 视频生成的最新进展需要越来越高效的训练配方&#xff0c;以减轻不断上升的计算成本。在本报告中&#xff0c;我们介绍了 ContentV&#xff0c;这是一种 8B 参数文本到视频模型&#xff0c;在 256 …...

Server2003 B-1 Windows操作系统渗透

任务环境说明&#xff1a; 服务器场景&#xff1a;Server2003&#xff08;开放链接&#xff09; 服务器场景操作系统&#xff1a;Windows7 1.通过本地PC中渗透测试平台Kali对服务器场景Windows进行系统服务及版本扫描渗透测试&#xff0c;并将该操作显示结果中Telnet服务对应的…...

一次Oracle的非正常关闭

数据库自己会关闭吗&#xff1f; 从现象来说Oracle MySQL Redis等都会出现进程意外停止的情况。而这些停止都是非人为正常关闭或者暴力关闭&#xff08;abort或者kill 进程&#xff09; 一次测试环境的非关闭 一般遇到这种情况先看一下错误日志吧。 2025-06-01T06:26:06.35…...