文章目录

  • 前言
  • 关键代码
  • 代码中的不足

前言

真的太开心了,查阅了很多文档终于解决了这个问题。

事情的起因是由于我在项目中使用了 MixedReality-WebRTC 库实现与 Web 端的实时音视频通讯,同时呢,还在项目中使用了 Unity 封装好的 Windows API 实现图像捕获功能(详情可看:Hololens2开发笔记-捕获照片到内存并上传至服务器 这篇文章),并将图像数据上传到云端。在实践的过程中就出现了问题了,它俩居然是冲突的 emmmm

当 HoloLens2 在和 Web 端进行实时音视频通讯的时候,如果我使用了图像捕获功能,程序就崩溃了,呜呜呜,我就猜想原因可能就出在对相机资源的抢占上吧

好在天无绝人之路,我无意中发现,当在使用 MixedReality-WebRTC 库的时候,如果使用基于 Unity 封装好的 Windows API 来捕获图像会一直失败,但是调用系统相机拍照功能,居然不会冲突,可以在实时音视频传输的同时拍摄图像,诶这就有点意思了

开始查阅资料,重新细读了 HoloLens2 官方文档中的 可定位相机 这篇文章,这可真是给了我大大的帮助啊

首先我了解到我之前使用的一直与 WebRTC 冲突的图像捕获功能是基于 Unity 封装好的 Windows API,如下图

然后,我又在 面向开发人员的 Mixed Reality 捕获 这篇文章中,发现 HoloLens2 是可以共享对相机的访问的(我之前开发过 HoloLens1 代,就遇到过对相机资源的抢占这种情况,对这个问题印象就比较深刻),如下图

既然可以设置 SharedMode 属性来共享对相机的访问,那么 MixedReality-WebRTC 库对视频流的捕获到底是不是 SharedReadOnly 共享模式呢

开始细读 MixedReality-WebRTC 库提供的那些 Unity 脚本文件,终于,在 WebcamSource.cs 脚本中,发现了 SharedReadOnly 设置代码,如下

这下事情就很清楚了,MixedReality-WebRTC 库是以 SharedReadOnly 共享相机模式进行运行的,而 Unity 封装好的 Windows API 实现的图像捕获功能可能是以 ExclusiveControl 独占模式来捕获图像的,所以就造成了冲突。

而系统自带的拍照功能应该是以 SharedReadOnly 模式运行,所以就可以在实时音视频通讯的同时,来拍摄图像了。后面的代码实践中也证实了这一点

面向开发人员的 Mixed Reality 捕获 这篇文档还提到开发人员只需编写几行代码,即可使用 相机捕获 UI API 获取用户捕获的混合现实照片或视频。

所有的解决办法都指向了使用 UWP 原生 MediaCapture API 来使用图像捕获功能便可解决冲突

在经历了几个日夜的学习和踩坑后,终于成功解决问题,开心哈哈~~

