Ste*_*ten 6 key-value-observing ios avcapturesession avcapturedevice
我正在屏幕区域进行一些运动检测.在开始检测之前,我想设置焦距和曝光并锁定它们,这样它们就不会触发假动作.因此,我将AVCaptureFocusModeAutoFocus和AVCaptureExposureModeAutoExpose发送到设备并添加KeyvalueObserver.当观察者说它已完成聚焦并改变曝光时,它会锁定它们(并开始运动检测).一切都可以正常工作,但锁定曝光会在几秒钟内崩溃应用程序",尽管在两种情况下都有相同的代码.
static void * const MyAdjustingFocusObservationContext = (void*)&MyAdjustingFocusObservationContext;
static void * const MyAdjustingExposureObservationContext = (void*)&MyAdjustingExposureObservationContext;
-(void)focusAtPoint{
CGPoint point;
if(fromRight) point.x = 450.0/480.0;
else point.x = 30.0/480.0;
point.y = 245.0/320.0;
AVCaptureDevice *device =[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if(device != nil) {
NSError *error;
if([device lockForConfiguration:&error]){
if([device isExposureModeSupported:AVCaptureFocusModeContinuousAutoFocus] && [device isFocusPointOfInterestSupported]) {
[device setFocusPointOfInterest:point];
[device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
[device addObserver:self forKeyPath:@"adjustingFocus" options:NSKeyValueObservingOptionNew context:MyAdjustingFocusObservationContext];
NSLog(@"focus now");
}
if([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure] && [device isExposurePointOfInterestSupported]) {
[device setExposurePointOfInterest:point];
[device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
[device addObserver:self forKeyPath:@"adjustingExposure" options:NSKeyValueObservingOptionNew context:MyAdjustingExposureObservationContext];
NSLog(@"expose now");
}
[device unlockForConfiguration];
}else{
NSLog(@"Error in Focus Mode");
}
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error;
if([keyPath isEqualToString:@"adjustingFocus"]){
if(![object isAdjustingFocus]){
[device removeObserver:self forKeyPath:keyPath context:context];
if([device isFocusModeSupported:AVCaptureFocusModeLocked]) {
[device lockForConfiguration:&error];
device.focusMode = AVCaptureFocusModeLocked;
[device unlockForConfiguration];
NSLog(@" focus locked");
}
}
}
if([keyPath isEqualToString:@"adjustingExposure"]){
if(![object isAdjustingExposure]){
[device removeObserver:self forKeyPath:keyPath context:context];
if([device isExposureModeSupported:AVCaptureExposureModeLocked]) {
[device lockForConfiguration:&error];
device.exposureMode=AVCaptureExposureModeLocked; //causes the crash
[device unlockForConfiguration];
NSLog(@" exposure locked");
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果我注释掉"device.exposureMode = AVCaptureExposureModeLocked"这一行,一切正常(除了焦点没有锁定).如果我将线条移动到焦点观察者,一切正常(除了曝光有时会在正确设置之前锁定).如果我以其他方式锁定曝光,例如通过计时器,它可以工作.
崩溃日志对我没什么帮助(希望有人可以解释)
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x00000000
Crashed Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 Foundation 0x3209d5e2 NSKVOPendingNotificationRelease + 6
1 CoreFoundation 0x317b21c8 __CFArrayReleaseValues + 352
2 CoreFoundation 0x317419f8 _CFArrayReplaceValues + 308
3 CoreFoundation 0x3174391c CFArrayRemoveValueAtIndex + 80
4 Foundation 0x3209d6b6 NSKeyValuePopPendingNotificationPerThread + 38
5 Foundation 0x32090328 NSKeyValueDidChange + 356
6 Foundation 0x3206a6ce -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 90
7 AVFoundation 0x30989fd0 -[AVCaptureFigVideoDevice handleNotification:payload:] + 1668
8 AVFoundation 0x30983f60 -[AVCaptureDeviceInput handleNotification:payload:] + 84
9 AVFoundation 0x3098fc64 avcaptureSessionFigRecorderNotification + 924
10 AVFoundation 0x309b1c64 AVCMNotificationDispatcherCallback + 188
11 CoreFoundation 0x317cee22 __CFNotificationCenterAddObserver_block_invoke_0 + 122
12 CoreFoundation 0x31753034 _CFXNotificationPost + 1424
13 CoreFoundation 0x3175460c CFNotificationCenterPostNotification + 100
14 CoreMedia 0x31d3db8e CMNotificationCenterPostNotification + 114
15 Celestial 0x34465aa4 FigRecorderRemoteCallbacksServer_NotificationIsPending + 628
16 Celestial 0x34465826 _XNotificationIsPending + 66
17 Celestial 0x344657dc figrecordercallbacks_server + 96
18 Celestial 0x34465028 remrec_ClientPortCallBack + 172
19 CoreFoundation 0x317cc5d8 __CFMachPortPerform + 116
20 CoreFoundation 0x317d7170 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32
21 CoreFoundation 0x317d7112 __CFRunLoopDoSource1 + 134
22 CoreFoundation 0x317d5f94 __CFRunLoopRun + 1380
23 CoreFoundation 0x31748eb8 CFRunLoopRunSpecific + 352
24 CoreFoundation 0x31748d44 CFRunLoopRunInMode + 100
25 GraphicsServices 0x3530c2e6 GSEventRunModal + 70
26 UIKit 0x3365e2fc UIApplicationMain + 1116
27 ShootKing 0x000ed304 main (main.m:16)
28 ShootKing 0x000ed28c start + 36
Run Code Online (Sandbox Code Playgroud)
ipm*_*mcc 16
你不会在任何文档中找到这个(即我没有"证据"),但是我可以告诉你痛苦的个人经历,包括很多天(如果不是几周)的调试,这种崩溃是由为该属性的KVO通知处理程序内的属性添加/删除观察者引起的.(根据NSKeyValuePopPendingNotificationPerThread我的经验,在堆栈跟踪中的存在是"冒烟的枪".)我还经验地观察到通知给定属性的观察者的顺序是非确定性的,所以即使在里面添加或删除观察者通知处理程序工作一些当时,它可以在不同的情况下任意失败.(我假设在KVO的某个地方有一个无序的数据结构可以在不同的顺序中枚举,可能基于指针的数值或任意类似的东西.)在过去,我已经解决过这可以通过在设置属性之前/之后立即发布NSNotification来为观察者提供添加/删除自己的机会.这是一个笨重的模式,但它比崩溃更好(并允许我继续使用依赖于KVO的其他东西,比如绑定.)
另外,请注意,在您发布的代码中,您没有使用上下文来识别您的观察结果,并且您在实现中并未调用super observeValueForKeyPath:....这两件事都可能导致细微的,难以诊断的错误.KVO的防弹模式看起来像这样:
static void * const MyAdjustingFocusObservationContext = (void*)&MyAdjustingFocusObservationContext;
static void * const MyAdjustingExposureObservationContext = (void*)&MyAdjustingExposureObservationContext;
- (void)focusAtPoint
{
// ... other stuff ...
[device addObserver:self forKeyPath:@"adjustingFocus" options:NSKeyValueObservingOptionNew context:MyAdjustingFocusObservationContext];
[device addObserver:self forKeyPath:@"adjustingExposure" options:NSKeyValueObservingOptionNew context:MyAdjustingExposureObservationContext];
// ... other stuff ...
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == MyAdjustingFocusObservationContext)
{
// Do stuff
}
else if (context == MyAdjustingExposureObservationContext)
{
// Do other stuff
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:我想跟进,看看我是否可以在这种特定情况下提供更多帮助.我从代码和你的评论中收集到你正在寻找这些观察结果,实际上是一次性的.我看到两种方法:
对于这个对象来说,总是观察捕获设备(即addObserver:...当你初始化时,removeObserver:...当你释放时),然后使用一些名为waitingForFocus和的ivars来"控制"行为,这将是更直接和更安全的方法waitingForExposure.-focusAtPoint您目前在哪里addObserver:...将ivars设置为YES.然后observeValueForKeyPath:...只在那些ivars 然后采取行动YES而不是removeObserver:...仅仅设置ivars NO.这应该具有所需的效果,而不需要每次添加和删除观察.
我想到的另一种方法是removeObserver:...使用GCD"稍后" 调用.所以你会改变removeObserver:...这样的:
dispatch_async(dispatch_get_main_queue(), ^{ [device removeObserver:self forKeyPath:keyPath context:context]; });
Run Code Online (Sandbox Code Playgroud)
这将导致在通知过程完成后在运行循环中的其他位置进行调用.这稍微不那么防弹,因为没有什么可以保证在发生延迟删除调用之前第二次不会触发通知.在这方面,第一种方法在实现期望的一次性行为方面更严格地"正确".
编辑2:我不能放手.:)我弄清楚你为什么会崩溃.我观察到exposureMode在KVO处理程序中的设置adjustingExposure最终会导致另一个通知adjustingExposure,因此堆栈会爆炸,直到您的进程被终止.我能够通过将observeValueForKeyPath:...处理更改的部分包装adjustingExposure在一个dispatch_async(dispatch_get_main_queue(), ^{...});(包括最终removeObserver:...调用)中来使其工作.在此之后它对我有用,并且肯定锁定曝光和焦点.也就是说,就像我上面提到的那样,用ivars可以更好地处理这个以防止递归而不是任意延迟dispatch_async().
希望有所帮助.
| 归档时间: |
|
| 查看次数: |
3116 次 |
| 最近记录: |