16

我正在构建一个 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 );

}

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

编辑:

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

4

4 回答 4

33

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 对象在块中捕获时会自动延长其生命周期。

于 2012-11-26T05:36:11.197 回答
19

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

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

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

于 2014-12-30T11:52:41.133 回答
2

就我而言,问题就在那里,因为我打电话

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

在我打电话之前

[_session startRunning];

我刚开始打电话addOutput:startRunning

希望它对某人有所帮助。

于 2014-03-28T15:33:55.943 回答
0

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

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

文档对此非常困惑。

于 2021-02-09T19:46:47.960 回答