如何使用C#创建Windows Webcam应用

简介: 原文:如何使用C#创建Windows Webcam应用 最近想用C#写一个camera的应用。搜索了Google和StackOverflow,发现大部分的sample用了WIA或者DirectShow。
原文: 如何使用C#创建Windows Webcam应用

最近想用C#写一个camera的应用。搜索了Google和StackOverflow,发现大部分的sample用了WIA或者DirectShow。WIA和DirectShow都没有直接提供C#接口,所以实现起来也比较复杂。试着运行了几个WIA的工程,在Windows10上还不起作用。发现微软提供了一个MediaCapture类,包含了丰富的C#接口,可用于音频,视频。要用这个类,就需要创建一个UWP工程。这里分享下如何使用MediaCapture的C#接口来获取camera的每一帧数据。

微软示例

微软在GitHub上放了大量的UWP示例,里面包含了各种camera的接口使用方法。

在Android中要获取camera的每一帧数据,可以通过onPreviewCallback的回调函数实现。参考CameraFrames这个示例,也可以实现类似的功能。

如何创建Windows Webcam应用

1. 在package.appxmanifest中获取webcam权限:

2. 创建image element用于绘制webcam的预览界面:

3. 通过Image Element初始化FrameRenderer :

_frameRenderer = new FrameRenderer(PreviewImage);

4. 初始化MediaCapture对象:

// Create a new media capture object.
_mediaCapture = new MediaCapture();
 
var settings = new MediaCaptureInitializationSettings()
{
    // Select the source we will be reading from.
    SourceGroup = groupModel.SourceGroup,
 
    // This media capture has exclusive control of the source.
    SharingMode = MediaCaptureSharingMode.ExclusiveControl,
 
    // Set to CPU to ensure frames always contain CPU SoftwareBitmap images,
    // instead of preferring GPU D3DSurface images.
    MemoryPreference = MediaCaptureMemoryPreference.Cpu,
 
    // Capture only video. Audio device will not be initialized.
    StreamingCaptureMode = StreamingCaptureMode.Video,
};
 
try
{
    // Initialize MediaCapture with the specified group.
    // This can raise an exception if the source no longer exists,
    // or if the source could not be initialized.
    await _mediaCapture.InitializeAsync(settings);
    _logger.Log($"Successfully initialized MediaCapture for {groupModel.DisplayName}");
}
catch (Exception exception)
{
    _logger.Log(exception.Message);
    DisposeMediaCapture();
}

5. 使用DeviceWatcher 列出所有设备:

var deviceSelector = MediaFrameSourceGroup.GetDeviceSelector();
_watcher = DeviceInformation.CreateWatcher(deviceSelector);
_watcher.Added += Watcher_Added;
_watcher.Removed += Watcher_Removed;
_watcher.Updated += Watcher_Updated;
_watcher.Start();
 
private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    await AddDeviceAsync(args.Id);
}
 
private async Task AddDeviceAsync(string id)
{
    var group = await MediaFrameSourceGroup.FromIdAsync(id);
    if (group != null)
    {
        await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                _sourceCollection.Add(new FrameSourceGroupModel(group));
            });
    }
}

6. 针对用户的选择更新设备源:

_mediaCapture.FrameSources.TryGetValue(info.SourceInfo.Id, out _source);

7. 通过MediaFrameReader 注册回调函数:

if (_source != null)
{
    _reader = await _mediaCapture.CreateFrameReaderAsync(_source);
    _reader.FrameArrived += Reader_FrameArrived;
}

8. 启动webcam:

MediaFrameReaderStartStatus result = await _reader.StartAsync();

9. 读取并绘制每一帧数据:

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.
    using (var frame = sender.TryAcquireLatestFrame())
    {
        _frameRenderer.ProcessFrame(frame);
    }
}
 
public void ProcessFrame(MediaFrameReference frame)
{
    var softwareBitmap = FrameRenderer.ConvertToDisplayableImage(frame?.VideoMediaFrame);
 
    if (softwareBitmap != null)
    {
        // Swap the processed frame to _backBuffer and trigger UI thread to render it
        softwareBitmap = Interlocked.Exchange(ref _backBuffer, softwareBitmap);
 
        // UI thread always reset _backBuffer before using it.  Unused bitmap should be disposed.
        softwareBitmap?.Dispose();
 
        // Changes to xaml ImageElement must happen in UI thread through Dispatcher
        var task = _imageElement.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            async () =>
            {
                // Don't let two copies of this task run at the same time.
                if (_taskRunning)
                {
                    return;
                }
                _taskRunning = true;
 
                // Keep draining frames from the backbuffer until the backbuffer is empty.
                SoftwareBitmap latestBitmap;
                while ((latestBitmap = Interlocked.Exchange(ref _backBuffer, null)) != null)
                {
                    var imageSource = (SoftwareBitmapSource)_imageElement.Source;
                    await imageSource.SetBitmapAsync(latestBitmap);
                    latestBitmap.Dispose();
                }
 
                _taskRunning = false;
            });
    }
}

参考

Basic photo, video, and audio capture with MediaCapture

源码

https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/CameraFrames

目录
相关文章
|
29天前
|
Java 数据库 C#
C#winforms实现windows窗体人脸识别
C#winforms实现windows窗体人脸识别
29 0
|
2月前
|
自然语言处理 C# Windows
C#开源免费的Windows右键菜单管理工具
C#开源免费的Windows右键菜单管理工具
|
3月前
|
API Python Windows
python3应用windows api对后台程序窗口及桌面截图并保存的方法
python3应用windows api对后台程序窗口及桌面截图并保存的方法
89 1
|
4月前
|
安全 C# Windows
C#开源的一个能利用Windows通知栏背单词的软件 - ToastFish
C#开源的一个能利用Windows通知栏背单词的软件 - ToastFish
|
1月前
|
数据可视化 数据库 C++
Qt 5.14.2揭秘高效开发:如何用VS2022快速部署Qt 5.14.2,打造无与伦比的Windows应用
Qt 5.14.2揭秘高效开发:如何用VS2022快速部署Qt 5.14.2,打造无与伦比的Windows应用
|
2月前
|
安全 Linux iOS开发
上传 iOS 应用变得更加容易 - 在 Windows 上架 iOS APP 的工具介绍
上传 iOS 应用变得更加容易 - 在 Windows 上架 iOS APP 的工具介绍
|
3月前
|
存储 Kubernetes 安全
虚拟机测试Windows Server 2016原地升级2019,应用和数据完美保留
Windows Server 2016可以无缝升级到2019版本,确保应用程序和数据在原地升级过程中完整保留。
102 0
|
3月前
|
Linux iOS开发 Windows
windows 如何上架 ios 应用到 app store
windows 如何上架 ios 应用到 app store
|
3月前
|
消息中间件 Java Kafka
windows下kafka的环境配置及rdkafka库的应用
windows下kafka的环境配置及rdkafka库的应用
|
3月前
|
API C# C++
微软官方发布的C#开源、免费、实用的Windows工具箱
微软官方发布的C#开源、免费、实用的Windows工具箱