关键代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;#if UNITY_WSA && !UNITY_EDITOR
using System.Threading.Tasks;
using global::Windows.UI.Core;
using global::Windows.Foundation;
using global::Windows.Media.Core;
using global::Windows.Media.Capture;
using global::Windows.ApplicationModel.Core;
using global::Windows.Storage;
using global::Windows.Storage.Streams;
using global::Windows.Graphics.Imaging;
using global::Windows.Media.MediaProperties;
using global::Windows.Storage.FileProperties;
#endifpublic class MediaCaptureUtil : MonoBehaviour
{public static MediaCaptureUtil Instance;private void Awake(){Instance = this;}private void CleanPreviousImages(){string directoryPath = Path.Combine(Application.persistentDataPath, "StreetViewFolder");DirectoryInfo info = new DirectoryInfo(directoryPath);var fileInfo = info.GetFiles();foreach (var file in fileInfo){try{file.Delete();}catch (Exception){Debug.Log($"Cannot delete file: {file.Name}");}}}public void CaptureImage(){// 清理过时的图片资源CleanPreviousImages();// 在 UWP 线程下捕获图片到文件
#if UNITY_WSA && !UNITY_EDITORtry{// MediaCapture API 的调用只能运行在 UWP 线程下UnityEngine.WSA.Application.InvokeOnUIThread(() => CaptureImageByMediaCapture(), waitUntilDone: true);//UnityEngine.WSA.Application.InvokeOnUIThread(() => CaptureImageByCameraCaptureUI(), //    waitUntilDone: true);}catch (Exception ex){Debug.LogError($"Camera access failure: {ex.Message}.");return;}
#endif// 延迟 1s 执行,确保图片已成功捕获到文件StartCoroutine(Delay(1, () =>{// 从文件中读取图片,转为字节数组string filePath = Path.Combine(Application.persistentDataPath, "StreetViewFolder/Photo.jpg");byte[] imageBytes = GetImageAsByteArray(filePath);// 自定义视觉分析if (imageBytes != null){StartCoroutine(ImageProcessor.Instance.AnalyseLastImageCaptured(imageBytes));}else{LogManager.Instance.PrintError("failed to read the specified image");}}));}private byte[] GetImageAsByteArray(string imageFilePath){using (FileStream fileStream = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read)){using (BinaryReader binaryReader = new BinaryReader(fileStream)){return binaryReader.ReadBytes((int)fileStream.Length);}}}private IEnumerator Delay(float second, Action OnFinished){yield return new WaitForSeconds(second);OnFinished();}#if UNITY_WSA && !UNITY_EDITOR/// <summary>/// 使用 MediaCapture API 捕获照片./// </summary>/// <remarks>/// This must be called from the main UWP UI thread (not the main Unity app thread)./// </remarks>async private void CaptureImageByMediaCapture(){MediaCapture mediaCapture = new MediaCapture();var settings = new MediaCaptureInitializationSettings{// 最重要的属性,使 MediaCapture 可以共享对相机的访问SharingMode = MediaCaptureSharingMode.SharedReadOnly};await mediaCapture.InitializeAsync(settings);// 将图片数据都保存到指定文件夹StorageFolder destinationFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("StreetViewFolder",CreationCollisionOption.OpenIfExists);// 捕获图片using (var captureStream = new InMemoryRandomAccessStream()){await mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), captureStream);StorageFile file = await destinationFolder.CreateFileAsync("Photo.jpg", CreationCollisionOption.ReplaceExisting);using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite)){var decoder = await BitmapDecoder.CreateAsync(captureStream);var encoder = await BitmapEncoder.CreateForTranscodingAsync(fileStream, decoder);var properties = new BitmapPropertySet {{ "System.Photo.Orientation", new BitmapTypedValue(PhotoOrientation.Normal, PropertyType.UInt16) }};await encoder.BitmapProperties.SetPropertiesAsync(properties);await encoder.FlushAsync();}}}/// <summary>/// 使用 CameraCaptureUI API 捕获照片./// </summary>/// <remarks>/// This must be called from the main UWP UI thread (not the main Unity app thread)./// </remarks>async private void CaptureImageByCameraCaptureUI(){CameraCaptureUI captureUI = new CameraCaptureUI();captureUI.PhotoSettings.Format = CameraCaptureUIPhotoFormat.Jpeg;captureUI.PhotoSettings.CroppedSizeInPixels = new Size(200, 200);StorageFile photo = await captureUI.CaptureFileAsync(CameraCaptureUIMode.Photo);StorageFolder destinationFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("ProfilePhotoFolder",CreationCollisionOption.OpenIfExists);await photo.CopyAsync(destinationFolder, "ProfilePhoto.jpg", NameCollisionOption.ReplaceExisting).AsTask();await photo.DeleteAsync();}
#endif
}

其中,上传图片数据到服务端代码可以参考 Hololens2开发笔记-捕获照片到内存并上传至服务器 和 Hololens2开发笔记-捕获照片成文件并上传至服务器 这两篇文章~

代码中的不足

虽然成功解决了问题,但现在代码中还是有点小不足的

主要就是图片捕获是在 UWP 线程下运行的,而读取捕获的图片文件是在 Unity 线程下运行的,这就涉及到线程的通信问题了

我在代码中为了确保图片数据已经被刷存到了文件中,只是简单的延迟了 1s 后才去读取图片数据,这里应该会有更好的处理办法,但由于知识所限,就只能先这样了,如果有更好的处理办法,欢迎给我留言~

学无止境哈哈哈

如果这篇文章对您有帮助,欢迎关注我的 github 项目⭐ ο(=•ω<=)ρ⌒☆

HoloLens2开发笔记-使用UWP原生MediaCapture解决图像捕获与WebRTC视频流冲突问题相关推荐

  1. Java开发笔记(一百零一)通过加解锁避免资源冲突

    前面介绍了如何通过线程同步来避免多线程并发的资源冲突问题,然而添加synchronized的方式只在简单场合够用,在一些高级场合就暴露出它的局限性,包括但不限于下列几点: 1.synchronized ...

  2. Hololens2开发笔记-构建国际象棋应用(unreal)

    文章目录 前言 入门 必备知识 安装 Visual Studio 2019 初始化你的项目 目标 创建新的 Unreal 项目 启用所需插件 创建关卡 导入资产 添加蓝图 使用材质 填充场景 Mixe ...

  3. Hololens2开发笔记-重刷系统(正常发布版本和内部预览版本)

    文章目录 刷机:正常发布版本 刷机:内部预览版本 这个过程心情真的是大起大落,真不容易.感兴趣的可以看看我的踩坑记录~ 回归正传,这里给出 HoloLens2 两个版本的刷机教程:正常发布版本和内部预 ...

  4. Hololens2开发笔记-研究模式API文档翻译

    文章目录 综述 大纲 主传感器读取循环 传感器类型 相机传感器 惯性传感器 传感器坐标帧 传感器 传感器帧 VLC帧载荷 AHAT和长抛摄像机帧载荷 长抛失效 AHAT无效 IMU帧载荷 同意提示 设 ...

  5. Unity HoloLens2 开发笔记(六):使用眼动追踪 追踪物体

    注:只有 HoloLens 2 支持眼动追踪,1代HoloLens并不支持. 1.确保启用了眼动追踪功能 Mixed Reality Toolkit > Utilities > Confi ...

  6. Hololens2开发笔记-获取经纬度位置信息(unity)

    文章目录 环境 相关代码 效果展示 注意事项 环境 Hololen2 Windows 10 Unity 2019.4.19f1c1 Visual Studio 2019 MRTK 2.5.4 相关代码 ...

  7. OpenCV开发笔记(十):OpenCV图像颜色通道分离和图像颜色多通道混合

    若该文为原创文章,未经允许不得转载 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/101420314 各位读者,知识无穷而人力有穷, ...

  8. Opencv开发笔记(三):使用形态学滤波对图像进行边缘及角点检测

    形态学滤波可以用于检测图像中指定的特征点,接下来这篇文章我们运用Opencv中的形态学函数来检测灰度图中的直角和角点. 首先我们为了方便使用先封装一个名称为MorphoFeatures的类,然后就可以 ...

  9. OpenCV开发笔记(二):cvui交互界面

    若该文为原创文章,未经允许不得转载 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/100110016 各位读者,知识无穷而人力有穷, ...

  10. 张高兴的 Xamarin.Forms 开发笔记:为 Android 与 iOS 引入 UWP 风格的汉堡菜单 ( MasterDetailPage )...

    所谓 UWP 样式的汉堡菜单,我曾在"张高兴的 UWP 开发笔记:汉堡菜单进阶"里说过,也就是使用 Segoe MDL2 Assets 字体作为左侧 Icon,并且左侧使用填充颜色 ...

最新文章

  1. Oracle 维护数据的完整性 一 索引
  2. centos7安装nginx和php,centos7安装nginx1.10和php7
  3. 同理心案例及故事分享_神经形态,视觉可及性和同理心
  4. 三种方式使得iOS应用能够在后台进行数据更新和下载
  5. django-正向查找
  6. servlet请求的执行过程_Springmvc执行流程
  7. HBase笔记整理(二)
  8. 一张图读懂MVC设计模式,从用户发起请求到获取响应,应用内部到底是如何数据流转、业务串联的
  9. Linux Shell 流程控制语句实例
  10. ThinkPhp项目部署到Linux session_start() 报错:failed:No such file or directory
  11. 证据理论(2)—— 多种合成公式
  12. 对数的matlab代码,Matlab的对数
  13. 调用百度AI开放平台实现图片文字识别
  14. Qt 出现空指针错误:The inferior stopped because it received a signal from the Operating System
  15. BI神器Power Query(6)-- PQ从工作簿导入数据(2/2)
  16. Java session write
  17. 分布式学习笔记001
  18. c语言常用算法pdf,妙趣横生的算法(C语言实现 第2版) 带目录完整pdf[94MB]
  19. 黑帽seo收徒之 微信视频号创作 变现
  20. jdbc shadring 扩容_shadring-jdbc解决查询数据库分库分表的问题

热门文章

  1. discuz模板风格制作入门
  2. Linux命令学习(1) cat命令详解
  3. 华硕笔记本Delete键和Insert键合二为一与分离
  4. cad计算机清空按键,cad delete键不能用怎么办-解决cad按delete键不能删除的方法 - 河东软件园...
  5. Python3---有关日期的处理---最近自然周最近自然月最近一周最近一月---dateutil模块
  6. go如何实现图片拼接,文字书写
  7. UE4 记录 UE4 中贴图
  8. Ubuntu18.04LTS安装TigerVNC
  9. word文档通配符换行_PDF如何转化成Word文档?
  10. 《嵌入式-STM32开发指南》第三部分 外设篇 - 第4章 超声波测距