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 ...
随机推荐
- JHipster技术栈理解 - UAA原理分析
本文简要分析了UAA的认证机制和部分源码功能. UAA全称User Account and Authentication. 相关源码都是通过Jhipster生成,包括UAA,Gateway,Ident ...
- 腾讯云Centos安装gitlab
参考了网上很多人写的安装教程,结果并不好,最后阅读了官方的英文api,才安装成功,这里记录下来,方便以后使用.我的安装环境为腾讯云主机Centos7.3 64bit gitlab官方api地址点我试试 ...
- 2016-04-25-信息系统实践手记5-CACHE设计一例
layout: post title: 2016-04-25-信息系统实践手记5-CACHE设计一例 key: 20160425 tags: 业务 场景 CACHE 系统分析 系统设计 缓存 modi ...
- Linux中DNS的设置
1.查看本机的域名 hostname 2.修改DNS 临时修改: hostname desktop0.example.com 永久修改: hostnamectl set-hostname deskto ...
- 遍历结构体内部元素和值(Name and Value)
代码: DataStruct.MessageStruct ms = new DataStruct.MessageStruct();//实例化结构体 //遍历结构体 foreach (System ...
- MySQL 8.0.11安装配置
官网地址:https://dev.mysql.com/downloads/mysql/ 我这里是RHEL6.5的系统,因此选择RedHat 6 x86,64bit操作系统---下载第一个RPM Bun ...
- Oracle的实例恢复解析
在数据库服务器异常断电重启后,数据库会进行实例恢复,那么实例恢复的过程中Oracle做了什么操作呢?参考官网在这里做一下解释,菜鸟水平有限,欢迎勘正. 首先说下实例恢复的定义: Instance re ...
- c/c++ 模板与STL小例子系列<二> 模板类与友元函数
c/c++ 模板与STL小例子系列 模板类与友元函数 比如某个类是个模板类D,有个需求是需要重载D的operator<<函数,这时就需要用到友元. 实现这样的友元需要3个必要步骤 1,在模 ...
- android调试工具adb命令大全
转载: 一.adb介绍SDK的Tools文件夹下包含着Android模拟器操作的重要命令adb,adb的全称为(Android Debug Bridge就是调试桥的作用.通过adb我们可以在Eclip ...
- June 13. 2018 Week 24th. Wednesday
Life is too short to miss out anything; try to take it slowly. 生命短暂,放慢脚步,别错过任何沿途的风景. From Ferris Bue ...