WebCamSource.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright (c) 2021 homuler
  2. //
  3. // Use of this source code is governed by an MIT-style
  4. // license that can be found in the LICENSE file or at
  5. // https://opensource.org/licenses/MIT.
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using UnityEngine;
  11. #if UNITY_ANDROID
  12. using UnityEngine.Android;
  13. #endif
  14. namespace Mediapipe.Unity
  15. {
  16. public class WebCamSource : ImageSource
  17. {
  18. private readonly int _preferableDefaultWidth = 1280;
  19. private const string _TAG = nameof(WebCamSource);
  20. private readonly ResolutionStruct[] _defaultAvailableResolutions;
  21. public WebCamSource(int preferableDefaultWidth, ResolutionStruct[] defaultAvailableResolutions)
  22. {
  23. _preferableDefaultWidth = preferableDefaultWidth;
  24. _defaultAvailableResolutions = defaultAvailableResolutions;
  25. }
  26. private static readonly object _PermissionLock = new object();
  27. private static bool _IsPermitted = false;
  28. private WebCamTexture _webCamTexture;
  29. private WebCamTexture webCamTexture
  30. {
  31. get => _webCamTexture;
  32. set
  33. {
  34. if (_webCamTexture != null)
  35. {
  36. _webCamTexture.Stop();
  37. }
  38. _webCamTexture = value;
  39. }
  40. }
  41. public override int textureWidth => !isPrepared ? 0 : webCamTexture.width;
  42. public override int textureHeight => !isPrepared ? 0 : webCamTexture.height;
  43. public override bool isVerticallyFlipped => isPrepared && webCamTexture.videoVerticallyMirrored;
  44. public override bool isFrontFacing => isPrepared && (webCamDevice is WebCamDevice valueOfWebCamDevice) && valueOfWebCamDevice.isFrontFacing;
  45. public override RotationAngle rotation => !isPrepared ? RotationAngle.Rotation0 : (RotationAngle)webCamTexture.videoRotationAngle;
  46. private WebCamDevice? _webCamDevice;
  47. private WebCamDevice? webCamDevice
  48. {
  49. get => _webCamDevice;
  50. set
  51. {
  52. if (_webCamDevice is WebCamDevice valueOfWebCamDevice)
  53. {
  54. if (value is WebCamDevice valueOfValue && valueOfValue.name == valueOfWebCamDevice.name)
  55. {
  56. // not changed
  57. return;
  58. }
  59. }
  60. else if (value == null)
  61. {
  62. // not changed
  63. return;
  64. }
  65. _webCamDevice = value;
  66. resolution = GetDefaultResolution();
  67. }
  68. }
  69. public override string sourceName => (webCamDevice is WebCamDevice valueOfWebCamDevice) ? valueOfWebCamDevice.name : null;
  70. private WebCamDevice[] _availableSources;
  71. private WebCamDevice[] availableSources
  72. {
  73. get
  74. {
  75. if (_availableSources == null)
  76. {
  77. _availableSources = WebCamTexture.devices;
  78. }
  79. return _availableSources;
  80. }
  81. set => _availableSources = value;
  82. }
  83. public override string[] sourceCandidateNames => availableSources?.Select(device => device.name).ToArray();
  84. #pragma warning disable IDE0025
  85. public override ResolutionStruct[] availableResolutions
  86. {
  87. get
  88. {
  89. #if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
  90. if (webCamDevice is WebCamDevice valueOfWebCamDevice) {
  91. return valueOfWebCamDevice.availableResolutions.Select(resolution => new ResolutionStruct(resolution)).ToArray();
  92. }
  93. #endif
  94. return webCamDevice == null ? null : _defaultAvailableResolutions;
  95. }
  96. }
  97. #pragma warning restore IDE0025
  98. public override bool isPrepared => webCamTexture != null;
  99. public override bool isPlaying => webCamTexture != null && webCamTexture.isPlaying;
  100. private IEnumerator Initialize()
  101. {
  102. yield return GetPermission();
  103. if (!_IsPermitted)
  104. {
  105. yield break;
  106. }
  107. if (webCamDevice != null)
  108. {
  109. yield break;
  110. }
  111. availableSources = WebCamTexture.devices;
  112. if (availableSources != null && availableSources.Length > 0)
  113. {
  114. // webCamDevice = availableSources[0];
  115. // 施工区
  116. for (int i = 0; i < availableSources.Length; i++)
  117. {
  118. Debug.LogWarning(availableSources[i].name);
  119. if (availableSources[i].name == "MX Brio") //
  120. {
  121. webCamDevice = availableSources[i];
  122. break;
  123. }
  124. }
  125. // 施工区
  126. }
  127. }
  128. private IEnumerator GetPermission()
  129. {
  130. lock (_PermissionLock)
  131. {
  132. if (_IsPermitted)
  133. {
  134. yield break;
  135. }
  136. #if UNITY_ANDROID
  137. if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
  138. {
  139. Permission.RequestUserPermission(Permission.Camera);
  140. yield return new WaitForSeconds(0.1f);
  141. }
  142. #elif UNITY_IOS
  143. if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) {
  144. yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);
  145. }
  146. #endif
  147. #if UNITY_ANDROID
  148. if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
  149. {
  150. Debug.LogWarning("Not permitted to use Camera");
  151. yield break;
  152. }
  153. #elif UNITY_IOS
  154. if (!Application.HasUserAuthorization(UserAuthorization.WebCam)) {
  155. Debug.LogWarning("Not permitted to use WebCam");
  156. yield break;
  157. }
  158. #endif
  159. _IsPermitted = true;
  160. yield return new WaitForEndOfFrame();
  161. }
  162. }
  163. public override void SelectSource(int sourceId)
  164. {
  165. if (sourceId < 0 || sourceId >= availableSources.Length)
  166. {
  167. throw new ArgumentException($"Invalid source ID: {sourceId}");
  168. }
  169. webCamDevice = availableSources[sourceId];
  170. }
  171. public override IEnumerator Play()
  172. {
  173. yield return Initialize();
  174. if (!_IsPermitted)
  175. {
  176. throw new InvalidOperationException("Not permitted to access cameras");
  177. }
  178. InitializeWebCamTexture();
  179. webCamTexture.Play();
  180. yield return WaitForWebCamTexture();
  181. }
  182. public override IEnumerator Resume()
  183. {
  184. if (!isPrepared)
  185. {
  186. throw new InvalidOperationException("WebCamTexture is not prepared yet");
  187. }
  188. if (!webCamTexture.isPlaying)
  189. {
  190. webCamTexture.Play();
  191. }
  192. yield return WaitForWebCamTexture();
  193. }
  194. public override void Pause()
  195. {
  196. if (isPlaying)
  197. {
  198. webCamTexture.Pause();
  199. }
  200. }
  201. public override void Stop()
  202. {
  203. if (webCamTexture != null)
  204. {
  205. webCamTexture.Stop();
  206. }
  207. webCamTexture = null;
  208. }
  209. public override Texture GetCurrentTexture() => webCamTexture;
  210. private ResolutionStruct GetDefaultResolution()
  211. {
  212. var resolutions = availableResolutions;
  213. return resolutions == null || resolutions.Length == 0 ? new ResolutionStruct() : resolutions.OrderBy(resolution => resolution, new ResolutionStructComparer(_preferableDefaultWidth)).First();
  214. }
  215. private void InitializeWebCamTexture()
  216. {
  217. Stop();
  218. if (webCamDevice is WebCamDevice valueOfWebCamDevice)
  219. {
  220. webCamTexture = new WebCamTexture(valueOfWebCamDevice.name, resolution.width, resolution.height, (int)resolution.frameRate);
  221. return;
  222. }
  223. throw new InvalidOperationException("Cannot initialize WebCamTexture because WebCamDevice is not selected");
  224. }
  225. private IEnumerator WaitForWebCamTexture()
  226. {
  227. const int timeoutFrame = 2000;
  228. var count = 0;
  229. Debug.Log("Waiting for WebCamTexture to start");
  230. yield return new WaitUntil(() => count++ > timeoutFrame || webCamTexture.width > 16);
  231. if (webCamTexture.width <= 16)
  232. {
  233. throw new TimeoutException("Failed to start WebCam");
  234. }
  235. }
  236. private class ResolutionStructComparer : IComparer<ResolutionStruct>
  237. {
  238. private readonly int _preferableDefaultWidth;
  239. public ResolutionStructComparer(int preferableDefaultWidth)
  240. {
  241. _preferableDefaultWidth = preferableDefaultWidth;
  242. }
  243. public int Compare(ResolutionStruct a, ResolutionStruct b)
  244. {
  245. var aDiff = Mathf.Abs(a.width - _preferableDefaultWidth);
  246. var bDiff = Mathf.Abs(b.width - _preferableDefaultWidth);
  247. if (aDiff != bDiff)
  248. {
  249. return aDiff - bDiff;
  250. }
  251. if (a.height != b.height)
  252. {
  253. // prefer smaller height
  254. return a.height - b.height;
  255. }
  256. // prefer smaller frame rate
  257. return (int)(a.frameRate - b.frameRate);
  258. }
  259. }
  260. }
  261. }