Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I properly cleanup an AVCaptureSession and AVCaptureVideoPreviewLayer

Tags:

ios

ios4

I'm using the AVFoundation api to create a camera preview view and I'm having trouble cleaning up after I'm done.

The best answer I've found to this problem is in this SO thread, thanks Codo.

However, he doesn't address the deallocation of the AVCaptureVideoPreviewLayer, and that's where I'm having trouble.

In my view controller class I have some initialization code in a startCameraCapture method. Listening to Codo's answer, I'm using dispatch_set_finalizer_f(_captureQueue, capture_cleanup); to register a callback to be called when the queue is truly closed. I'm also retaining self, to make sure my object doesn't go away before the queue is done calling my object. I then use the capture_cleanup callback to release self.

-(void) startCameraCapture {
    _camSession = [[AVCaptureSession alloc] init];
    if (_previewLayer == nil) {
        _previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_camSession];
    }
    _previewLayer.frame = self.compView.bgView.frame;   
    [self.compView.bgView.layer addSublayer:_previewLayer];

    // Get the default camera device
    AVCaptureDevice* camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    // Create a AVCaptureInput with the camera device
    NSError *error=nil;
    AVCaptureInput* cameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:camera error:&error];
    if (cameraInput == nil) {
        NSLog(@"Error to create camera capture:%@",error);

    }

    AVCaptureVideoDataOutput* videoOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease];

    // create a queue to run the capture on
    _captureQueue=dispatch_queue_create("captureQueue", NULL);
    dispatch_set_context(_captureQueue, self);
    dispatch_set_finalizer_f(_captureQueue, capture_cleanup);

    // setup our delegate
    [videoOutput setSampleBufferDelegate:self queue:_captureQueue];

    dispatch_release(_captureQueue);

    // retain self as a workouround a queue finalization bug in apples's sdk 
    // per Stackoverflow answer https://stackoverflow.com/questions/3741121/how-to-properly-release-an-avcapturesession
    [self retain];

    // configure the pixel format
    videoOutput.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey,
                                 nil];

    // and the size of the frames we want
    [_camSession setSessionPreset:AVCaptureSessionPresetMedium];

    // Add the input and output
    [_camSession addInput:cameraInput];
    [_camSession addOutput:videoOutput];

    [cameraInput release];

    // Start the session
    [_camSession startRunning];     
}

Here the capture_cleanup callback:

 static void capture_cleanup(void* p)
    {
        LiveCompViewController* ar = (LiveCompViewController*)p;
        [ar release];  // releases capture session if dealloc is called
    }

Then my cleanup code looks like this:

-(void) stopCameraCapture {
[_camSession stopRunning];
    [_camSession release];
    _camSession=nil;    

    // Remove the layer in order to release the camSession
    [_previewLayer removeFromSuperlayer];
    _previewLayer = nil;

}

The problem I'm having is that removing the _previewLayer from the superlayer in stopCameraCapture is causing the following console error:

"...modifying layer that is being finalized..."

But I need to remove the layer so that it gets released and deallocated so that it releases the _camSession which in turn releases the dispatch_queue and then finally calls my capture_cleanup callback which finally releases self.

I don't understand why I'm getting the console error and how to fix it. Why is the Layer being finalized at the time I'm calling [_previewLayer removeFromSuperlayer] if self.dealloc hasn't been called.

Note: self is a viewController and I haven't popped it yet, so it is retained by the NavigationContoller.

like image 248
eddy Avatar asked Mar 23 '11 18:03

eddy


1 Answers

Try stopping the session before releasing:

[captureSession stopRunning];
like image 101
Henry Avatar answered Oct 03 '22 21:10

Henry