2010-10-02 16 views
21

Estoy tratando de usar el nuevo AVFoundation framework para tomar fotos con el iPhone.iPhone SDK 4 AVFoundation - Cómo utilizar captureStillImageAsynchronouslyFromConnection correctamente?

Con un botón presione este se llama methos. Puedo escuchar el sonido del obturador pero no puedo ver la salida del registro. Si llamo a este método varias veces, la vista previa de la cámara se congelará.

¿Hay algún tutorial sobre cómo usar captureStillImageAsynchronouslyFromConnection?

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

No sé si esto fue cierto en 2010, pero a finales de 2011, he estado utilizando fácilmente 'captureStillImageAsynchronouslyFromConnection' en el al mismo tiempo que recibe una transmisión de video usando el delegado de AVCaptureVideoDataOutput, 'captureOutput' y mientras usa una previewLayer. Obtengo una imagen de 5 megapíxeles de stillImage, una 852x640 para la alimentación de video y la previewLayer. – mahboudz

+0

El comentario anterior se basó en un iPhone4. En un iPhone 4s, obtengo un 8 megapíxeles todavía, mientras obtengo 852x640 para el video feed y previewLayer. – mahboudz

+0

¿Cuál es la diferencia entre initWithSession y layerWithSession? Los documentos no discuten cuándo usar uno frente al otro. http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVCaptureVideoPreviewLayer_Class/Reference/Reference.html – knite

Respuesta

16

Hemos tenido este problema cuando todavía estaba en 4.0 beta. Intenté un buen montón de cosas. Aquí va:

  • AVCaptureStillImageOutput y AVCaptureVideoDataOutput no parecen funcionar bien entre sí. Si la salida de video se está ejecutando, la salida de la imagen nunca parece completarse (hasta que pause la sesión poniendo el teléfono en modo de suspensión, entonces parece que sale una sola imagen).
  • AVCaptureStillImageOutput solo parece funcionar de manera sensata con AVCaptureSessionPresetPhoto; de lo contrario, obtendrá marcos de video con codificación JPEG. También podría usar marcos BGRA de mayor calidad (por cierto, la salida nativa de la cámara parece ser BGRA, no parece tener el submuestreo de color de 2vuy/420v).
  • El video (todo lo que no es foto) y los ajustes preestablecidos de fotos parecen fundamentalmente diferentes; nunca obtienes ningún cuadro de video si la sesión está en modo de fotografía (tampoco obtienes ningún error). Tal vez cambiaron esto ...
  • Parece que no tiene dos sesiones de captura (una con un preajuste de video y una salida de video, una con preajuste de foto y una salida de imagen). Podrían haber arreglado esto.
  • Puede detener la sesión, cambiar el preajuste a foto, iniciar la sesión, tomar la foto y, cuando la foto se complete, detener, cambiar el preajuste y comenzar de nuevo. Esto demora un poco y la capa de vista previa de video se detiene y se ve terrible (vuelve a ajustar los niveles de exposición). Esto también de vez en cuando se estanca en la versión beta (después de llamar a "stopRunning, session.running" todavía era SÍ).
  • Es posible que pueda deshabilitar el AVCaptureConnection (es supuesta para que funcione). Recuerdo este bloqueo; ellos pueden haber arreglado esto.

Terminé simplemente capturando fotogramas de video. El botón "tomar foto" simplemente establece un indicador; en la devolución de llamada del marco de video, si se establece el indicador, devuelve el cuadro de video en lugar de un UIImage *. Esto fue suficiente para nuestras necesidades de procesamiento de imágenes — "tomar foto" existe en gran medida para que el usuario pueda obtener una respuesta negativa (y una opción para enviar un informe de error); en realidad, no queremos imágenes de 2/3/5 megapíxeles, ya que tardan años en procesarse.

Si los marcos de video no son lo suficientemente buenos (es decir, desea capturar marcos de visor entre capturas de imagen de alta resolución), primero vería si se han solucionado utilizando varias sesiones de AVCapture, ya que es la única forma de configurarlo ambos presets.

Probablemente valga la pena presentar un error. Archivé un error durante el lanzamiento de 4.0 GM; Apple me pidió un código de muestra, pero para entonces había decidido utilizar la solución del cuadro de video y tenía un lanzamiento para lanzar.

Además, el valor preestablecido "bajo" es muy de baja resolución (y da como resultado una vista previa de video de baja resolución y baja tasa de fotogramas). Me gustaría ir por 640x480 si está disponible, volviendo a Medium si no.

+1

corrige perfectamente que AVCaptureStillImageOutput solo funciona correctamente con AVCaptureSessionPresetPhoto –

61

Después de mucho ensayo y error, he trabajado la manera de hacer esto.

Sugerencia: Los documentos oficiales de Apple son, simplemente, incorrectos. El código que te dan en realidad no funciona.

lo escribí aquí con instrucciones paso a paso:

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

porciones de código en el enlace, pero en resumen:

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

No puedo hacer +1 suficiente. ¡Gracias por compartir esto! –

+0

Tscott (en el comentario a continuación) tiene un ejemplo de trabajo del código de Adam. Lo descargué e instalé desde Github e inmediatamente no funcionó en mi iPad. Sin embargo, después de revisar los archivos xib, vi que tenía los enchufes de archivos xib de iPhone conectados al código, pero que el iPad xib estaba en blanco. Después de agregar la vista de la imagen, las etiquetas, los botones y otros objetos al archivo iPad xib, pude obtener el código para ejecutar. –

+0

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

6

Esta ha sido una gran ayuda - Estuve atrapado en las malas hierbas durante bastante tiempo tratando de seguir el ejemplo AVCam.

Aquí hay un proyecto de trabajo completo con mis comentarios que explican lo que está sucediendo. Esto ilustra cómo puede usar el administrador de captura con múltiples salidas. En este ejemplo, hay dos salidas.

La primera es la salida de la imagen fija del ejemplo anterior.

El segundo proporciona el acceso fotograma por fotograma al video que sale de la cámara. Puede agregar más código para hacer algo interesante con los marcos si lo desea. En este ejemplo, estoy actualizando un contador de cuadros en la pantalla desde la devolución de llamada del delegado.

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

+0

Funcionó muy bien. Solo una cabeza hasta que alguien descargue este código de Gitub: el archivo xib del iPad está en blanco. Debe copiar los objetos xib del iPhone y copiarlos/pegarlos en los archivos xib del iPad. A continuación, conecte los objetos a sus respectivos puntos de venta –

+0

No funciona para mí. No en iphone 5S con beta8, no en ipad con IOS 7.1.2. –

0

Debe utilizar Adam respuesta 's, pero si se utiliza Swift (como la mayoría de ustedes probablemente lo hacen hoy en día), esto es un puerto Swift 1.2 de su código:

  1. Hacer seguro que import ImageIO
  2. añadir un alojamiento private var stillImageOutput: AVCaptureStillImageOutput!
  3. Instantiate stillImageOutput antes captureSession.startRunning():

De esta manera:

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

A continuación, utiliza este código para capturar una imagen:

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

Todo esto asume que ya tiene una configuración AVCaptureSession y sólo tiene que tomar una imagen fija de ella , al igual que I.

Cuestiones relacionadas