Question

Mon objectif est d'écrire un contrôleur de vue de caméra personnalisé qui:

  1. Peut prendre des photos dans les quatre orientations d'interface avec le dos et, lorsqu'il est disponible, la caméra frontale.
  2. Faire tourner correctement et évolue la "vidéo" de l'aperçu ainsi que la photo pleine résolution.
  3. Permet d'appliquer un effet (simple) à la fois à la "vidéo" et à la photo de résolution complète de l'aperçu.

Implémentation (sur iOS 4.2 / Xcode 3.2.5):

En raison de l'exigence (3), je devais descendre à AvFoundation.

J'ai commencé avec Q&R technique QA1702 et a fait ces modifications:

  1. A changé le SessionPreset en AvcaptureSessionPresetPhoto.
  2. Ajout d'un AVCaptureRestiLImageOutput comme sortie supplémentaire avant de démarrer la session.

Le problème que j'ai est avec les performances du traitement de l'image d'aperçu (un cadre de l'aperçu "vidéo").

Tout d'abord, j'obtiens le résultat uiImage de imageFromSampleBuffer: sur le tampon d'échantillon de captureOutput:didOutputSampleBuffer:fromConnection:. Ensuite, je l'échelle et le fait pivoter pour l'écran à l'aide d'un CGGraphicsContext.

À ce stade, la fréquence d'images est déjà sous les 15 ips spécifiées dans la sortie vidéo de la session et lorsque j'ajoute l'effet, il tombe à sous ou environ 10. Rapidement l'application se bloque en raison de la mémoire faible.

J'ai réussi à réduire la fréquence d'images à 9 ips sur l'iPhone 4 et 8 ips sur l'iPod Touch (4e génération).

J'ai également ajouté un code pour "rincer" la file d'attente de répartition, mais je ne sais pas combien cela aide réellement. Fondamentalement, toutes les images de 8 à 10, un drapeau se déroule qui signale captureOutput:didOutputSampleBuffer:fromConnection: pour revenir immédiatement plutôt que de traiter le cadre. L'approche est réinitialisée après une opération de synchronisation sur les finitions de file d'attente d'expédition de sortie.

À ce stade, je ne me soucie même pas des basses fréquences d'images, mais évidemment, nous ne pouvons pas expédier avec les accidents de mémoire faible. Quelqu'un a-t-il une idée de la façon d'agir pour empêcher les conditions de mémoire faibles dans ce cas (et / ou une meilleure façon de "rincer" la file d'attente de répartition)?

Était-ce utile?

La solution

Pour éviter les problèmes de mémoire, créez simplement un pool d'autorease dans captureOutput:didOutputSampleBuffer:fromConnection:.

Cela a du sens depuis imageFromSampleBuffer: Renvoie un objet UIImage Autoreled. De plus, il libère les objets autores créés immédiatement par le code de traitement d'image immédiatement.

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

Mes tests ont montré que cela fonctionnera sans avertissements de mémoire sur un iPhone 4 ou iPod Touch (4e génération) même si le FPS demandé est très élevé (par exemple 60) et le traitement d'image est très lent (par exemple 0,5+ seconde).

Ancienne solution:

Comme Brad l'a souligné, Apple recommande que le traitement d'image soit sur un thread d'arrière-plan afin de ne pas interférer avec la réactivité de l'interface utilisateur. Je n'ai pas remarqué beaucoup de décalage dans ce cas, mais les meilleures pratiques sont les meilleures pratiques, alors utilisez la solution ci-dessus avec le pool d'autorelease au lieu d'exécuter cela sur la file d'attente / thread principal de répartition principale.

Pour éviter les problèmes de mémoire, utilisez simplement la file d'attente de répartition principale au lieu d'en créer une nouvelle.

Cela signifie également que vous n'avez pas à passer au fil principal dans captureOutput:didOutputSampleBuffer:fromConnection: Lorsque vous souhaitez mettre à jour l'interface utilisateur.

Dans setupCaptureSession, changer de:

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

À:

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

Autres conseils

Une approche fondamentalement meilleure serait d'utiliser OpenGL pour gérer autant de choses lourdes liées à l'image pour vous (car je vois que vous essayez Votre dernière tentative). Cependant, même alors, vous pourriez avoir des problèmes avec la construction de cadres à traiter.

Bien qu'il semble étrange que vous rencontriez une accumulation de mémoire lors du traitement des cadres (d'après mon expérience, vous cessez simplement de les obtenir si vous ne pouvez pas les traiter assez rapidement), les files d'attente de grandes répartitions centrales peuvent être coincées si elles attendent E / S.

Peut-être qu'un sémaphore d'expédition vous permettrait de gazéger l'ajout de nouveaux éléments aux files d'attente de traitement. Pour en savoir plus, je recommande fortement à Mike Ash "GCD Praccicum"Article, où il envisage d'optimiser une opération de traitement des miniatures liées aux E / S à l'aide de sémaphores de répartition.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top