2010-10-02 6 views
21

Ich versuche das neue AVFoundation framework für die Aufnahme von Standbildern mit dem iPhone zu verwenden.iPhone SDK 4 AVFoundation - Wie benutze CaptureStillImageAsynchronousFromConnection korrekt?

Mit einem Tastendruck wird dieses Verfahren aufgerufen. Ich kann den Auslöser hören, aber ich kann die Protokollausgabe nicht sehen. Wenn ich diese Methode mehrmals anrufe, wird die Kameravorschau eingefroren.

Gibt es eine Anleitung, wie Sie captureStillImageAsynchronouslyFromConnection verwenden können?

[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection: 
       [[self stillImageOutput].connections objectAtIndex:0] 
        completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, 
          NSError *error) { 
               NSLog(@"inside"); 
          }]; 
 
- (void)initCapture { 
    AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput 
              deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] 
              error:nil]; 

    AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init]; 

    captureOutput.alwaysDiscardsLateVideoFrames = YES; 

    dispatch_queue_t queue; 
    queue = dispatch_queue_create("cameraQueue", NULL); 
    [captureOutput setSampleBufferDelegate:self queue:queue]; 
    dispatch_release(queue); 

    NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey; 
    NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]; 
    NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key]; 
    [captureOutput setVideoSettings:videoSettings]; 

    self.captureSession = [[AVCaptureSession alloc] init]; 
    self.captureSession.sessionPreset = AVCaptureSessionPresetLow; 

    [self.captureSession addInput:captureInput]; 
    [self.captureSession addOutput:captureOutput]; 

    self.prevLayer = [AVCaptureVideoPreviewLayer layerWithSession: self.captureSession]; 

    [self.prevLayer setOrientation:AVCaptureVideoOrientationLandscapeLeft]; 

    self.prevLayer.frame = CGRectMake(0.0, 0.0, 480.0, 320.0); 
    self.prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; 

    [self.view.layer addSublayer: self.prevLayer]; 


    // Setup the default file outputs 
    AVCaptureStillImageOutput *_stillImageOutput = [[[AVCaptureStillImageOutput alloc] init] autorelease]; 
    NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: 
            AVVideoCodecJPEG, AVVideoCodecKey, 
            nil]; 
    [_stillImageOutput setOutputSettings:outputSettings]; 
    [outputSettings release]; 
    [self setStillImageOutput:_stillImageOutput]; 

    if ([self.captureSession canAddOutput:stillImageOutput]) { 
     [self.captureSession addOutput:stillImageOutput]; 
    } 

    [self.captureSession commitConfiguration]; 
    [self.captureSession startRunning]; 

} 
+0

Ich weiß nicht, ob das im Jahr 2010 wahr war, aber seit Ende 2011, ich habe einfach 'captureStillImageAsynchronousFromConnection' bei der Gleichzeitig mit dem Erhalt eines Video-Feeds mit dem Delegate von AVCaptureVideoDataOutput, "captureOutput" und während der Verwendung eines previewLayers. Ich bekomme ein 5megapixel Bild von stillImage, ein 852x640 für den Video Feed und previewLayer. – mahboudz

+0

Der obige Kommentar basiert auf einem iPhone4. Auf einem iPhone 4s bekomme ich noch eine 8megapixel, während ich für den Videofeed und previewLayer 852x640 bekomme. – mahboudz

+0

Was ist der Unterschied zwischen initWithSession und layerWithSession? Die Dokumente diskutieren nicht, wann man das eine gegen das andere verwendet. http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVCaptureVideoPreviewLayer_Class/Reference/Reference.html – knite

Antwort

16

