Pergunta

Meu objetivo é escrever um controlador de visualização de câmera personalizado que:

  1. Pode tirar fotos em todas as quatro orientações de interface com a câmera traseira e, quando disponível, frontal.
  2. Gira e dimensiona adequadamente o "vídeo" de visualização, bem como a foto em resolução total.
  3. Permite que um efeito (simples) seja aplicado a AMBOS o "vídeo" de visualização e a foto de resolução total.

Implementação (no iOS 4.2 / Xcode 3.2.5):

Devido ao requisito (3), precisei ir para AVFoundation.

Comecei com Perguntas técnicas e respostas QA1702 e fiz essas mudanças:

  1. Foi alterado o sessionPreset para AVCaptureSessionPresetPhoto.
  2. Adicionado um AVCaptureStillImageOutput como uma saída adicional antes de iniciar a sessão.

O problema que estou tendo é com o desempenho do processamento da imagem de visualização (um quadro do "vídeo" de visualização).

Primeiro, obtenho o resultado UIImage de imageFromSampleBuffer: no buffer de amostra de captureOutput:didOutputSampleBuffer:fromConnection:. Então, eu o dimensiono e giro para a tela usando um CGGraphicsContext.

Neste ponto, a taxa de quadros já está abaixo dos 15 FPS especificados na saída de vídeo da sessão e quando adiciono o efeito, ele cai para menos ou em torno de 10. O aplicativo trava rapidamente devido à falta de memória .

Tive algum sucesso ao diminuir a taxa de quadros para 9 FPS no iPhone 4 e 8 FPS no iPod Touch (4ª geração).

Também adicionei algum código para "liberar" a fila de despacho, mas não tenho certeza do quanto isso está realmente ajudando. Basicamente, a cada 8 a 10 quadros, um sinalizador é definido que sinaliza captureOutput:didOutputSampleBuffer:fromConnection: para retornar imediatamente em vez de processar o quadro. O sinalizador é redefinido após a conclusão de uma operação de sincronização na fila de despacho de saída.

Neste ponto, eu nem me importo com as baixas taxas de quadros, mas obviamente não podemos enviar com as falhas de memória baixas. Alguém tem alguma ideia de como agir para evitar as condições de pouca memória neste caso (e / ou a melhor maneira de "liberar" a fila de despacho)?

Foi útil?

Solução

Para evitar problemas de memória, basta criar um pool de liberação automática em captureOutput:didOutputSampleBuffer:fromConnection:.

Isso faz sentido, pois imageFromSampleBuffer: retorna um objeto UIImage liberado automaticamente. Além disso, ele libera qualquer objeto liberado automaticamente criado pelo código de processamento de imagem imediatamente.

// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
fromConnection:(AVCaptureConnection *)connection
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    // Create a UIImage from the sample buffer data
    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];

    < Add your code here that uses the image >

    [pool release];
}

Meus testes mostraram que isso será executado sem avisos de memória em um iPhone 4 ou iPod Touch (4ª geração), mesmo se o FPS solicitado for muito alto (por exemplo, 60) e o processamento de imagem for muito lento (por exemplo, 0,5+ segundos).

SOLUÇÃO ANTIGA:

Como Brad apontou, a Apple recomenda que o processamento de imagem seja feito em segundo plano para não interferir na capacidade de resposta da IU. Não notei muito atraso neste caso, mas as melhores práticas são as melhores práticas, então use a solução acima com pool de autorelease em vez de executá-lo na fila de despacho principal / thread principal.

Para evitar problemas de memória, basta usar a fila de despacho principal em vez de criar uma nova.

Isso também significa que você não precisa mudar para o thread principal em captureOutput:didOutputSampleBuffer:fromConnection: quando quiser atualizar a IU.

Em setupCaptureSession, altere FROM:

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

PARA:

// we want our dispatch to be on the main thread
[output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];

Outras dicas

Uma abordagem fundamentalmente melhor seria usar OpenGL para lidar com o máximo de trabalho pesado relacionado a imagens para você (como vejo que você está tentando em sua última tentativa ). No entanto, mesmo assim, você pode ter problemas com a criação de frames a serem processados.

Embora pareça estranho que você esteja executando um acúmulo de memória ao processar frames (na minha experiência, você simplesmente para de recebê-los se não puder processá-los rápido o suficiente), as filas do Grand Central Dispatch podem ficar congestionadas se elas estão esperando no I / O.

Talvez um semáforo de despacho permitisse que você acelerasse a adição de novos itens às filas de processamento. Para obter mais informações sobre isso, recomendo fortemente o " GCD Practicum "artigo, onde ele analisa a otimização de uma operação de processamento de miniaturas vinculadas a E / S usando semáforos de despacho.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top