Windows Community Toolkit 3.0 - CameraPreview
概述
Windows Community Toolkit 3.0 于 2018 年 6 月 2 日 Release,同时正式更名为 Windows Community Toolkit,原名为 UWP Community Toolkit。顾名思义,3.0 版本会更注重整个 Windows 平台的工具实现,而不再只局限于 UWP 应用,这从 Release Note 也可以看出来:https://github.com/Microsoft/WindowsCommunityToolkit/releases
我们从今年 3 月份开始陆续针对 Windows Community Toolkit 2.2 版本的特性和代码实现做了分析,从本篇开始,我们会对 3.0 版本做持续的分享,首先本篇带来的关于 CameraPreview 相关的分享。
CameraPreview 控件允许在 MediaPlayerElement 中简单预览摄像机帧源组的视频,开发者可以在所选摄像机实时获取 Video Frame 和 Bitmap,仅显示支持彩色视频预览或视频记录流。
这是一个非常有用的控件,之前在 Face++ 工作时,我们做的很多事情都是对摄像头传出的视频帧做人脸检测或关键点标注等操作。所以该控件对摄像头的控制,以及对视频帧的传出,就成了我们工作的资源源头,我们对视频帧做规范化,再进行算法处理,再把处理后的视频帧反馈到视频播放控件中,就可以完成检测,人脸美颜处理等很多操作。

Windows Community Toolkit Doc - CameraPreview
Windows Community Toolkit Source Code - CameraPreview
Namespace: Microsoft.Toolkit.Uwp.UI.Controls; Nuget: Microsoft.Toolkit.Uwp.UI.Controls;
开发过程
代码分析
首先来看 CameraPreview 的类结构:
- CameraPreview.Cpmstants.cs - 定义了 CameraPreview 的两个常量字符串;
- CameraPreview.Events.cs - 定义了 CameraPreview 的事件处理 PreviewFailed;
- CameraPreview.Properties.cs - 定义了 CameraPreview 的依赖属性 IsFrameSourceGroupButtonVisible;
- CameraPreview.cs - CameraPreview 的主要处理逻辑;
- CameraPreview.xaml - CameraPreview 的样式文件;
- PreviewFailedEventArgs.cs - 定义了 CameraPreview 的事件处理 PreviewFailed 的参数;

接下来我们主要关注 CameraPreview.xaml 和 CameraPreview.cs 的代码实现:
1. CameraPreview.xaml
CameraPreview 控件的样式文件组成很简单,就是用户播放预览视频帧的 MediaPlayerElement 和 FrameSourceGroup 按钮。
<Style TargetType="local:CameraPreview" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CameraPreview">
<Grid Background="{TemplateBinding Background}">
<MediaPlayerElement x:Name="MediaPlayerElementControl" HorizontalAlignment="Left">
</MediaPlayerElement>
<Button x:Name="FrameSourceGroupButton" Background="{ThemeResource SystemBaseLowColor}"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" Foreground="{ThemeResource SystemAltHighColor}" />
</Button>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
2. CameraPreview.cs
我们先来看一下 CameraPreview 的类组成:

整体的处理逻辑很清晰:
- 通过 OnApplyTemplate(), InitializeAsync(), SetUIControls(), SetMediaPlayerSource() 等方法初始化控件,初始化摄像头视频源组,选择视频源赋值 MediaPleyerElement 做展示;
- 通过 StartAsync() 方法开始使用摄像头视频源,开发者用于展示和获取每一帧图像 Bitmap;
- 使用完成后,调用 Stop() 来结束并释放摄像头资源;
而 CameraPreview 类中出现了一个很重要的帮助类 CameraHelper,它的作用是对摄像头资源的获取和视频帧的获取/处理,它是 CameraPreview 中的核心部分,下面我们来看 CameraHelper 的实现:

我们看到 CameraHelper 类中包括了获取摄像头视频源组,初始化和开始获取视频帧,接收视频帧进行处理,释放资源等方法,我们来看几个主要方法实现:
1. GetFrameSourceGroupsAsync()
获取视频源组的方法,使用 DeviceInformation 类获取所有类别为 VideoCapture 的设备,再使用 MediaFrameSourceGroup 类获取所有 mediaFrameSourceGroup,在 groups 中获取彩色视频预览和视频录制的所有 group。
public static async Task<IReadOnlyList<MediaFrameSourceGroup>> GetFrameSourceGroupsAsync()
{
if (_frameSourceGroups == null)
{
var videoDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
var groups = await MediaFrameSourceGroup.FindAllAsync();
// Filter out color video preview and video record type sources and remove duplicates video devices.
_frameSourceGroups = groups.Where(g => g.SourceInfos.Any(s => s.SourceKind == MediaFrameSourceKind.Color &&
(s.MediaStreamType == MediaStreamType.VideoPreview || s.MediaStreamType == MediaStreamType.VideoRecord))
&& g.SourceInfos.All(sourceInfo => videoDevices.Any(vd => vd.Id == sourceInfo.DeviceInformation.Id))).ToList();
}
return _frameSourceGroups;
}
2. InitializeAndStartCaptureAsync()
使用 GetFrameSourceGroupsAsync() 和 InitializeMediaCaptureAsync() 对视频源组和 MediaCapture 进行初始化;利用 MediaCapture 读取选择的视频源组对应的预览帧源,注册 Reader_FrameArrived 事件,开始读取操作,返回操作结果;
public async Task<CameraHelperResult> InitializeAndStartCaptureAsync()
{
CameraHelperResult result;
try
{
await semaphoreSlim.WaitAsync();
...
result = await InitializeMediaCaptureAsync();
if (_previewFrameSource != null)
{
_frameReader = await _mediaCapture.CreateFrameReaderAsync(_previewFrameSource);
if (Windows.Foundation.Metadata.ApiInformation.IsPropertyPresent("Windows.Media.Capture.Frames.MediaFrameReader", "AcquisitionMode"))
{
_frameReader.AcquisitionMode = MediaFrameReaderAcquisitionMode.Realtime;
}
_frameReader.FrameArrived += Reader_FrameArrived;
if (_frameReader == null)
{
result = CameraHelperResult.CreateFrameReaderFailed;
}
else
{
MediaFrameReaderStartStatus statusResult = await _frameReader.StartAsync();
if (statusResult != MediaFrameReaderStartStatus.Success)
{
result = CameraHelperResult.StartFrameReaderFailed;
}
}
}
_initialized = result == CameraHelperResult.Success;
return result;
}
...
}
3. InitializeMediaCaptureAsync()
上面方法中使用的初始化 MediaCapture 的方法,首先获取预览帧源,获取顺序是彩色预览 -> 视频录制;接着判断它支持的格式,包括视频帧率(>= 15 帧),媒体编码格式的支持(Nv12,Bgra8,Yuy2,Rgb32),按照视频宽高进行排序;对支持状态进行判断,如果状态可用,则返回默认最高分辨率;同时该方法会对权限等进行判断,对错误状态返回对应状态;只有状态为 CameraHelperResult.Success 时才是正确状态。
CameraHelperResult 中对应的错误状态有:CreateFrameReaderFailed,StartFrameReaderFailed,NoFrameSourceGroupAvailable,NoFrameSourceAvailable,CameraAccessDenied,InitializationFailed_UnknownError,NoCompatibleFrameFormatAvailable。
private async Task<CameraHelperResult> InitializeMediaCaptureAsync()
{
...
try
{
await _mediaCapture.InitializeAsync(settings);
// Find the first video preview or record stream available
_previewFrameSource = _mediaCapture.FrameSources.FirstOrDefault(source => source.Value.Info.MediaStreamType == MediaStreamType.VideoPreview
&& source.Value.Info.SourceKind == MediaFrameSourceKind.Color).Value;
if (_previewFrameSource == null)
{
_previewFrameSource = _mediaCapture.FrameSources.FirstOrDefault(source => source.Value.Info.MediaStreamType == MediaStreamType.VideoRecord
&& source.Value.Info.SourceKind == MediaFrameSourceKind.Color).Value;
}
if (_previewFrameSource == null)
{
return CameraHelperResult.NoFrameSourceAvailable;
}
// get only formats of a certain framerate and compatible subtype for previewing, order them by resolution
_frameFormatsAvailable = _previewFrameSource.SupportedFormats.Where(format =>
format.FrameRate.Numerator / format.FrameRate.Denominator >= // fps
&& (
||
||
|| ))?.OrderBy(format => format.VideoFormat.Width * format.VideoFormat.Height).ToList();
if (_frameFormatsAvailable == null || !_frameFormatsAvailable.Any())
{
return CameraHelperResult.NoCompatibleFrameFormatAvailable;
}
// set the format with the higest resolution available by default
var defaultFormat = _frameFormatsAvailable.Last();
await _previewFrameSource.SetFormatAsync(defaultFormat);
}
catch (UnauthorizedAccessException)
{ ... }
catch (Exception)
{ ... }
return CameraHelperResult.Success;
}
4. Reader_FrameArrived(sender, args)
获取到视频帧的处理,触发 FrameArrived 事件,传入 VideoFrame,开发者可以对 frame 做自己的处理。
private void Reader_FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
// TryAcquireLatestFrame will return the latest frame that has not yet been acquired.
// This can return null if there is no such frame, or if the reader is not in the
// "Started" state. The latter can occur if a FrameArrived event was in flight
// when the reader was stopped.
var frame = sender.TryAcquireLatestFrame();
if (frame != null)
{
var vmf = frame.VideoMediaFrame;
EventHandler<FrameEventArgs> handler = FrameArrived;
var frameArgs = new FrameEventArgs() { VideoFrame = vmf.GetVideoFrame() };
handler?.Invoke(sender, frameArgs);
}
}
调用示例
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d">
<StackPanel Orientation="Vertical" Margin="20">
<controls:CameraPreview x:Name="CameraPreviewControl">
</controls:CameraPreview>
<Image x:Name="CurrentFrameImage" MinWidth="300" Width="400" HorizontalAlignment="Left"></Image>
</StackPanel>
</Page>
// Initialize the CameraPreview control and subscribe to the events
CameraPreviewControl.PreviewFailed += CameraPreviewControl_PreviewFailed;
await CameraPreviewControl.StartAsync();
CameraPreviewControl.CameraHelper.FrameArrived += CameraPreviewControl_FrameArrived;
// Create a software bitmap source and set it to the Xaml Image control source.
var softwareBitmapSource = new SoftwareBitmapSource();
CurrentFrameImage.Source = softwareBitmapSource;
private void CameraPreviewControl_FrameArrived(object sender, FrameEventArgs e)
{
var videoFrame = e.VideoFrame;
var softwareBitmap = e.VideoFrame.SoftwareBitmap;
var targetSoftwareBitmap = softwareBitmap;
if (softwareBitmap != null)
{
if (softwareBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8 || softwareBitmap.BitmapAlphaMode == BitmapAlphaMode.Straight)
{
targetSoftwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}
await softwareBitmapSource.SetBitmapAsync(targetSoftwareBitmap);
}
}
总结
到这里我们就把 Windows Community Toolkit 3.0 中的 CameraPreview 的源代码实现过程讲解完成了,希望能对大家更好的理解和使用这个扩展有所帮助。
相信大家在做到很多跟摄像头有关的功能,比如人脸检测,视频直播的美颜处理,贴纸操作等操作时都会用到这个控件。如果大家有好玩的应用场景,欢迎多多交流,谢谢!
最后,再跟大家安利一下 WindowsCommunityToolkit 的官方微博:https://weibo.com/u/6506046490, 大家可以通过微博关注最新动态。
衷心感谢 WindowsCommunityToolkit 的作者们杰出的工作,感谢每一位贡献者,Thank you so much, ALL WindowsCommunityToolkit AUTHORS !!!
Windows Community Toolkit 3.0 - CameraPreview的更多相关文章
- Windows Community Toolkit 3.0 新功能 在WinForms 和 WPF 使用 UWP 控件
本文告诉大家一个令人震惊的消息,Windows Community Toolkit 有一个大更新,现在的版本是 3.0 .最大的提升就是 WinForm 和 WPF 程序可以使用部分 UWP 控件. ...
- Windows Community Toolkit 4.0 - DataGrid - Part03
概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Part02 中,我们针对 DataGrid 控件的 Utilities 部分做了详细分享.而在 ...
- Windows Community Toolkit 4.0 - DataGrid - Part02
概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Part01 中,我们针对 DataGrid 控件的 CollectionView 部分做了详细 ...
- Windows Community Toolkit 4.0 - DataGrid - Part01
概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Overview 中,我们对 DataGrid 控件做了一个概览的介绍,今天开始我们会做进一步的 ...
- Windows Community Toolkit 4.0 - DataGrid - Overview
概述 Windows Community Toolkit 4.0 于 2018 月 8 月初发布:Windows Community Toolkit 4.0 Release Note. 4.0 版本相 ...
- Windows Community Toolkit 3.0 - UniformGrid
概述 UniformGrid 控件是一个响应式的布局控件,允许把 items 排列在一组均匀分布的行或列中,以填充整体的可用显示空间,形成均匀的多个网格.默认情况下,网格中的每个单元格大小相同. 这是 ...
- Windows Community Toolkit 3.0 - InfiniteCanvas
概述 InfiniteCanvas 是一个 Canvas 控件,它支持无限画布的滚动,支持 Ink,文本,格式文本,画布缩放操作,撤销重做操作,导入和导出数据. 这是一个非常实用的控件,在“来画视频” ...
- Windows Community Toolkit 3.0 - Gaze Interaction
概述 Gaze Input & Tracking - 也就是视觉输入和跟踪,是一种和鼠标/触摸屏输入非常不一样的交互方式,利用人类眼球的识别和眼球方向角度的跟踪,来判断人眼的目标和意图,从而非 ...
- 与众不同 windows phone (44) - 8.0 位置和地图
[源码下载] 与众不同 windows phone (44) - 8.0 位置和地图 作者:webabcd 介绍与众不同 windows phone 8.0 之 位置和地图 位置(GPS) - Loc ...
随机推荐
- (后台)SQL Server 代理(已禁用代理 XP) 怎么解决(转)
百度知道搜索的答案: 在SQL Server Management Studio中连接到SQL Server实例后,会显示“SQL Server 代理”节点.如果当前该实例的Agent服务没有启动,“ ...
- 机器学习之隐马尔科夫模型HMM(六)
摘要 隐马尔可夫模型(Hidden Markov Model,HMM)是统计模型,它用来描述一个含有隐含未知参数的马尔科夫过程.其难点是从可观察的参数中确定该过程的隐含参数,然后利用这些参数来作进一步 ...
- weblogic的web.xml报错----Malformed UTF-8 char -- is an XML encoding declaration missing
weblogic报错: Malformed UTF-8 char -- is an XML encoding declaration missing 把编码修改成utf8,上传到weblogic就报这 ...
- x86服务器MCE(Machine Check Exception)问题
MCE现象 Intel在Pentium 4.Xenon和P6系列处理器中实现了机器检查(Machinecheck)架构,提供能够检测和报告硬件(机器)的错误机制,如系统总线错误.ECC错误.奇偶校验错 ...
- python 制作一对一聊天
用到的参考资料 https://blog.csdn.net/jia666666/article/details/81624550 https://blog.csdn.net/jia666666/art ...
- SQLServer数据库增删改查
一.数据库定义 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库.数据库的操作分为两种形式:一种是直接在数据库管理工具图形化界面进行操作:一种是使用数据库脚本进行操作,数据库脚本可 ...
- March 08th, 2018 Week 10th Thursday
Easy come, easy go. 易得则易失. Easy come, easy go, I finally undestand the phrase through somewhat hard ...
- iOS弹出UIViewController小视图
在TestViewController1中弹出TestViewController2 在TestViewController中点击按钮或者什么触发方法里面写入以下代码 TestViewControll ...
- Java访问级别修饰符
用途 控制其他类可以访问的字段或方法 修饰符 public.protected.no modifier(未声明).private 访问级别 修饰符 当前类 包 子类 其他包 public √ √ √ ...
- MySQL 初识别语句,数据库、表、行的增删改查
一.MySQL 开场语句 1.登陆 mysql -u root -p ; #回车然后输入密码 2.退出 eixt | quit #二者选其一 3.查看数据文件路径(配置文件中学习的) show glo ...