Wir hatten dieses Problem bei 4.0 noch in der Beta war. Ich habe einiges ausprobiert. Hier gehts:

  • AVCaptureStillImageOutput und AVCaptureVideoDataOutput scheinen nicht gut miteinander zu spielen. Wenn die Videoausgabe ausgeführt wird, scheint die Bildausgabe nie abgeschlossen zu sein (bis Sie die Sitzung pausieren, indem Sie das Telefon in den Ruhezustand versetzen; dann scheint es, dass Sie ein einzelnes Bild erhalten).
  • AVCaptureStillImageOutput scheint nur sinnvoll zu funktionieren mit AVCaptureSessionPresetPhoto; Andernfalls erhalten Sie effektiv JPEG-kodierte Videoframes. Könnte auch hochwertigere BGRA-Frames verwenden (übrigens scheint die native Ausgabe der Kamera BGRA zu sein; es scheint nicht das Farb-Subsampling von 2vuy/420v zu haben).
  • Das Video (alles was nicht Foto ist) und Foto Voreinstellungen scheinen grundlegend anders; Sie erhalten niemals Video-Frames, wenn sich die Sitzung im Fotomodus befindet (Sie erhalten auch keinen Fehler). Vielleicht haben sie das geändert ...
  • Sie können anscheinend nicht zwei Aufnahmesitzungen haben (eine mit einer Videovoreinstellung und einer Videoausgabe, eine mit Fotovoreinstellung und einer Bildausgabe). Sie könnten das behoben haben.
  • Sie können die Sitzung stoppen, die Voreinstellung auf Foto ändern, die Sitzung starten und das Foto machen. Wenn das Foto fertig ist, stoppen Sie, ändern Sie die Voreinstellung zurück und starten Sie erneut. Dies dauert eine Weile und die Videovorschau-Schicht bleibt stehen und sieht schrecklich aus (sie passt die Belichtungsstufen an). Dies kam auch gelegentlich in der Beta-Version zum Stillstand (nach dem Aufruf von -stopRunning war session.running immer noch JA).
  • Sie können möglicherweise die AVCaptureConnection deaktivieren (es ist angenommen zu arbeiten). Ich erinnere mich an diese Sackgasse; Vielleicht haben sie das behoben.

Ich landete nur Videoframes erfassen. Die Schaltfläche "Bild aufnehmen" setzt einfach eine Flagge; Wenn im Video-Frame-Callback das Flag gesetzt ist, wird anstelle eines UIImage * der Videoframe zurückgegeben. Dies war ausreichend für unsere Bildverarbeitungsbedürfnisse — "Take Picture" existiert weitgehend, so dass der Benutzer eine negative Antwort erhalten kann (und eine Option, einen Fehlerbericht zu senden); Wir wollen eigentlich keine 2/3/5 Megapixel-Bilder, da sie ewig brauchen. Wenn Video-Frames nicht gut genug sind (dh Sie möchten Frames zwischen hochauflösenden Bildaufnahmen aufnehmen), würde ich zuerst sehen, ob sie mit mehreren AVCapture-Sitzungen behoben haben, da dies die einzige Möglichkeit ist, die Sie einstellen können beide Voreinstellungen

Es ist wahrscheinlich wert, einen Fehler zu archivieren. Ich habe einen Bug um den Start von 4.0 GM eingereicht; Apple hat mich nach einem Beispielcode gefragt, aber zu diesem Zeitpunkt hatte ich beschlossen, die Umgehung des Videoframes zu verwenden und eine Freigabe zu veröffentlichen.

Zusätzlich ist die "niedrige" Voreinstellung sehr niedrig-res (und führt zu einer Videovorschau mit niedriger Auflösung und niedriger Bildrate). Ich würde für 640x480 gehen, wenn verfügbar, zurückfallen auf Medium, wenn nicht.

+1

perfekt richtig, dass AVCaptureStillImageOutput scheint nur sinnvoll zu arbeiten mit AVCaptureSessionPresetPhoto –

61

Nach viel Versuch und Irrtum, ich arbeitete heraus, wie dies zu tun.

Hinweis: Apples offizielle Dokumente sind - einfach - falsch. Der Code, den sie dir geben, funktioniert nicht wirklich.

Ich schrieb es hier oben mit Schritt-für-Schritt-Anleitung:

http://red-glasses.com/index.php/tutorials/ios4-take-photos-with-live-video-preview-using-avfoundation/

Viele Code auf den Link, aber zusammenfassend:

-(void) viewDidAppear:(BOOL)animated 
{ 
    AVCaptureSession *session = [[AVCaptureSession alloc] init]; 
    session.sessionPreset = AVCaptureSessionPresetMedium; 

    CALayer *viewLayer = self.vImagePreview.layer; 
    NSLog(@"viewLayer = %@", viewLayer); 

    AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session]; 

    captureVideoPreviewLayer.frame = self.vImagePreview.bounds; 
    [self.vImagePreview.layer addSublayer:captureVideoPreviewLayer]; 

    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 

    NSError *error = nil; 
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; 
    if (!input) { 
     // Handle the error appropriately. 
     NSLog(@"ERROR: trying to open camera: %@", error); 
    } 
    [session addInput:input]; 

stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; 
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil]; 
[stillImageOutput setOutputSettings:outputSettings]; 

[session addOutput:stillImageOutput]; 

    [session startRunning]; 
} 

