18 de set de 2010 11:40 do Codo

Como liberar corretamente uma AVCaptureSession

Estou usando as classes do AV Foundation para capturar o fluxo de vídeo ao vivo da câmera e processar as amostras de vídeo. Isso funciona bem. No entanto, tenho problemas ao liberar corretamente as instâncias de base do AV (sessão de captura, camada de visualização, entrada e saída) quando terminar.

Quando não preciso mais da sessão e de todos os objetos associados, paro a sessão de captura e a libero. Isso funciona na maioria das vezes. No entanto, às vezes o aplicativo trava com um sinal EXEC_BAD_ACCESS gerado no segundo encadeamento criado pela fila de despacho (e onde as amostras de vídeo são processadas). A falha ocorre principalmente devido à minha própria instância de classe, que serve como o exemplo de delegado do buffer e é liberada após a interrupção da sessão de captura.

A documentação da Apple menciona o problema: Parar a sessão de captura é uma operação assíncrona. Ou seja: isso não acontece imediatamente. Em particular, o segundo segmento continua a processar amostras de vídeo e acessar diferentes instâncias, como a sessão de captura ou os dispositivos de entrada e saída.

Então, como libero corretamente o AVCaptureSession e todas as instâncias relacionadas? Existe uma notificação que me diga com segurança que o AVCaptureSession foi finalizado?

Aqui está o meu código:

Declarações:

AVCaptureSession* session;
AVCaptureVideoPreviewLayer* previewLayer;
UIView* view;

Configuração de instâncias:

AVCaptureDevice* camera = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
session = [[AVCaptureSession alloc] init];

AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice: camera error: &error];
[session addInput: input];
AVCaptureVideoDataOutput* output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
[session addOutput: output];

dispatch_queue_t queue = dispatch_queue_create("augm_reality", NULL);
[output setSampleBufferDelegate: self queue: queue];
dispatch_release(queue);

previewLayer = [[AVCaptureVideoPreviewLayer layerWithSession: session] retain];
previewLayer.frame = view.bounds;
[view.layer addSublayer: previewLayer];

[session startRunning];

Limpar:

[previewLayer removeFromSuperlayer];
[previewLayer release];
[session stopRunning];
[session release];

questionAnswers (0)

20 de jan de 2012 02:45 do Mahyar

você pode usar um dispatch_semaphore para cada fila e continuar com sua rotina de limpeza assim que terminar.

#define GCD_TIME(delayInSeconds) dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC)

static void vQueueCleanup(void* context) {
  VideoRecordingViewController *vc = (VideoRecordingViewController*)context;
  if (vc.vSema) dispatch_semaphore_signal(vc.vSema);
}

static void aQueueCleanup(void* context) {
  VideoRecordingViewController *vc = (VideoRecordingViewController*)context;
  if (vc.aSema) dispatch_semaphore_signal(vc.aSema);
}

//In your cleanup method:
vSema = dispatch_semaphore_create(0);
aSema = dispatch_semaphore_create(0);
self.avSession = nil;
if (vSema) dispatch_semaphore_wait(vSema, GCD_TIME(0.5));
if (aSema) dispatch_semaphore_wait(aSema, GCD_TIME(0.5));
[self.navigationController popViewControllerAnimated:YES];

Lembre-se de que você deve definir seus delegados de buffer de amostra dos objetos AVCaptureVideoDataOutput / AVCaptureAudioDataOutput para zero ou eles nunca liberarão suas filas associadas e, portanto, nunca chamarão seus finalizadores ao liberar seu AVCaptureSession.

[avs removeOutput:vOut];
[vOut setSampleBufferDelegate:nil queue:NULL];
04 de out de 2010 10:24 do VLegakis

. Este funciona para mim:

NSError *error = nil;

if(session)
    [session release];

// Create the session
session = [[AVCaptureSession alloc] init];


// Configure the session to produce lower resolution video frames, if your 
// processing algorithm can cope. We'll specify medium quality for the
// chosen device.
session.sessionPreset = AVCaptureSessionPresetMedium;

// Find a suitable AVCaptureDevice
AVCaptureDevice *device = [AVCaptureDevice
                           defaultDeviceWithMediaType:AVMediaTypeVideo];

// Create a device input with the device and add it to the session.
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device 
                                                                    error:&error];
if (!input) {
    // Handling the error appropriately.
}
[session addInput:input];

// Create a VideoDataOutput and add it to the session
AVCaptureVideoDataOutput *output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
[session addOutput:output];


// Configure your output.
dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
[output setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);

// Specify the pixel format
output.videoSettings = 
[NSDictionary dictionaryWithObject:
 [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] 
                            forKey:(id)kCVPixelBufferPixelFormatTypeKey];

