iphone - framework - swift ios documentation
iPhone SDK 4 AVFoundation-¿Cómo utilizar captureStillImageAsynchronouslyFromConnection correctamente? (5)
Apple tiene algunas notas y código de ejemplo en esto:
Estoy tratando de usar el nuevo AVFoundation framework
para tomar fotografías con el iPhone.
Con un botón pulsar este methos se llama. 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á.
¿Existe 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]; }
Debería usar la respuesta de Adam , pero si usa Swift (como la mayoría de ustedes probablemente hoy en día), aquí hay un puerto Swift 1.2 de su código:
- Asegúrate de
import ImageIO
- Agregue una propiedad
private var stillImageOutput: AVCaptureStillImageOutput!
- Cree
stillImageOutput
instancia destillImageOutput
antes decaptureSession.startRunning()
:
Me gusta esto:
stillImageOutput = AVCaptureStillImageOutput()
stillImageOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
captureSession.addOutput(stillImageOutput)
Luego usa 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 supone que ya tienes una configuración de AVCaptureSession
y solo necesitas quitarle una imagen fija, al igual que yo.
Después de muchas pruebas y errores, resolví cómo hacer esto.
Sugerencia: los documentos oficiales de Apple están, simplemente, equivocados. El código que te dan no funciona realmente.
Lo escribí aquí con instrucciones paso a paso:
Un montón 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;
}];
}
Esto ha sido de gran ayuda; estuve atrapado en la maleza durante bastante tiempo intentando seguir el ejemplo de 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 imagen fija del ejemplo anterior.
El segundo proporciona acceso fotograma a 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 acabo de actualizar un contador de cuadros en la pantalla desde la devolución de llamada de delegado.
Tuvimos este problema cuando 4.0 aún estaba en beta. Intenté un buen montón de cosas. Aquí va:
- AVCaptureStillImageOutput y AVCaptureVideoDataOutput no parecen jugar bien entre sí. Si la salida de video se está ejecutando, la salida de la imagen parece no completarse (hasta que se detiene la sesión poniendo el teléfono en suspensión; entonces parece que se saca una sola imagen).
- AVCaptureStillImageOutput solo parece funcionar de manera sensata con AVCaptureSessionPresetPhoto; De lo contrario, efectivamente obtendrás fotogramas codificados en JPEG. También podría utilizar marcos BGRA de mayor calidad (por cierto, la salida nativa de la cámara parece ser BGRA; no parece tener el submuestreo en color de 2vuy / 420v).
- El video (todo lo que no es Photo) y los ajustes preestablecidos de fotos parecen fundamentalmente diferentes; nunca se obtiene ningún fotograma de video si la sesión está en modo foto (tampoco recibe un error). Tal vez hayan cambiado esto ...
- Parece que no puede tener dos sesiones de captura (una con un ajuste preestablecido de video y una salida de video, una con ajuste preestablecido de fotos y una salida de imagen). Podrían haber arreglado esto.
- Puede detener la sesión, cambiar el ajuste preestablecido a foto, iniciar la sesión, tomar la foto, y cuando la foto se complete, detener, volver a cambiar el ajuste preestablecido y comenzar de nuevo. Esto toma un tiempo y la capa de vista previa del video se detiene y se ve terrible (reajusta los niveles de exposición). Esto también se bloquea en ocasiones en la versión beta (después de llamar -stopRunning, session.running aún era SÍ).
- Es posible que pueda deshabilitar AVCaptureConnection (se supone que funciona). Recuerdo este estancamiento; pueden haber arreglado esto
Terminé simplemente capturando cuadros de video. El botón "tomar foto" simplemente establece una bandera; en la devolución de llamada del cuadro de video, si la marca está activada, devuelve el cuadro de video en lugar de un UIImage *. Esto fue suficiente para nuestras necesidades de procesamiento de imágenes: la "toma de fotografías" 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 fotogramas de video no son lo suficientemente buenos (es decir, si desea capturar fotogramas del visor entre capturas de imágenes de alta resolución), primero veré si se han arreglado utilizando varias sesiones de captura de AV, ya que esa es la única forma en que puede configurar ambos ajustes preestablecidos.
Probablemente vale la pena presentar un error. Presenté un error en torno al lanzamiento de 4.0 GM; Apple me pidió un código de ejemplo, pero para entonces decidí usar la solución de marco de video y tenía un lanzamiento que liberar.
Además, el valor predeterminado "bajo" es de muy baja resolución (y da como resultado una vista previa de video de baja resolución y baja velocidad de cuadros). Me gustaría 640x480 si estuviera disponible, volviendo a Medio si no.