Pregunta

Mi objetivo es escribir un controlador de vista de cámara personalizado que:

  1. Puede tomar fotografías en las cuatro orientaciones de interfaz con la cámara trasera y, cuando esté disponible, la cámara frontal.
  2. Gira y escala correctamente el "video" de vista previa, así como la foto de resolución completa.
  3. Permite aplicar un efecto (simple) a AMBOS, el "video" de vista previa y la foto de resolución completa.

Implementación (en iOS 4.2 / Xcode 3.2.5):

Debido al requisito (3), necesitaba ir a AVFoundation.

Empecé con Preguntas y respuestas técnicas QA1702 e hice estas cambios:

  1. Se cambió sessionPreset a AVCaptureSessionPresetPhoto.
  2. Se agregó un AVCaptureStillImageOutput como un resultado adicional antes de comenzar la sesión.

El problema que tengo es con el rendimiento del procesamiento de la imagen de vista previa (un fotograma del "video" de vista previa).

Primero, obtengo el resultado de UIImage de imageFromSampleBuffer: en el búfer de muestra de captureOutput:didOutputSampleBuffer:fromConnection:. Luego, lo escalo y lo giro para la pantalla usando un CGGraphicsContext.

En este punto, la velocidad de fotogramas ya está por debajo de los 15 FPS que se especifica en la salida de video de la sesión y cuando agrego el efecto, cae por debajo o alrededor de 10. Rápidamente la aplicación se bloquea debido a poca memoria .

He tenido cierto éxito al reducir la velocidad de fotogramas a 9 FPS en el iPhone 4 y 8 FPS en el iPod Touch (cuarta generación).

También agregué algo de código para "limpiar" la cola de despacho, pero no estoy seguro de cuánto ayuda realmente. Básicamente, cada 8-10 fotogramas, se establece una bandera que indica a captureOutput:didOutputSampleBuffer:fromConnection: que regrese de inmediato en lugar de procesar el fotograma. La bandera se restablece después de que finaliza una operación de sincronización en la cola de envío de salida.

En este punto, ni siquiera me importan las bajas velocidades de cuadro, pero obviamente no podemos enviarnos con los bloqueos de poca memoria. ¿Alguien tiene alguna idea de cómo tomar medidas para prevenir las condiciones de poca memoria en este caso (y / o una mejor manera de "vaciar" la cola de despacho)?

¿Fue útil?

Solución

Para evitar problemas de memoria, simplemente cree un grupo de liberación automática en captureOutput:didOutputSampleBuffer:fromConnection:.

Esto tiene sentido ya que imageFromSampleBuffer: devuelve un objeto UIImage liberado automáticamente. Además, libera cualquier objeto liberado automáticamente creado por el código de procesamiento de imágenes de inmediato.

// 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];
}

Mis pruebas han demostrado que esto se ejecutará sin advertencias de memoria en un iPhone 4 o iPod Touch (cuarta generación) incluso si el FPS solicitado es muy alto (por ejemplo, 60) y el procesamiento de imágenes es muy lento (por ejemplo, más de 0,5 segundos).

ANTIGUA SOLUCIÓN:

Como señaló Brad, Apple recomienda que el procesamiento de imágenes esté en un hilo de fondo para no interferir con la capacidad de respuesta de la interfaz de usuario. No noté mucho retraso en este caso, pero las mejores prácticas son las mejores prácticas, así que use la solución anterior con el grupo de liberación automática en lugar de ejecutar esto en la cola de envío principal / hilo principal.

Para evitar problemas de memoria, simplemente use la cola de despacho principal en lugar de crear una nueva.

Esto también significa que no tiene que cambiar al hilo principal en captureOutput:didOutputSampleBuffer:fromConnection: cuando desee actualizar la interfaz de usuario.

En setupCaptureSession, cambie DE:

// 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()];

Otros consejos

Un enfoque fundamentalmente mejor sería usar OpenGL para manejar la mayor parte del trabajo pesado relacionado con la imagen por usted (como veo que está intentando en su último intento ). Sin embargo, incluso entonces, es posible que tenga problemas con la creación de marcos para su procesamiento.

Si bien parece extraño que se esté acumulando memoria al procesar marcos (en mi experiencia, simplemente deja de obtenerlos si no puede procesarlos lo suficientemente rápido), las colas de Grand Central Dispatch pueden atascarse si están esperando en E / S.

Quizás un semáforo de despacho le permitiría acelerar la adición de nuevos elementos a las colas de procesamiento. Para obtener más información al respecto, recomiendo encarecidamente el " GCD Practicum ", donde analiza la optimización de una operación de procesamiento de miniaturas enlazadas de E / S utilizando semáforos de despacho.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top