fix(ios): 修复iOS摄像头对焦异常,优化多摄选择逻辑
1. 重构iOS原生对焦插件,支持按摄像头名称精准匹配设备 2. 优化摄像头选择逻辑,优先选用后置主摄并排除长焦/超广角镜头 3. 修复连续自动对焦失效问题,增加延迟初始化和定期校验 4. 添加调试日志便于排查摄像头相关问题
This commit is contained in:
parent
2acd61f832
commit
5c21080203
@ -1,23 +1,64 @@
|
|||||||
#import <AVFoundation/AVFoundation.h>
|
#import <AVFoundation/AVFoundation.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
/// 通过名称查找摄像头设备
|
||||||
|
static AVCaptureDevice *FindCameraByName(const char *cameraName)
|
||||||
|
{
|
||||||
|
if (cameraName == NULL || strlen(cameraName) == 0) {
|
||||||
|
return [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *name = [NSString stringWithUTF8String:cameraName];
|
||||||
|
|
||||||
|
// iOS 10+ DiscoverySession
|
||||||
|
if (@available(iOS 10.0, *)) {
|
||||||
|
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession
|
||||||
|
discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera,
|
||||||
|
AVCaptureDeviceTypeBuiltInTelephotoCamera,
|
||||||
|
AVCaptureDeviceTypeBuiltInUltraWideCamera]
|
||||||
|
mediaType:AVMediaTypeVideo
|
||||||
|
position:AVCaptureDevicePositionUnspecified];
|
||||||
|
for (AVCaptureDevice *device in session.devices) {
|
||||||
|
if ([device.localizedName containsString:name] ||
|
||||||
|
[device.modelID containsString:name]) {
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 没找到精确匹配,返回第一个活跃的
|
||||||
|
for (AVCaptureDevice *device in session.devices) {
|
||||||
|
if (device.isConnected && !device.isSuspended) {
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
/// 启用连续自动对焦和自动曝光
|
/// 启用连续自动对焦和自动曝光(传入 C# 选中的摄像头名称)
|
||||||
void _EnableContinuousAutoFocus()
|
void _EnableContinuousAutoFocus(const char *cameraName)
|
||||||
{
|
{
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
AVCaptureDevice *device = FindCameraByName(cameraName);
|
||||||
if (!device) return;
|
if (!device) {
|
||||||
|
NSLog(@"[CameraFocusHelper] 未找到摄像头设备");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSLog(@"[CameraFocusHelper] 配置设备: %@ (name=%@, model=%@)",
|
||||||
|
device.localizedName, device.localizedName, device.modelID);
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
|
|
||||||
// 锁定设备开始配置
|
|
||||||
if ([device lockForConfiguration:&error]) {
|
if ([device lockForConfiguration:&error]) {
|
||||||
|
|
||||||
// 连续自动对焦模式(持续调整,适合扫描二维码)
|
// 连续自动对焦
|
||||||
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
|
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
|
||||||
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
|
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
|
||||||
|
NSLog(@"[CameraFocusHelper] 已设为连续自动对焦");
|
||||||
|
} else {
|
||||||
|
NSLog(@"[CameraFocusHelper] 设备不支持连续自动对焦");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 连续自动曝光
|
// 连续自动曝光
|
||||||
@ -25,36 +66,34 @@ extern "C" {
|
|||||||
device.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
|
device.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对焦范围限制为近距(适合二维码)
|
// 对焦范围限制为近距
|
||||||
if ([device respondsToSelector:@selector(setAutoFocusRangeRestriction:)]) {
|
if ([device respondsToSelector:@selector(isAutoFocusRangeRestrictionSupported)] &&
|
||||||
if ([device isAutoFocusRangeRestrictionSupported]) {
|
[device isAutoFocusRangeRestrictionSupported]) {
|
||||||
device.autoFocusRangeRestriction = AVCaptureAutoFocusRangeRestrictionNear;
|
device.autoFocusRangeRestriction = AVCaptureAutoFocusRangeRestrictionNear;
|
||||||
}
|
NSLog(@"[CameraFocusHelper] 已设对焦范围为近距");
|
||||||
}
|
}
|
||||||
|
|
||||||
[device unlockForConfiguration];
|
[device unlockForConfiguration];
|
||||||
|
NSLog(@"[CameraFocusHelper] 摄像头配置完成");
|
||||||
NSLog(@"[CameraFocusHelper] 已启用连续自动对焦");
|
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"[CameraFocusHelper] 锁定设备失败: %@", error.localizedDescription);
|
NSLog(@"[CameraFocusHelper] lockForConfiguration 失败: %@", error.localizedDescription);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 触发一次自动对焦(用于手动点按时调用)
|
/// 确保连续自动对焦仍在生效(传入 C# 选中的摄像头名称)
|
||||||
void _TriggerAutoFocus()
|
void _EnsureContinuousAutoFocus(const char *cameraName)
|
||||||
{
|
{
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
AVCaptureDevice *device = FindCameraByName(cameraName);
|
||||||
if (!device) return;
|
if (!device) return;
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
if ([device lockForConfiguration:&error]) {
|
if ([device lockForConfiguration:&error]) {
|
||||||
if ([device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
|
if (device.focusMode != AVCaptureFocusModeContinuousAutoFocus &&
|
||||||
device.focusMode = AVCaptureFocusModeAutoFocus;
|
[device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
|
||||||
}
|
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
|
||||||
if ([device isExposureModeSupported:AVCaptureExposureModeAutoExpose]) {
|
NSLog(@"[CameraFocusHelper] 重新设回连续自动对焦");
|
||||||
device.exposureMode = AVCaptureExposureModeAutoExpose;
|
|
||||||
}
|
}
|
||||||
[device unlockForConfiguration];
|
[device unlockForConfiguration];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,10 +45,10 @@ namespace Kill.UI.Pages
|
|||||||
|
|
||||||
#if UNITY_IOS && !UNITY_EDITOR
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
[DllImport("__Internal")]
|
[DllImport("__Internal")]
|
||||||
private static extern void _EnableContinuousAutoFocus();
|
private static extern void _EnableContinuousAutoFocus(string cameraName);
|
||||||
|
|
||||||
[DllImport("__Internal")]
|
[DllImport("__Internal")]
|
||||||
private static extern void _TriggerAutoFocus();
|
private static extern void _EnsureContinuousAutoFocus(string cameraName);
|
||||||
#endif
|
#endif
|
||||||
// 摄像头相关
|
// 摄像头相关
|
||||||
private WebCamTexture webCamTexture;
|
private WebCamTexture webCamTexture;
|
||||||
@ -59,6 +59,9 @@ namespace Kill.UI.Pages
|
|||||||
// 扫描结果回调
|
// 扫描结果回调
|
||||||
public event Action<string> OnQRCodeScanned;
|
public event Action<string> OnQRCodeScanned;
|
||||||
|
|
||||||
|
// iOS 摄像头名称(供原生对焦插件使用)
|
||||||
|
private string selectedCameraName;
|
||||||
|
|
||||||
void OnEnable()
|
void OnEnable()
|
||||||
{
|
{
|
||||||
StartScan();
|
StartScan();
|
||||||
@ -148,17 +151,32 @@ namespace Kill.UI.Pages
|
|||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先使用后置摄像头,并选择支持自动对焦的摄像头
|
// 优先使用后置主摄(广角),iOS 多摄设备排除超广角/长焦
|
||||||
string deviceName = devices[0].name;
|
string deviceName = devices[0].name;
|
||||||
|
currentDevice = devices[0];
|
||||||
for (int i = 0; i < devices.Length; i++)
|
for (int i = 0; i < devices.Length; i++)
|
||||||
{
|
{
|
||||||
if (!devices[i].isFrontFacing)
|
if (!devices[i].isFrontFacing)
|
||||||
|
{
|
||||||
|
string lowerName = devices[i].name.ToLower();
|
||||||
|
// iOS 多摄命名: "Back Camera", "Telephoto Camera", "Ultra Wide Camera"
|
||||||
|
// 优先选不带 telephoto/ultra wide 的后置摄像头(即主摄)
|
||||||
|
if (!lowerName.Contains("telephoto") && !lowerName.Contains("ultra wide"))
|
||||||
{
|
{
|
||||||
deviceName = devices[i].name;
|
deviceName = devices[i].name;
|
||||||
currentDevice = devices[i];
|
currentDevice = devices[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 兜底:第一个后置
|
||||||
|
if (deviceName == devices[0].name)
|
||||||
|
{
|
||||||
|
deviceName = devices[i].name;
|
||||||
|
currentDevice = devices[i];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.Log($"[ScanQRcode] 选中摄像头: {deviceName}");
|
||||||
|
selectedCameraName = deviceName;
|
||||||
RectTransform cameraPreviewRect=cameraPreview.GetComponent<RectTransform>();
|
RectTransform cameraPreviewRect=cameraPreview.GetComponent<RectTransform>();
|
||||||
// 使用较低的扫描分辨率以提高性能
|
// 使用较低的扫描分辨率以提高性能
|
||||||
int targetWidth = 2160;
|
int targetWidth = 2160;
|
||||||
@ -172,11 +190,13 @@ namespace Kill.UI.Pages
|
|||||||
// 等待摄像头启动
|
// 等待摄像头启动
|
||||||
yield return new WaitUntil(() => webCamTexture.width > 100);
|
yield return new WaitUntil(() => webCamTexture.width > 100);
|
||||||
|
|
||||||
// iOS: 调用原生插件强制开启连续自动对焦
|
// iOS: 延迟后调用原生插件强制开启连续自动对焦
|
||||||
|
// (等待 Unity 内部 AVCaptureSession 完全就绪)
|
||||||
#if UNITY_IOS && !UNITY_EDITOR
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
if (enableAutoFocus)
|
if (enableAutoFocus)
|
||||||
{
|
{
|
||||||
_EnableContinuousAutoFocus();
|
yield return new WaitForSeconds(1.5f);
|
||||||
|
_EnableContinuousAutoFocus(deviceName);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// 调整预览画面比例
|
// 调整预览画面比例
|
||||||
@ -287,7 +307,7 @@ namespace Kill.UI.Pages
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 自动对焦维护协程
|
/// 自动对焦维护协程
|
||||||
/// iOS: 走原生插件(连续自动对焦已启用,只需定期触发即可)
|
/// iOS: 走原生插件周期确保连续对焦模式不被重置
|
||||||
/// 其他平台: 使用 WebCamTexture.autoFocusPoint 定期设置对焦点
|
/// 其他平台: 使用 WebCamTexture.autoFocusPoint 定期设置对焦点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IEnumerator AutoFocusCoroutine()
|
private IEnumerator AutoFocusCoroutine()
|
||||||
@ -295,8 +315,11 @@ namespace Kill.UI.Pages
|
|||||||
while (isScanning)
|
while (isScanning)
|
||||||
{
|
{
|
||||||
#if UNITY_IOS && !UNITY_EDITOR
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
// iOS 通过原生插件触发一次自动对焦
|
// iOS 通过原生插件确保连续自动对焦仍在生效
|
||||||
_TriggerAutoFocus();
|
if (!string.IsNullOrEmpty(selectedCameraName))
|
||||||
|
{
|
||||||
|
_EnsureContinuousAutoFocus(selectedCameraName);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
// Android/其他: 定期重置对焦点到画面中心
|
// Android/其他: 定期重置对焦点到画面中心
|
||||||
if (webCamTexture != null && webCamTexture.isPlaying)
|
if (webCamTexture != null && webCamTexture.isPlaying)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user