AVCaptureDeviceOutput没有调用委托方法captureOutput

Sui*_*oth 13 iphone avfoundation ios5

我正在构建一个iOS应用程序(我的第一个),它可以动态处理视频静止帧.为了深入研究,我从Apple 的AV*文档中提供了一个示例.

该过程涉及设置输入(摄像机)和输出.输出使用委托,在这种情况下是控制器本身(它符合并实现所需的方法).

我遇到的问题是委托方法永远不会被调用.下面的代码是控制器的实现,它有几个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 );

}
Run Code Online (Sandbox Code Playgroud)

注意:我正在使用iOS 5.0作为目标.

编辑:

我发现了一个问题,虽然要求解决另一个问题,但正在完成我的代码应该做的事情.我已将该问题的代码逐字复制到空白的xcode应用程序中,将NSLogs添加到captureOutput函数中并且不会被调用.这是配置问题吗?有什么我想念的吗?

Tom*_*mmy 29

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 );
         });
}
Run Code Online (Sandbox Code Playgroud)

大多数人主张现在在实例变量名称的开头使用下划线但是为了简单起见我省略了它.在验证诊断正确后,您可以使用Xcode的内置重构工具来解决这个问题.

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

  • 我尝试了这个解决方案,它对我不起作用.你能给我任何其他见解吗? (2认同)

Rin*_*nov 19

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

仅供参考,我在AV Foundation框架文档中找不到任何关于此限制的明确描述,但Apple支持在几年前证实了这一点,如本SO答案所述.

解决此问题的一种方法是AVCaptureMovieFileOutputdidOutputSampleBuffer委托方法中完全删除并手动将记录的帧写入文件,以及自定义缓冲区数据处理.您可能会发现 两个 SO答案很有用.