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 ...
随机推荐
- Python 爬虫实例(爬百度百科词条)
爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成.爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入 ...
- windows10 专业版的远程服务器管理工具下载
一.安装远程服务器管理工具: 下载地址: https://www.microsoft.com/zh-cn/download/details.aspx?id=45520 二.关闭远程服务器管理工具: 1 ...
- c/c++ 智能指针 shared_ptr 和 new结合使用
智能指针 shared_ptr 和 new结合使用 用make_shared函数初始化shared_ptr是最推荐的,但有的时候还是需要用new关键字来初始化shared_ptr. 一,先来个表格,唠 ...
- c++ 右值引用,move关键字
c++ move关键字 move的由来:在 c++11 以前存在一个有趣的现象:T& 指向 lvalue (左传引用), const T& 既可以指向 lvalue 也可以指向 rv ...
- windows下安装consul
Consul 是一个支持多数据中心分布式高可用的服务发现和配置共享的服务软件, 由 HashiCorp 公司用 Go 语言开发, 基于 Mozilla Public License 2.0 的协议进行 ...
- 【2018.05.05 C与C++基础】C++中的自动废料收集:概念与问题引入
在阅读C++语言的设计与演化一书时,作者多次提到希望能设计出一个自动废料收集,然而出于种种考虑,始终未将自动废料收集纳入标准讨论中,而是由Coder自己考虑是否在程序中实现废料收集. 当然了,许多Ja ...
- 《Java大学教程》--第3章 迭代
迭代(iteration).重复(repetition):三种循环* for: 重复执行固定次数* while: 重复执行不固定次数* do...while: 比while至少多一次 1.答:P47迭 ...
- echarts设置y轴值间隔
其中min.max可以自定义可以动态获取数据 yAxis : [ { type : 'value', axi ...
- C#、Java中的一些小知识点总结(持续更新......)
前言:在项目中,有时候一些小的知识,总是容易让人忽略,但是这些功能加在项目中往往十分的有用,因此笔者在这里总结项目中遇到的一些实用的小知识点,以备用,并持续更新...... 1.禁用DataGridV ...
- Python requests模块解析XML
检查QQ是否在线(api感觉不准) import requests from xml.etree import ElementTree qq_str = input('please input the ...