Frage

Mein Ziel ist es, einen benutzerdefinierten Kameraansichts-Controller zu schreiben, der:

  1. Kann Fotos in allen vier Schnittstellenausrichtungen sowohl mit der hinteren als auch, falls verfügbar, der vorderen Kamera aufnehmen.
  2. Dreht und skaliert das "Video" der Vorschau sowie das Foto in voller Auflösung ordnungsgemäß.
  3. Ermöglicht das Anwenden eines (einfachen) Effekts sowohl auf das "Video" der Vorschau als auch auf das Foto in voller Auflösung.

    Implementierung (unter iOS 4.2 / Xcode 3.2.5):

    Aufgrund der Anforderung (3) musste ich zu AVFoundation wechseln.

    Ich habe mit Technischen Fragen und Antworten QA1702 begonnen und diese erstellt Änderungen:

    1. Das sessionPreset wurde in AVCaptureSessionPresetPhoto geändert.
    2. Vor dem Starten der Sitzung wurde ein AVCaptureStillImageOutput als zusätzliche Ausgabe hinzugefügt.

      Das Problem, das ich habe, ist die Leistung bei der Verarbeitung des Vorschaubildes (ein Rahmen des Vorschau- "Videos").

      Zuerst erhalte ich das UIImage-Ergebnis von imageFromSampleBuffer: auf dem Beispielpuffer von captureOutput:didOutputSampleBuffer:fromConnection:. Dann skaliere und drehe ich es mit einem CGGraphicsContext für den Bildschirm.

      Zu diesem Zeitpunkt liegt die Bildrate bereits unter den 15 FPS, die in der Videoausgabe der Sitzung angegeben sind, und wenn ich den Effekt hinzufüge, sinkt sie auf unter oder um 10. Schnell stürzt die App aufgrund des geringen Speichers ab .

      Ich hatte einige Erfolge damit, die Bildrate auf dem iPhone 4 auf 9 FPS und auf dem iPod Touch (4. Generation) auf 8 FPS zu senken.

      Ich habe auch Code hinzugefügt, um die Versandwarteschlange zu "leeren", bin mir aber nicht sicher, wie viel es tatsächlich hilft. Grundsätzlich wird alle 8-10 Frames ein Flag gesetzt, das den captureOutput:didOutputSampleBuffer:fromConnection: signalisiert, sofort zurückzukehren, anstatt den Frame zu verarbeiten. Das Flag wird zurückgesetzt, nachdem eine Synchronisierungsoperation in der Ausgabeversendungswarteschlange abgeschlossen ist.

      An dieser Stelle stört mich nicht einmal die niedrige Bildrate, aber offensichtlich können wir nicht mit den geringen Speicherabstürzen versenden. Hat jemand eine Idee, wie er Maßnahmen ergreifen kann, um die Bedingungen mit wenig Speicher in diesem Fall zu verhindern (und / oder eine bessere Möglichkeit, die Versandwarteschlange zu "leeren")?

War es hilfreich?

Lösung

Um Speicherprobleme zu vermeiden, erstellen Sie einfach einen Autorelease-Pool in captureOutput:didOutputSampleBuffer:fromConnection:.

Dies ist sinnvoll, da imageFromSampleBuffer: ein automatisch freigegebenes UIImage-Objekt zurückgibt. Außerdem werden alle automatisch freigegebenen Objekte, die durch Bildverarbeitungscode erstellt wurden, sofort freigegeben.

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

Meine Tests haben gezeigt, dass dies ohne Speicherwarnungen auf einem iPhone 4 oder iPod Touch (4. Generation) ausgeführt wird, selbst wenn die angeforderten FPS sehr hoch sind (z. B. 60) und die Bildverarbeitung sehr langsam ist (z. B. 0,5+ Sekunden).

ALTE LÖSUNG:

Wie Brad betonte, empfiehlt Apple, die Bildverarbeitung in einem Hintergrund-Thread durchzuführen, um die Reaktionsfähigkeit der Benutzeroberfläche nicht zu beeinträchtigen. Ich habe in diesem Fall keine große Verzögerung festgestellt, aber Best Practices sind Best Practices. Verwenden Sie daher die oben genannte Lösung mit Autorelease-Pool, anstatt diese in der Hauptversandwarteschlange / im Hauptthread auszuführen.

Um Speicherprobleme zu vermeiden, verwenden Sie einfach die Hauptversandwarteschlange, anstatt eine neue zu erstellen.

Dies bedeutet auch, dass Sie nicht zum Haupt-Thread in captureOutput:didOutputSampleBuffer:fromConnection: wechseln müssen, wenn Sie die Benutzeroberfläche aktualisieren möchten.

Ändern Sie im setupCaptureSession FROM:

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

TO:

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

Andere Tipps

Ein grundlegend besserer Ansatz wäre, OpenGL zu verwenden, um so viel bildbezogenes schweres Heben für Sie zu erledigen (wie ich sehe, versuchen Sie es in Ihr letzter Versuch ). Selbst dann können Probleme beim Aufbau der zu verarbeitenden Frames auftreten.

Während es seltsam erscheint, dass Sie bei der Verarbeitung von Frames auf Speicherakkumulation stoßen (meiner Erfahrung nach hören Sie einfach auf, sie zu erhalten, wenn Sie sie nicht schnell genug verarbeiten können), können Grand Central Dispatch-Warteschlangen blockiert werden, wenn sie auftreten warten auf I / O.

Mit einem Versandsemaphor können Sie möglicherweise das Hinzufügen neuer Elemente zu den Verarbeitungswarteschlangen drosseln. Für weitere Informationen empfehle ich Mike Ashs " GCD Practicum "- Artikel, in dem er sich mit der Optimierung eines E / A-gebundenen Miniaturbildverarbeitungsvorgangs mithilfe von Versandsemaphoren befasst.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top