web-dev-qa-db-fra.com

iPhone SDK 4 AVFoundation - Comment utiliser correctement captureStillImageAsynchronouslyFromConnection?

J'essaie d'utiliser le nouveau AVFoundation framework pour prendre des photos avec l'iPhone.

Avec un bouton appuyez sur cette methos est appelée. Je peux entendre le son de l'obturateur mais je ne peux pas voir la sortie du journal. Si j'appelle cette méthode plusieurs fois, l'aperçu de la caméra se fige.

Existe-t-il un tutoriel sur l'utilisation de captureStillImageAsynchronouslyFromConnection?

[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:
                [[self stillImageOutput].connections objectAtIndex:0]
                     completionHandler:^(CMSampleBufferRef imageDataSampleBuffer,
                            NSError *error) {
                                              NSLog(@"inside");
                            }];
 - (void) initCapture {
 AVCaptureDeviceInput * captureInput = [AVCaptureDeviceInput [.________] 
 AVCaptureVideoDataOutput * captureOutput = [[AVCaptureVideoDataOutput alloc] init]; 
 
 CaptureOutput.alwaysDiscardsLateVideoFrames = YES; 
 
 dispatch_queue_t file d'attente; 
 queue = dispatch_queue_create ("cameraQueue", NULL); 
 [captureOutput setSampleBufferDelegate: self file: queue]; 
 dispatch_release (file d'attente); 
 
 NSString * clé = (NSString *) kCVPixelBufferPixelFormatTypeKey; 
 NSNumber * value = [NSNumber numberWithUnsignedInt: kCVPixelFormatType_32BGRA]; 
 NSDictionary * videoSettings = [NSDictionary dictionaryWithObject: valeur pourKey: clé]; 
 [captureOutput setVideoSettings: videoSettings]; 
 
 self.captureSession = [[AVCaptureSession alloc] init]; 
 self.captureSession.srespreset = AVCaptureSessionPresetLow; 
 
 [self.captureSession addInput : captureInput]; 
 [self.captureSession addOutput: captureOutput]; 
 
 self.prevLayer = [AVCaptureVideoPreviewLayer layerWithSession: self.captureSession]; 
 
 [auto.prevLayer setOrientation: AVCaptureVideoOrientationLandscapeLeft]; 
 
 self.prevLayer.frame = CGRectMake (0.0, 0.0, 480.0, 320.0); 
 self.prevLayer.frame = AVL. 
 
 [self.view.layer addSublayer: self.prevLayer]; 
 
 
 // Configurer les sorties de fichier par défaut 
 AVCaptureStillImageOutput * _stillImageOutput = [[[[AVCaptureStillImageOutput all] init] autorelease]; 
 NSDictionary * outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: 
 AVVide oCodecJPEG, AVVideoCodecKey, 
 nil]; 
 [_stillImageOutput setOutputSettings: paramètres de sortie]; 
 
 if ([self.captureSession canAddOutput: stillImageOutput]) {
 [self.captureSession addOutput: stillImageOutput]; 
} 
 
 [Self.captureSession commitConfiguration]; 
 [self.captureSession startRunning]; 
 
} 
21
dan

Nous avons eu ce problème lorsque la version 4.0 était encore en version bêta. J'ai essayé pas mal de choses. Voici:

  • AVCaptureStillImageOutput et AVCaptureVideoDataOutput ne semblent pas fonctionner correctement. Si la sortie vidéo est en cours d'exécution, la sortie de l'image ne semble jamais s'achever (jusqu'à ce que vous interrompiez la session en mettant le téléphone en veille, vous semblez alors ne plus avoir qu'une seule image).
  • AVCaptureStillImageOutput semble fonctionner uniquement avec AVCaptureSessionPresetPhoto; sinon, vous obtenez efficacement des images vidéo codées en JPEG. Vous pouvez également utiliser des images BGRA de meilleure qualité (la sortie de l'appareil photo semble être BGRA; elle ne semble pas avoir le sous-échantillonnage couleur de 2vuy/420v).
  • Les paramètres vidéo (tout ce qui n'est pas Photo) et les paramètres prédéfinis Photo semblent fondamentalement différents. vous n'obtenez jamais d'images vidéo si la session est en mode photo (vous n'obtenez pas d'erreur non plus). Peut-être qu'ils ont changé cela ...
  • Vous ne pouvez pas sembler avoir deux sessions de capture (une avec un préréglage vidéo et une sortie vidéo, une avec un préréglage Photo et une sortie image). Ils pourraient avoir résolu ce problème.
  • Vous pouvez arrêter la session, modifier le préréglage en photo, démarrer la session, prendre la photo et, une fois la photo terminée, arrêter, modifier le préréglage et recommencer. Cela prend un certain temps et la couche d'aperçu vidéo se bloque et a une apparence affreuse (elle ajuste à nouveau les niveaux d'exposition). Il arrivait aussi parfois que la beta soit bloquée (après l'appel de -stopRunning, session.running était toujours OUI).
  • Vous pourrez peut-être désactiver AVCaptureConnection (c'est supposé fonctionner). Je me souviens de cette impasse; ils peuvent avoir résolu ce problème.

J'ai fini par capturer des images vidéo. Le bouton "prendre une photo" définit simplement un drapeau; dans le rappel d'image vidéo, si l'indicateur est défini, il renvoie l'image vidéo au lieu d'un UIImage *. C’était suffisant pour nos besoins en traitement d’images - "prendre une photo" existe en grande partie afin que l’utilisateur puisse obtenir une réponse négative (et une option pour soumettre un rapport de bogue); nous ne voulons pas réellement d'images 2/3/5 mégapixels, car leur traitement prend beaucoup de temps.

Si les images vidéo ne sont pas suffisantes (par exemple, si vous souhaitez capturer des images du viseur entre des captures d’images haute résolution), je commencerai par vérifier si elles ont été corrigées à l’aide de plusieurs sessions AVCapture, car c’est le seul moyen de définir les deux paramètres prédéfinis.

Cela vaut probablement le coup de rapporter un bogue. J'ai déposé un bogue lors du lancement de la version 4.0 de GM; Apple m'a demandé un exemple de code, mais j'avais alors décidé d'utiliser la solution de contournement d'image vidéo et j'avais une version à publier.

De plus, le préréglage "bas" est très basse résolution (et donne un aperçu vidéo basse résolution et basse fréquence d'images). J'irais pour 640x480 si disponible, retombant à moyen sinon.

16
tc.

Après beaucoup d'essais et d'erreurs, j'ai trouvé comment faire.

Astuce: les documents officiels d'Apple sont - tout simplement - faux. Le code qu'ils vous donnent ne fonctionne pas réellement.

Je l'ai écrit ici avec des instructions étape par étape:

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

Beaucoup de code sur le lien, mais en résumé:

-(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;
     }];
}
62
Adam

Cela a été une aide précieuse - j'ai été coincé dans les mauvaises herbes pendant un bon bout de temps en essayant de suivre l'exemple d'AVCam. 

Voici un projet complet avec mes commentaires qui expliquent ce qui se passe. Ceci illustre comment utiliser le gestionnaire de capture avec plusieurs sorties. Dans cet exemple, il existe deux sorties.

Le premier est la sortie d'image fixe de l'exemple ci-dessus.

La seconde fournit un accès image par image à la vidéo sortant de la caméra. Vous pouvez ajouter plus de code pour créer quelque chose d’intéressant avec les images si vous le souhaitez. Dans cet exemple, je mets à jour un compteur de trames sur l’écran depuis le rappel du délégué. 

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

6
Tereus Scott
3
Michael Grinich

Vous devriez utiliser la réponse de Adam , mais si vous utilisez Swift (comme beaucoup le font probablement de nos jours), voici un portage Swift 1.2 de son code:

  1. Assurez-vous que import ImageIO
  2. Ajouter une propriété private var stillImageOutput: AVCaptureStillImageOutput!
  3. Instancier stillImageOutput avant captureSession.startRunning():

Comme ça:

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

Puis utilisez ce code pour capturer une image:

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
    }
}

Tout cela suppose que vous ayez déjà une configuration AVCaptureSession et que vous ayez juste besoin de prendre une photo, comme moi.

0
RyJ