201 lines
6.0 KiB
C#
201 lines
6.0 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
#if UNITY_ANDROID
|
|
using UnityEngine.Android;
|
|
#endif
|
|
|
|
namespace Aisu.SIM1.Face.Samples.FaceDetectionDemo
|
|
{
|
|
/// <summary>
|
|
/// MVP 摄像头预览,使用 WebCamTexture。
|
|
/// </summary>
|
|
public sealed class FaceDetectionDemoCamera : MonoBehaviour
|
|
{
|
|
[SerializeField] private RawImage m_preview; // 预览图。
|
|
[SerializeField] private AspectRatioFitter m_aspectFitter; // 宽高比适配。
|
|
|
|
private WebCamTexture m_texture; // 当前摄像头纹理。
|
|
private FaceDetectionDemoLogger m_logger; // 日志。
|
|
private WebCamDevice[] m_devices; // 当前摄像头设备列表。
|
|
|
|
public bool IsOpen => m_texture != null && m_texture.isPlaying;
|
|
|
|
/// <summary>
|
|
/// 绑定 UI 和日志。
|
|
/// </summary>
|
|
public void Bind(RawImage preview, AspectRatioFitter aspectFitter, FaceDetectionDemoLogger logger)
|
|
{
|
|
m_preview = preview;
|
|
m_aspectFitter = aspectFitter;
|
|
m_logger = logger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 枚举摄像头名称。
|
|
/// </summary>
|
|
public string[] GetDeviceNames()
|
|
{
|
|
m_devices = WebCamTexture.devices;
|
|
var names = new List<string>();
|
|
for (var i = 0; i < m_devices.Length; i++)
|
|
{
|
|
names.Add(i + ": " + m_devices[i].name);
|
|
m_logger?.Info("摄像头[" + i + "] " + m_devices[i].name + ",前置=" + m_devices[i].isFrontFacing + ",自动对焦=" + m_devices[i].isAutoFocusPointSupported);
|
|
}
|
|
|
|
return names.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 请求权限后枚举摄像头。
|
|
/// </summary>
|
|
public void GetDeviceNamesWhenAuthorized(Action<string[]> callback)
|
|
{
|
|
StartCoroutine(GetDeviceNamesWhenAuthorizedRoutine(callback));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 打开摄像头。
|
|
/// </summary>
|
|
public void Open(int deviceIndex)
|
|
{
|
|
Close();
|
|
if (m_devices == null || m_devices.Length == 0)
|
|
{
|
|
m_logger?.Error("没有可用摄像头");
|
|
return;
|
|
}
|
|
|
|
if (deviceIndex < 0 || deviceIndex >= m_devices.Length)
|
|
{
|
|
m_logger?.Error("摄像头索引无效: " + deviceIndex);
|
|
return;
|
|
}
|
|
|
|
StartCoroutine(OpenWhenAuthorized(deviceIndex));
|
|
}
|
|
|
|
private IEnumerator OpenWhenAuthorized(int deviceIndex)
|
|
{
|
|
yield return EnsureCameraAuthorization();
|
|
if (!HasCameraAuthorization())
|
|
{
|
|
m_logger?.Error("摄像头权限未授权");
|
|
yield break;
|
|
}
|
|
|
|
var deviceName = m_devices[deviceIndex].name;
|
|
m_texture = new WebCamTexture(deviceName, 1280, 720, 30);
|
|
m_texture.Play();
|
|
if (m_preview != null)
|
|
{
|
|
m_preview.texture = m_texture;
|
|
}
|
|
|
|
m_logger?.Info("摄像头已打开: " + deviceName);
|
|
}
|
|
|
|
private IEnumerator GetDeviceNamesWhenAuthorizedRoutine(Action<string[]> callback)
|
|
{
|
|
Close();
|
|
yield return EnsureCameraAuthorization();
|
|
if (!HasCameraAuthorization())
|
|
{
|
|
m_logger?.Error("摄像头权限未授权,无法枚举摄像头");
|
|
callback?.Invoke(new string[0]);
|
|
yield break;
|
|
}
|
|
|
|
string[] names = null;
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
names = GetDeviceNames();
|
|
if (names.Length > 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
yield return new WaitForSeconds(0.3f);
|
|
}
|
|
|
|
callback?.Invoke(names ?? new string[0]);
|
|
}
|
|
|
|
private IEnumerator EnsureCameraAuthorization()
|
|
{
|
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
|
if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
|
|
{
|
|
m_logger?.Info("正在请求摄像头权限");
|
|
Permission.RequestUserPermission(Permission.Camera);
|
|
var timeout = 10f;
|
|
while (!Permission.HasUserAuthorizedPermission(Permission.Camera) && timeout > 0f)
|
|
{
|
|
timeout -= Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
}
|
|
#else
|
|
yield return null;
|
|
#endif
|
|
}
|
|
|
|
private static bool HasCameraAuthorization()
|
|
{
|
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
|
return Permission.HasUserAuthorizedPermission(Permission.Camera);
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// 关闭摄像头。
|
|
/// </summary>
|
|
public void Close()
|
|
{
|
|
if (m_texture != null)
|
|
{
|
|
if (m_texture.isPlaying)
|
|
{
|
|
m_texture.Stop();
|
|
}
|
|
|
|
Destroy(m_texture);
|
|
m_texture = null;
|
|
}
|
|
|
|
if (m_preview != null)
|
|
{
|
|
m_preview.texture = null;
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (m_texture == null || m_aspectFitter == null || m_texture.width <= 16 || m_texture.height <= 16)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_aspectFitter.aspectRatio = (float)m_texture.width / m_texture.height;
|
|
if (m_preview != null)
|
|
{
|
|
m_preview.rectTransform.localEulerAngles = new Vector3(0f, 0f, -m_texture.videoRotationAngle);
|
|
m_preview.uvRect = m_texture.videoVerticallyMirrored ? new Rect(0f, 1f, 1f, -1f) : new Rect(0f, 0f, 1f, 1f);
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
|