-(IBAction) captureNow 
{ 
    AVCaptureConnection *videoConnection = nil; 
    for (AVCaptureConnection *connection in stillImageOutput.connections) 
    { 
     for (AVCaptureInputPort *port in [connection inputPorts]) 
     { 
      if ([[port mediaType] isEqual:AVMediaTypeVideo]) 
      { 
       videoConnection = connection; 
       break; 
      } 
     } 
     if (videoConnection) { break; } 
    } 

    NSLog(@"about to request a capture from: %@", stillImageOutput); 
    [stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) 
    { 
     CFDictionaryRef exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL); 
     if (exifAttachments) 
     { 
      // Do something with the attachments. 
      NSLog(@"attachements: %@", exifAttachments); 
     } 
     else 
      NSLog(@"no attachments"); 

     NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; 
     UIImage *image = [[UIImage alloc] initWithData:imageData]; 

     self.vImage.image = image; 
    }]; 
} 
+4

Ich kann nicht genug +1. Danke für das Teilen! –

+0

Tscott (im Kommentar unten) hat ein funktionierendes Beispiel für Adams Code. Ich habe es heruntergeladen und installiert von Github und initialliy es funktionierte nicht auf meinem iPad. Nach dem Überprüfen der Xib-Dateien sah ich jedoch, dass er die iPhone-Xib-Dateianschlüsse mit dem Code verbunden hatte, aber dass das iPad Xib leer war. Nachdem ich die Bildansicht, Labels, Schaltflächen und andere Objekte zur iPad Xib-Datei hinzugefügt hatte, konnte ich den Code ausführen lassen. –

+0

Aktualisierter Link zum Tutorial: http://red-glasses.com/index.php/tutorials/ios4-take-photos-with-live-video-preview-using-avfoundation/ –

6

Dies hat eine große Hilfe gewesen - Ich steckte eine ganze Weile im Unkraut fest und versuchte dem AVCam-Beispiel zu folgen.

Hier ist ein komplettes Arbeitsprojekt mit meinen Kommentaren, die erklären, was passiert. Dies zeigt, wie Sie den Capture Manager mit mehreren Ausgaben verwenden können. In diesem Beispiel gibt es zwei Ausgänge.

Die erste ist die Standbildausgabe des obigen Beispiels.

Der zweite bietet Frame-für-Frame-Zugriff auf das Video aus der Kamera. Sie können mehr Code hinzufügen, um etwas Interessantes mit den Frames zu tun, wenn Sie möchten. In diesem Beispiel aktualisiere ich gerade einen Frame-Zähler auf dem Bildschirm aus dem Delegate-Rückruf.

https://github.com/tdsltm/iphoneStubs/tree/master/VideoCamCaptureExample-RedGlassesBlog/VideoCamCaptureExample-RedGlassesBlog

+0

Großartig gearbeitet. Nur Köpfe bis zu jedem, der diesen Code von Gitub herunterlädt - die iPad xib-Datei ist leer. Sie müssen die iPhone xib-Objekte kopieren und sie in die iPad xib-Dateien kopieren und einfügen. Verbinden Sie dann die Objekte mit den entsprechenden Anschlüssen. –

+0

Funktioniert nicht für mich. Nicht in iphone 5S mit beta8, nicht auf ipad mit IOS 7.1.2. –

0

Sie sollten Adam ‚s Antwort, verwenden aber wenn Sie Swift verwenden (wie die meisten von euch wahrscheinlich heute tun), hier ist ein Swift 1.2 Port seines Code:

  1. Fabrikat sicher, dass Sie import ImageIO
  2. hinzufügen Eigenschaft private var stillImageOutput: AVCaptureStillImageOutput!
  3. Instantiate stillImageOutput vor captureSession.startRunning():

So:

stillImageOutput = AVCaptureStillImageOutput() 
stillImageOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG] 
captureSession.addOutput(stillImageOutput) 

Dann diesen Code verwenden, um ein Bild zu erfassen:

private func captureImage() { 
    var videoConnection: AVCaptureConnection? 
    for connection in stillImageOutput.connections as! [AVCaptureConnection] { 
     for port in connection.inputPorts { 
      if port.mediaType == AVMediaTypeVideo { 
       videoConnection = connection 
       break 
      } 
     } 
     if videoConnection != nil { 
      break 
     } 
    } 
    print("about to request a capture from: \(stillImageOutput)") 
    stillImageOutput.captureStillImageAsynchronouslyFromConnection(videoConnection) { (imageSampleBuffer: CMSampleBuffer!, error: NSError!) -> Void in 
     let exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, nil) 
     if let attachments = exifAttachments { 
      // Do something with the attachments 
      print("attachments: \(attachments)") 
     } else { 
      print("no attachments") 
     } 
     let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageSampleBuffer) 
     let image = UIImage(data: imageData) 
     // Do something with the image 
    } 
} 

Dies alles setzt voraus, dass Sie bereits einen AVCaptureSession Setup haben und brauchen nur eine von ihm noch nehmen , wie ich.