// If you wish to cap the frame rate to a known value, such as 15 fps, set 
// minFrameDuration.
output.minFrameDuration = CMTimeMake(1, 15);

previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
[delegate layerArrived:previewLayer];

NSNotificationCenter *notify =
[NSNotificationCenter defaultCenter];
[notify addObserver: self
            selector: @selector(onVideoError:)
            name: AVCaptureSessionRuntimeErrorNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStart:)
            name: AVCaptureSessionDidStartRunningNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStop:)
            name: AVCaptureSessionDidStopRunningNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStop:)
            name: AVCaptureSessionWasInterruptedNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStart:)
            name: AVCaptureSessionInterruptionEndedNotification
            object: session];

// Start the session running to start the flow of data
[session startRunning];

Btw esta sequência parece resolver o problema de notificações síncronas :)

31 de jan de 2014 05:45 do 8,470

1) [AVCaptureSession stopRunning] é uma operação síncrona que bloqueia até o receptor parar completamente de funcionar. Portanto, todos esses problemas não devem mais acontecer.

04 de out de 2010 18:32 do 51.6k

sar o finalizador da fila de expedição. Quando a fila de expedição é encerrada, podemos ter certeza de que não haverá mais ação no segundo encadeamento em que os buffers de amostra são processados.

static void capture_cleanup(void* p)
{
    AugmReality* ar = (AugmReality *)p; // cast to original context instance
    [ar release];  // releases capture session if dealloc is called
}

...

dispatch_queue_t queue = dispatch_queue_create("augm_reality", NULL);
dispatch_set_context(queue, self);
dispatch_set_finalizer_f(queue, capture_cleanup);
[output setSampleBufferDelegate: self queue: queue];
dispatch_release(queue);
[self retain];

...

Infelizmente, agora tenho que parar explicitamente de capturar. Caso contrário, liberar minha instância não a liberará porque o segundo segmento agora também aumenta e diminui o contador.

Um outro problema é que minha classe agora é liberada a partir de dois threads diferentes. Isso é confiável ou é o próximo problema que causa falhas?

24 de set de 2010 10:01 do VLegakis

NSNotificationCenter *notify =
[NSNotificationCenter defaultCenter];
[notify addObserver: self
            selector: @selector(onVideoError:)
            name: AVCaptureSessionRuntimeErrorNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStart:)
            name: AVCaptureSessionDidStartRunningNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStop:)
            name: AVCaptureSessionDidStopRunningNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStop:)
            name: AVCaptureSessionWasInterruptedNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStart:)
            name: AVCaptureSessionInterruptionEndedNotification
            object: session];

Eles estão chamando de volta os métodos relevantes em session.stopRunning, session.startRunning etc.

Lá você também deve implementar algum bloco de limpeza não documentado:

AVCaptureInput* input = [session.inputs objectAtIndex:0];
[session removeInput:input];
AVCaptureVideoDataOutput* output = (AVCaptureVideoDataOutput*)[session.outputs objectAtIndex:0];
[session removeOutput:output];  

O que eu achei confuso, porém, é que, ao chamar seeion.stopRunning, onVideoStop: é chamado de forma síncrona! apesar da assunção assíncrona da Apple no caso.

Está funcionando, mas por favor me avise caso você veja algum truque. Eu preferiria trabalhar com ele de forma assíncrona.

obrigado

11 de dez de 2013 06:47 do souvickcse
 -(void)deallocSession
{
[captureVideoPreviewLayer removeFromSuperlayer];
for(AVCaptureInput *input1 in session.inputs) {
    [session removeInput:input1];
}

for(AVCaptureOutput *output1 in session.outputs) {
    [session removeOutput:output1];
}
[session stopRunning];
session=nil;
outputSettings=nil;
device=nil;
input=nil;
captureVideoPreviewLayer=nil;
stillImageOutput=nil;
self.vImagePreview=nil;

}

ão. Ele resolveu meu problema de aviso de pouca memória.

09 de out de 2010 08:33 do Codo

ebi uma resposta de um funcionário da Apple. Ele diz que é um problema conhecido:

Esse é um problema com o AVCaptureSession / VideoDataOutput no iOS 4.0-4.1 que foi corrigido e aparecerá em uma atualização futura. Por enquanto, você pode contorná-lo, aguardando um curto período após interromper o AVCaptureSession, por exemplo, meio segundo, antes de descartar a sessão e a saída de dados.

Ele / ela propõe o seguinte código:

dispatch_after(
    dispatch_time(0, 500000000),
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), // or main queue, or your own
    ^{
        // Do your work here.
        [session release];
        // etc.
    }
);

Ainda gosto mais da abordagem com o finalizador da fila de expedição porque esse código apenas adivinha quando o segundo encadeamento pode ter sido concluído.

yourAnswerToTheQuestion