记录C#在Windows11 下使用原生的UWP的 MediaCapture 捕获摄像头画面
我们在做大屏端应用,都会遇到摄像头画面捕获和显示的技术。现在主流的方式大部分都是通过 UVC的方式去捕获摄像头的画面
比如:AForge.NET 组件库。甚至于我们组的大牛还实现了可以支持播放4K/8K的 摄像头播放器。
本文记录一下使用UWP原生的 MediaCapture 捕获摄像头,实现低延迟的摄像头画面播放。
MediaCaptuer是UWP应用的API: Basic photo, video, and audio capture with MediaCapture - Windows apps | Microsoft Learn
如果应用本身是.NetFramwork 的,需要手动引用 Nuget包:Microsoft.Windows.SDK.Contracts
引用:Microsoft.Toolkit.Wpf.UI.XamlHost,让WPF 程序支持 使用 UWP 控件
<PackageReference Include="Microsoft.Toolkit.Wpf.UI.XamlHost" Version="6.1.2" /> <PackageReference Include="Microsoft.Windows.SDK.Contracts" Version="10.0.26100.1" />
1、首先先获取设备,并初始化
public async Task<IList<DeviceInformation>> GetCaptureInfos() { var videos =await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture); if (videos.Count >0) { var displayDevices = videos.Where(x => x.Name.Contains(HdmiCaptureName)); return displayDevices.ToList(); } return new List<DeviceInformation>(); } private async Task<bool> InitCaptureInternal(string deviceId) { try { await _semaphoreSlim.WaitAsync(); if (_isCameraRunning) { Log.Info($"Hdmi 已经打开,不重新初始化"); return true; } _isCameraRunning = true; mediaCapture = new MediaCapture(); var settings = new MediaCaptureInitializationSettings() { VideoDeviceId = deviceId, StreamingCaptureMode = StreamingCaptureMode.Video, }; mediaCapture.Failed += MediaCapture_Failed; try { await mediaCapture.InitializeAsync(settings); } catch (UnauthorizedAccessException ex) { Log.Info($"摄像头没权限:{ex}"); } var frameSource = mediaCapture.FrameSources?.First().Value; if (frameSource != null) { _isFirstFrame = true; _mediaFrameReader = await mediaCapture.CreateFrameReaderAsync(frameSource); _mediaFrameReader.FrameArrived += Reader_FrameArrived; var res = await _mediaFrameReader.StartAsync(); Log.Info($"_mediaFrameReader开启状态:{res.ToString()}"); } else { Log.Info("mediaCapture.FrameSources为空,不监听流数据"); } await StartCapturePreview(); return true; } catch (Exception ex) { Log.Info($"初始化MediaCapture异常:{ex}"); return false; } finally { _semaphoreSlim.Release(); } }
2、播放摄像头画面
public async Task<int> StartCameraFrameAsync() { var res = -1; try { var result = await Application.Current.Dispatcher.Invoke(async () => { if (string.IsNullOrWhiteSpace(_deviceId)) { var hdmiDevices = await _devFilterManager.GetCaptureInfos(); var hdmiDevice = hdmiDevices.FirstOrDefault(); if (hdmiDevice == null) { H3CLog.Info($"没有捕获设备"); return -1; } _deviceId = hdmiDevice.Id; } var result = await InitCaptureInternal(_deviceId); if (!result) { result = await ReConnectAsync(_deviceId); } return result ? 0 : -1; }).TimeOutAsync(6000); if (result.Success) { res = result.Data; } } catch (Exception e) { Log.Error($"StartCameraFrameAsync异常", e); } return res; }
3、在xmal 端,需要定义UWP的控件去承载 摄像头画面
<!--自定义UWP的窗口控件-->
<xamlHost:WindowsXamlHost x:Name="VideoViewHost" Visibility="{Binding ImageVisibility}"/>
private void DrawUwpElement() { Windows.UI.Xaml.Controls.Grid mainGrid = new Windows.UI.Xaml.Controls.Grid(); var imageUri = UwpResourceHelper.NarrowActivateUri; MaximizedImg = new Windows.UI.Xaml.Controls.Image { Source = new Windows.UI.Xaml.Media.Imaging.BitmapImage(imageUri), HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Left, VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Bottom, Stretch = Stretch.Fill, Width = 40, Height = 40, Margin = new Windows.UI.Xaml.Thickness(24, 0, 0, 24), Tag = imageUri }; Windows.UI.Xaml.Controls.CaptureElement captureElement = new CaptureElement() { Stretch = Stretch.Fill }; mainGrid.Children.Add(captureElement); mainGrid.Children.Add(MaximizedImg); HdmiProvider.CaptureElement = captureElement; VideoViewHost.Child = mainGrid; _videoViewOperation = new VideoViewOperation(this, captureElement, MaximizedImg, VideoViewHost); _videoViewOperation.VideoViewStatusChanged += _videoViewOperation_VideoViewStatusChanged; }
4、展示画面如下图所示:

之前冬哥有对比过各个组件的延迟差异,延迟相关的验证可以参考该博客链接:.NET 摄像头采集 - 唐宋元明清2188 - 博客园

浙公网安备 33010602011771号