【问题标题】:AVCaptureDeviceOutput not calling delegate method captureOutputAVCaptureDeviceOutput 没有调用委托方法 captureOutput
【发布时间】:2012-11-10 14:01:20
【问题描述】:

我正在构建一个动态处理视频静止帧的 iOS 应用程序(我的第一个)。为了深入了解这一点,我关注了来自 Apple 的 example from the AV* documentation

该过程涉及设置输入(相机)和输出。输出与委托一起工作,在这种情况下,委托就是控制器本身(它符合并实现所需的方法)。

我遇到的问题是委托方法永远不会被调用。下面的代码是控制器的实现,它有几个 NSLog。我可以看到“已启动”消息,但“调用的委托方法”从未显示。

此代码都在实现“AVCaptureVideoDataOutputSampleBufferDelegate”协议的控制器中。

- (void)viewDidLoad {

    [super viewDidLoad];

    // Initialize AV session    
        AVCaptureSession *session = [AVCaptureSession new];

        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
            [session setSessionPreset:AVCaptureSessionPreset640x480];
        else
            [session setSessionPreset:AVCaptureSessionPresetPhoto];

    // Initialize back camera input
        AVCaptureDevice *camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

        NSError *error = nil;

        AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:camera error:&error];

        if( [session canAddInput:input] ){
            [session addInput:input];
        }


    // Initialize image output
        AVCaptureVideoDataOutput *output = [AVCaptureVideoDataOutput new];

        NSDictionary *rgbOutputSettings = [NSDictionary dictionaryWithObject:
                                           [NSNumber numberWithInt:kCMPixelFormat_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey];
        [output setVideoSettings:rgbOutputSettings];
        [output setAlwaysDiscardsLateVideoFrames:YES]; // discard if the data output queue is blocked (as we process the still image)


        //[output addObserver:self forKeyPath:@"capturingStillImage" options:NSKeyValueObservingOptionNew context:@"AVCaptureStillImageIsCapturingStillImageContext"];

        videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL);
        [output setSampleBufferDelegate:self queue:videoDataOutputQueue];


        if( [session canAddOutput:output] ){
            [session addOutput:output];
        }

        [[output connectionWithMediaType:AVMediaTypeVideo] setEnabled:YES];


    [session startRunning];

    NSLog(@"started");


}


- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {

        NSLog(@"delegate method called");

        CGImageRef cgImage = [self imageFromSampleBuffer:sampleBuffer];

        self.theImage.image = [UIImage imageWithCGImage: cgImage ];

        CGImageRelease( cgImage );

}

注意:我正在以 iOS 5.0 作为目标进行构建。

编辑:

我发现了一个question,尽管它要求解决一个不同的问题,但它正在做我的代码应该做的事情。我已经将那个问题的代码逐字复制到一个空白的 xcode 应用程序中,将 NSLogs 添加到 captureOutput 函数中并且它没有被调用。这是配置问题吗?我有什么遗漏吗?

