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 <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" {
|
||||
|
||||
/// 启用连续自动对焦和自动曝光
|
||||
void _EnableContinuousAutoFocus()
|
||||
/// 启用连续自动对焦和自动曝光(传入 C# 选中的摄像头名称)
|
||||
void _EnableContinuousAutoFocus(const char *cameraName)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||
if (!device) return;
|
||||
AVCaptureDevice *device = FindCameraByName(cameraName);
|
||||
if (!device) {
|
||||
NSLog(@"[CameraFocusHelper] 未找到摄像头设备");
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"[CameraFocusHelper] 配置设备: %@ (name=%@, model=%@)",
|
||||
device.localizedName, device.localizedName, device.modelID);
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
// 锁定设备开始配置
|
||||
if ([device lockForConfiguration:&error]) {
|
||||
|
||||
// 连续自动对焦模式(持续调整,适合扫描二维码)
|
||||
// 连续自动对焦
|
||||
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
|
||||
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
|
||||
NSLog(@"[CameraFocusHelper] 已设为连续自动对焦");
|
||||
} else {
|
||||
NSLog(@"[CameraFocusHelper] 设备不支持连续自动对焦");
|
||||
}
|
||||
|
||||
// 连续自动曝光
|
||||
@ -25,36 +66,34 @@ extern "C" {
|
||||
device.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
|
||||
}
|
||||
|
||||
// 对焦范围限制为近距(适合二维码)
|
||||
if ([device respondsToSelector:@selector(setAutoFocusRangeRestriction:)]) {
|
||||
if ([device isAutoFocusRangeRestrictionSupported]) {
|
||||
device.autoFocusRangeRestriction = AVCaptureAutoFocusRangeRestrictionNear;
|
||||
}
|
||||
// 对焦范围限制为近距
|
||||
if ([device respondsToSelector:@selector(isAutoFocusRangeRestrictionSupported)] &&
|
||||
[device isAutoFocusRangeRestrictionSupported]) {
|
||||
device.autoFocusRangeRestriction = AVCaptureAutoFocusRangeRestrictionNear;
|
||||
NSLog(@"[CameraFocusHelper] 已设对焦范围为近距");
|
||||
}
|
||||
|
||||
[device unlockForConfiguration];
|
||||
|
||||
NSLog(@"[CameraFocusHelper] 已启用连续自动对焦");
|
||||
NSLog(@"[CameraFocusHelper] 摄像头配置完成");
|
||||
} else {
|
||||
NSLog(@"[CameraFocusHelper] 锁定设备失败: %@", error.localizedDescription);
|
||||
NSLog(@"[CameraFocusHelper] lockForConfiguration 失败: %@", error.localizedDescription);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 触发一次自动对焦(用于手动点按时调用)
|
||||
void _TriggerAutoFocus()
|
||||
/// 确保连续自动对焦仍在生效(传入 C# 选中的摄像头名称)
|
||||
void _EnsureContinuousAutoFocus(const char *cameraName)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||
AVCaptureDevice *device = FindCameraByName(cameraName);
|
||||
if (!device) return;
|
||||
|
||||
NSError *error = nil;
|
||||
if ([device lockForConfiguration:&error]) {
|
||||
if ([device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
|
||||
device.focusMode = AVCaptureFocusModeAutoFocus;
|
||||
}
|
||||
if ([device isExposureModeSupported:AVCaptureExposureModeAutoExpose]) {
|
||||
device.exposureMode = AVCaptureExposureModeAutoExpose;
|
||||
if (device.focusMode != AVCaptureFocusModeContinuousAutoFocus &&
|
||||
[device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
|
||||
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
|
||||
NSLog(@"[CameraFocusHelper] 重新设回连续自动对焦");
|
||||
}
|
||||
[device unlockForConfiguration];
|
||||
}
|
||||
|
||||
@ -45,10 +45,10 @@ namespace Kill.UI.Pages
|
||||
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _EnableContinuousAutoFocus();
|
||||
private static extern void _EnableContinuousAutoFocus(string cameraName);
|
||||
|
||||
[DllImport("__Internal")]
|
||||
private static extern void _TriggerAutoFocus();
|
||||
private static extern void _EnsureContinuousAutoFocus(string cameraName);
|
||||
#endif
|
||||
// 摄像头相关
|
||||
private WebCamTexture webCamTexture;
|
||||
@ -59,6 +59,9 @@ namespace Kill.UI.Pages
|
||||
// 扫描结果回调
|
||||
public event Action<string> OnQRCodeScanned;
|
||||
|
||||
// iOS 摄像头名称(供原生对焦插件使用)
|
||||
private string selectedCameraName;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
StartScan();
|
||||
@ -148,17 +151,32 @@ namespace Kill.UI.Pages
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 优先使用后置摄像头,并选择支持自动对焦的摄像头
|
||||
// 优先使用后置主摄(广角),iOS 多摄设备排除超广角/长焦
|
||||
string deviceName = devices[0].name;
|
||||
currentDevice = devices[0];
|
||||
for (int i = 0; i < devices.Length; i++)
|
||||
{
|
||||
if (!devices[i].isFrontFacing)
|
||||
{
|
||||
deviceName = devices[i].name;
|
||||
currentDevice = devices[i];
|
||||
break;
|
||||
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;
|
||||
currentDevice = devices[i];
|
||||
break;
|
||||
}
|
||||
// 兜底:第一个后置
|
||||
if (deviceName == devices[0].name)
|
||||
{
|
||||
deviceName = devices[i].name;
|
||||
currentDevice = devices[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug.Log($"[ScanQRcode] 选中摄像头: {deviceName}");
|
||||
selectedCameraName = deviceName;
|
||||
RectTransform cameraPreviewRect=cameraPreview.GetComponent<RectTransform>();
|
||||
// 使用较低的扫描分辨率以提高性能
|
||||
int targetWidth = 2160;
|
||||
@ -172,11 +190,13 @@ namespace Kill.UI.Pages
|
||||
// 等待摄像头启动
|
||||
yield return new WaitUntil(() => webCamTexture.width > 100);
|
||||
|
||||
// iOS: 调用原生插件强制开启连续自动对焦
|
||||
// iOS: 延迟后调用原生插件强制开启连续自动对焦
|
||||
// (等待 Unity 内部 AVCaptureSession 完全就绪)
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
if (enableAutoFocus)
|
||||
{
|
||||
_EnableContinuousAutoFocus();
|
||||
yield return new WaitForSeconds(1.5f);
|
||||
_EnableContinuousAutoFocus(deviceName);
|
||||
}
|
||||
#endif
|
||||
// 调整预览画面比例
|
||||
@ -287,7 +307,7 @@ namespace Kill.UI.Pages
|
||||
|
||||
/// <summary>
|
||||
/// 自动对焦维护协程
|
||||
/// iOS: 走原生插件(连续自动对焦已启用,只需定期触发即可)
|
||||
/// iOS: 走原生插件周期确保连续对焦模式不被重置
|
||||
/// 其他平台: 使用 WebCamTexture.autoFocusPoint 定期设置对焦点
|
||||
/// </summary>
|
||||
private IEnumerator AutoFocusCoroutine()
|
||||
@ -295,8 +315,11 @@ namespace Kill.UI.Pages
|
||||
while (isScanning)
|
||||
{
|
||||
#if UNITY_IOS && !UNITY_EDITOR
|
||||
// iOS 通过原生插件触发一次自动对焦
|
||||
_TriggerAutoFocus();
|
||||
// iOS 通过原生插件确保连续自动对焦仍在生效
|
||||
if (!string.IsNullOrEmpty(selectedCameraName))
|
||||
{
|
||||
_EnsureContinuousAutoFocus(selectedCameraName);
|
||||
}
|
||||
#else
|
||||
// Android/其他: 定期重置对焦点到画面中心
|
||||
if (webCamTexture != null && webCamTexture.isPlaying)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user