Domanda

Il mio obiettivo è scrivere un controller personalizzato per la visualizzazione della videocamera che:

  1. Può scattare foto in tutti e quattro gli orientamenti dell'interfaccia con la fotocamera posteriore e, se disponibile, anteriore.
  2. Ruota e ridimensiona correttamente l'anteprima del "video" e la foto a piena risoluzione.
  3. Consente di applicare un (semplice) effetto SIA al "video" di anteprima e alla foto a piena risoluzione.

Implementazione (su iOS 4.2 / Xcode 3.2.5):

A causa del requisito (3), dovevo passare ad AVFoundation.

Ho iniziato con Domande e risposte tecniche QA1702 e ho creato questi modifiche:

  1. Modificato sessionPreset in AVCaptureSessionPresetPhoto.
  2. Aggiunto un AVCaptureStillImageOutput come output aggiuntivo prima di avviare la sessione.

Il problema che sto riscontrando riguarda le prestazioni di elaborazione dell'immagine di anteprima (un fotogramma del "video" di anteprima).

Per prima cosa, ottengo il risultato UIImage di imageFromSampleBuffer: sul buffer di esempio da captureOutput:didOutputSampleBuffer:fromConnection:. Quindi, lo ridimensiono e lo ruoto per lo schermo utilizzando CGGraphicsContext.

A questo punto, il frame rate è già al di sotto dei 15 FPS specificati nell'output video della sessione e quando aggiungo l'effetto, scende al di sotto o intorno a 10. L'app si blocca rapidamente a causa della memoria insufficiente .

Sono riuscito a ridurre la frequenza dei fotogrammi a 9 FPS su iPhone 4 e 8 FPS su iPod Touch (4a generazione).

Ho anche aggiunto del codice per "svuotare" la coda di invio, ma non sono sicuro di quanto stia effettivamente aiutando. Fondamentalmente, ogni 8-10 frame, viene impostato un flag che segnala a captureOutput:didOutputSampleBuffer:fromConnection: di tornare immediatamente piuttosto che elaborare il frame. Il flag viene reimpostato al termine di un'operazione di sincronizzazione sulla coda di invio dell'output.

A questo punto non mi importa nemmeno dei bassi frame rate, ma ovviamente non possiamo spedire con i crash di memoria insufficienti. Qualcuno ha idea di come agire per prevenire le condizioni di memoria insufficiente in questo caso (e / o un modo migliore per "svuotare" la coda di spedizione)?

È stato utile?

Soluzione

Per evitare problemi di memoria, creare semplicemente un pool di rilascio automatico in captureOutput:didOutputSampleBuffer:fromConnection:.

Ciò ha senso poiché imageFromSampleBuffer: restituisce un oggetto UIImage rilasciato automaticamente. Inoltre, libera immediatamente qualsiasi oggetto rilasciato automaticamente creato dal codice di elaborazione delle immagini.

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

I miei test hanno dimostrato che questo funzionerà senza avvisi di memoria su un iPhone 4 o iPod Touch (4a generazione) anche se l'FPS richiesto è molto alto (ad esempio 60) e l'elaborazione delle immagini è molto lenta (ad esempio 0,5+ sec).

/ p>

VECCHIA SOLUZIONE:

Come ha sottolineato Brad, Apple consiglia di elaborare le immagini su un thread in background in modo da non interferire con la reattività dell'interfaccia utente. Non ho notato molto ritardo in questo caso, ma le migliori pratiche sono le migliori pratiche, quindi utilizza la soluzione sopra con il pool di rilascio automatico invece di eseguirla sulla coda di invio principale / thread principale.

Per evitare problemi di memoria, usa semplicemente la coda di invio principale invece di crearne una nuova.

Ciò significa anche che non è necessario passare al thread principale in captureOutput:didOutputSampleBuffer:fromConnection: quando si desidera aggiornare l'interfaccia utente.

In setupCaptureSession, cambia FROM:

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

A:

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

Altri suggerimenti

Un approccio fondamentalmente migliore sarebbe quello di utilizzare OpenGL per gestire gran parte del lavoro pesante relativo alle immagini per te (come vedo che stai provando in il tuo ultimo tentativo ). Tuttavia, anche in questo caso potresti avere problemi con la creazione di frame da elaborare.

Anche se sembra strano che ti imbatti in un accumulo di memoria durante l'elaborazione dei frame (nella mia esperienza, smetti di riceverli se non riesci a elaborarli abbastanza velocemente), le code di Grand Central Dispatch possono bloccarsi se stanno aspettando I / O.

Forse un semaforo di invio ti consentirebbe di limitare l'aggiunta di nuovi elementi alle code di elaborazione. Per ulteriori informazioni, consiglio vivamente " GCD Practicum ", in cui esamina l'ottimizzazione di un'operazione di elaborazione di miniature vincolate da I / O utilizzando semafori di invio.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top