【问题讨论】:

  • 如果启动会话运行时出错(可能是因为您没有收到任何帧),那么它将发布AVCaptureSessionRuntimeErrorNotification 通知。使用[[NSNotificationCenter defaultCenter] addObserver:selector:name:object:]; 监听它,当你的选择器被调用时,从用户字典中获取`AVCaptureSessionErrorKey`来查看错误。
  • 感谢您的意见@Inafziger。我订阅了 AVCaptureSessionRuntimeErrorNotification 但它似乎没有触发:|
  • 视图控制器是通过什么方式创建的? 'started' 有输出吗?
  • @Tommy,视图控制器只是附加到情节提要场景。所以它是通过“标准”方式创建的。 “开始”确实得到输出。我还将代码移到了一个由按钮触发的函数中,以查看它是否与它在 viewDidLoad 中有关,但没有任何变化。
  • 我刚刚将我的代码逐字复制到我从 Apple 文档下载的名为“SquareCam”的示例项目中。我经历了整个实现,并在我的实现中将其替换掉。应用程序运行,委托 NSLog 被调用!但不是我的项目。我希望确保我包含所有相同的框架等,但我找不到我丢失的任何代码......这变得很奇怪:(

标签: iphone avfoundation ios5


【解决方案1】:

您的session 是一个局部变量。其范围仅限于viewDidLoad。由于这是一个新项目,我认为可以肯定地说您正在使用 ARC。在这种情况下,该对象不会泄漏,因此会像在链接问题中那样继续存在,而是编译器将确保在 viewDidLoad 退出之前释放该对象。

因此您的会话没有运行,因为它不再存在。

(另外:self.theImage.image = ... 不安全,因为它执行主队列的 UIKit 操作;您可能希望将 dispatch_async 转移到 dispatch_get_main_queue()

所以,样本更正:

@implementation YourViewController
{
     AVCaptureSession *session;
}

- (void)viewDidLoad {

    [super viewDidLoad];

    // Initialize AV session    
        session = [AVCaptureSession new];

        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
            [session setSessionPreset:AVCaptureSessionPreset640x480];
        else
         /* ... etc ... */
}


- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {

        NSLog(@"delegate method called");

        CGImageRef cgImage = [self imageFromSampleBuffer:sampleBuffer];

        dispatch_sync(dispatch_get_main_queue(),
        ^{
            self.theImage.image = [UIImage imageWithCGImage: cgImage ];
            CGImageRelease( cgImage );
         });
}

现在大多数人都提倡在实例变量名称的开头使用下划线,但为了简单起见,我省略了它。在确认诊断正确后,您可以使用 Xcode 的内置重构工具来修复它。

我将CGImageRelease 移动到发送到主队列的块内,以确保其生命周期超出捕获范围,进入UIImage。我无法立即找到任何文档来确认 CoreFoundation 对象在被捕获时会自动延长其生命周期。

【讨论】:

  • 非常感谢先生,完全做到了:) - 我没有想到要测试使会话成为实例变量。当然,现在我回去看看 SquareCam,它当然也在那里。我从这个问题中学到了很多其他的东西,所以这并不完全是浪费。再次感谢!
  • 我尝试了该解决方案,但它对我不起作用。你能给我其他的见解吗?
  • @tuler 我找到了另一个可能无法调用didOutputSampleBuffer 委托方法的原因,请在下面查看我的答案。 stackoverflow.com/a/27704982/2979418
【解决方案2】:

我发现了另一个不能调用didOutputSampleBuffer 委托方法的原因——保存到文件获取样本缓冲区 输出连接是互斥的。换句话说,如果您的会话已经有AVCaptureMovieFileOutput,然后您添加了AVCaptureVideoDataOutput,那么只会调用AVCaptureFileOutputRecordingDelegate 委托方法。

仅供参考,我在AV Foundation 框架文档中找不到任何地方对此限制的明确描述,但Apple 支持在几年前证实了这一点,正如SO answer 中所述。

解决问题的一种方法是完全删除AVCaptureMovieFileOutput,并在didOutputSampleBuffer 委托方法中手动将记录的帧写入文件,同时处理您的自定义缓冲区数据。您可能会发现 these two SO 答案很有用。

【讨论】:

  • 这是否也适用于同时使用 AVCaptureVideoDataOutput 和 AVCaptureAudioDataOutput?我已经成功地使用视频的委托方法捕获帧并写入文件,但是音频从不触发委托方法,所以我的视频没有声音。当我使用 AVCaptureMovieFileOutput 时声音正常,所以我知道不是我的输入有问题。
【解决方案3】:

在我的情况下,问题就在那里,因为我打电话给

if ([_session canAddOutput:_videoDataOutput])
        [_session addOutput:_videoDataOutput];

在我打电话之前

[_session startRunning];

我刚刚在startRunning 之后开始呼叫addOutput:

希望对某人有所帮助。

【讨论】:

  • @nbanic 请停止更新只是将单词“cuz”更改为“因为”,而不是对帖子进行其他改进。到目前为止,您已经编辑了几个答案,这些答案可能会带来其他重大改进。
  • 为什么会在 session startRunning 之后调用 addOutput?
  • 实际上应该在运行会话之前添加输出
  • >"为什么会在会话开始运行之后调用 addOutput?" |没有理由。代码是错误的。它以某种方式有所帮助,也许是因为另一个错误!
【解决方案4】:

我的 captureOutput 函数也没有被调用。并且接受的答案并没有完全指向我的问题,因为我的会话已经是一个实例变量。

但是,我的视频帧的 DispatchQueue 是本地的。并且 dispatchQueue 也必须是一个实例变量。我不太明白为什么这应该是必要的。也许底层的 AVCapture 代码只保留了一个指向它的弱指针?

文档对此非常混乱。

【讨论】:

    猜你喜欢
    • 2021-12-14
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-03
    • 2020-11-14
    • 2020-11-28
    • 2017-05-04
    相关资源
    最近更新 更多