【问题标题】:Why captureOutput is never called?为什么从不调用 captureOutput?
【发布时间】:2022-01-14 23:51:16
【问题描述】:

我正在尝试为 macOS 制作一个简单的控制台程序,它应该使用内置相机拍摄一张图像并将其保存到照片库中。

这是该程序的完整代码:

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <Photos/Photos.h>

volatile int a = 0;

@interface PhotoCaptureProcessor: NSObject <AVCapturePhotoCaptureDelegate>

@end

@implementation PhotoCaptureProcessor

- (void)    captureOutput:(AVCapturePhotoOutput *)output
 didFinishProcessingPhoto:(AVCapturePhoto *)photo
                    error:(NSError *)error {
    
    NSLog(@"Called\n");
    
    if(error) {
        return;
    }
    
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        
        [[PHAssetCreationRequest creationRequestForAsset] addResourceWithType:PHAssetResourceTypePhoto
                                                                         data:[photo fileDataRepresentation]
                                                                      options:nil];
        
    } completionHandler:^(BOOL success, NSError * _Nullable error) {
        if(error) {
            NSLog(@"Error saving photo\n");
        }
        a = 1;
    }];
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];
        AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
        NSError *error = nil;
        AVCaptureDeviceInput *inputVideoDevice = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
        
        [captureSession beginConfiguration];
        if(inputVideoDevice) {
            if([captureSession canAddInput:inputVideoDevice]) {
                [captureSession addInput:inputVideoDevice];
            }
        }
        else {
            return 1;
        }
        
        
        AVCapturePhotoOutput *photoOutput = [[AVCapturePhotoOutput alloc] init];
        if(photoOutput) {
            if([captureSession canAddOutput:photoOutput]) {
                [captureSession addOutput:photoOutput];
            }
        }
        captureSession.sessionPreset = AVCaptureSessionPresetPhoto;
        [captureSession commitConfiguration];
        
        [captureSession startRunning];
        
        while(![captureSession isRunning]){
            NSLog(@"Still not running\n");
        }
        
        AVCapturePhotoSettings *photoSettings = [[AVCapturePhotoSettings alloc] init];
        PhotoCaptureProcessor *photoProcessor = [[PhotoCaptureProcessor alloc] init];
        [photoOutput capturePhotoWithSettings:photoSettings delegate:photoProcessor];
        
        while(!a){
            
        }
    }
    
    return 0;
}

在调用方法 startRunning 后,我的 Mac 相机上的小灯变为绿色(我相信这表明相机正在使用中)。不幸的是,我的委托 PhotoCaptureProcessor 中的 captureOutput 方法从未被调用过。如果我右键单击captureOutput,然后单击转到定义,它会将我带到AVCapturePhotoCaptureDelegate 中的captureOutput(所以我相信方法签名很好)。

谁能告诉我我做错了什么?

【问题讨论】:

    标签: objective-c macos avfoundation


    【解决方案1】:

    API 正在运行,但由于没有授予或请求访问权限,因此它不会执行。
    Info.plist 中为 NSCameraUsageDescription 和/或 NSMicrophoneUsageDescription 创建一个条目。
    编写一条短消息,当弹出用户同意对话框时将显示该消息,说明您想要访问的原因.

    typedef void(^ConsentCompletionBlock)(void);
    
    -(void)requestUserConsentFor:(AVMediaType)mediatype completion:(ConsentCompletionBlock)block {
        if (@available(macOS 10.14, *)) { // || @available(iOS 7.0, *)
            // Request permission to access the camera and/or microphone.
            switch ([AVCaptureDevice authorizationStatusForMediaType:mediatype]) {
                case AVAuthorizationStatusAuthorized: {
                    NSLog(@"access is granted by user.");
                    break;
                }
                case AVAuthorizationStatusNotDetermined: {
                    NSLog(@"oops, users consent not known yet, asking user!");
                    [AVCaptureDevice requestAccessForMediaType:mediatype completionHandler:^(BOOL granted) {
                        if (granted) {
                            block();
                            return;
                        }
                        NSLog(@"oops, no access granted");
                    }];
                    break;
                }
                case AVAuthorizationStatusDenied: {
                    NSLog(@"rrrgh, user did not allow access");
                    return;
                }
                case AVAuthorizationStatusRestricted: {
                    NSLog(@"ok, access is restricted");
                    return;
                }
            }
        } else block(); //fallback for older systems
    }
    

    使用喜欢..

    [self requestUserConsentFor:AVMediaTypeVideo completion:^{
        //dont forget UI work needs main thread.
        dispatch_async(dispatch_get_main_queue(), ^{ 
            [self yourSessionSetupStuffMethod];
        });
    }];
    

    简短提醒,这不会让您的应用继续运行。

    Signing&Capabilities 将所需的条目放在 Info.plist 中,然后再将其复制到您的应用程序 Contents 文件夹中。如果没有 Info.plist,您必须手动告诉操作系统同意规则或寻找其他方式。这是因为我们不希望任何类型的代码在未经同意的情况下访问摄像头/麦克风。上面的代码实际上将授予的同意放在正确的位置.. 如果您能够运行它,但它需要提供的字符串才能知道告诉用户它的用途。

    正如您所指出的,它是一个"Single File Tool",没有关于将 Info.plist 数据直接放入二进制文件的 Info.plist 信息。因为它只是一个信息,不应该在运行时编辑它应该可以工作。

    你并不孤单trouble

    PS:上面的块声明允许包装您的实际代码,因此当权限未被授予之前或上次运行或在块可以执行之前,它不会执行。这应该可以保护您免于崩溃。请注意,在不需要权限的旧系统上,只会调用该块作为后备。

    【讨论】:

    • 我在 Xcode 中作为命令行工具制作了这个项目,所以我没有 Info.plist。同样在 Signing&Capabilities -> Hardened Runtime 中,我检查了相机、音频输入和照片库。这还不够吗?
    • 查看我对答案的修改
    • 好的,非常感谢。但由于这是我第一个 macOS 项目,我想我将制作另一个程序,它将是带有 Info.plist 的 macOS 应用程序。在这里,我只想测试 AVFoundation 的功能,我认为 macOS 的命令行工具将是最快的方法。无论如何,再次感谢。
    猜你喜欢
    • 2021-12-14
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    • 2016-10-19
    • 1970-01-01
    • 2014-11-03
    • 1970-01-01
    • 2020-08-21
    相关资源
    最近更新 更多