iPhone SDK 4 Avfoundation - Como usar o CapturestillImageAsynchronsly na concração corretamente?

StackOverflow https://stackoverflow.com/questions/3847140

  •  27-09-2019
  •  | 
  •  

Pergunta

Estou tentando usar o novo AVFoundation framework Para tirar fotos imóveis com o iPhone.

Com um botão, pressione este methos é chamado. Eu posso ouvir o som do obturador, mas não consigo ver a saída do log. Se eu chamar esse método várias vezes, a visualização da câmera congelará.

Existe algum tutorial por aí como 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];

}
Foi útil?

Solução

Tivemos esse problema quando o 4.0 ainda estava na versão beta. Eu tentei um monte de coisas. Aqui vai:

  • AvcapturestillImageOutput e AvcaptureVideodataOutput não parecem jogar bem um com o outro. Se a saída de vídeo estiver em execução, a saída da imagem nunca parecerá concluir (até você pausar a sessão colocando o telefone para dormir; então você parece obter uma única imagem).
  • AvcapturestillImageOutput parece funcionar de maneira sensata com o AVCAPTUSESSESSESSPRESETPHOTO; Caso contrário, você efetivamente obtém quadros de vídeo codificados por JPEG. Pode muito bem usar quadros Bgra de alta qualidade (aliás, a saída nativa da câmera parece ser Bgra; não parece ter a subamostragem de cores de 2VUY/420V).
  • O vídeo (tudo o que não é foto) e as predefinições de fotos parecem fundamentalmente diferentes; Você nunca recebe quadros de vídeo se a sessão estiver no modo de foto (você também não recebe um erro). Talvez eles tenham mudado isso ...
  • Você não consegue ter duas sessões de captura (uma com uma predefinição de vídeo e uma saída de vídeo, uma com predefinição de foto e uma saída de imagem). Eles podem ter corrigido isso.
  • Você pode parar a sessão, alterar a predefinição para foto, iniciar a sessão, tirar a foto e, quando a foto terminar, parar, alterar a predefinição e começar novamente. Isso leva um tempo e a camada de visualização de vídeo para e parece terrível (reajusta os níveis de exposição). Ocasionalmente, isso ocasionalmente entrava na versão beta (depois de chamar -stoprunning, session.running ainda era sim).
  • Você pode desativar a AvcaptureConnection (é suposto trabalhar). Lembro -me desse deadlocking; Eles podem ter corrigido isso.

Acabei apenas capturando quadros de vídeo. O botão "Tiro foto" simplesmente define um sinalizador; No retorno de chamada do quadro de vídeo, se o sinalizador estiver definido, ele retornará o quadro de vídeo em vez de um UIImage*. Isso foi suficiente para nossas necessidades de processamento de imagens-"tire foto" existe em grande parte para que o usuário possa obter uma resposta negativa (e uma opção para enviar um relatório de bug); Na verdade, não queremos imagens de 2/3/5 megapixels, pois elas levam séculos para processar.

Se os quadros de vídeo não forem bons o suficiente (ou seja, você deseja capturar quadros de visor entre capturas de imagens de alta resolução), primeiro veria se eles consertaram o uso de várias sessões de Avcapure, já que essa é a única maneira de definir as duas predefinições.

Provavelmente vale a pena arquivar um bug. Eu arquivei um bug no lançamento de 4,0 g; A Apple me pediu algum código de amostra, mas então eu decidi usar a solução alternativa do quadro de vídeo e tivesse um lançamento para ser lançado.

Além disso, a predefinição "baixa" é muito RES de baixa resolução (e resulta em uma visualização de vídeo de baixa resolução e baixa quadros). Eu iria para 640x480, se disponível, voltando para médio, se não.

Outras dicas

Depois de muitas tentativas e erros, elaborei como fazer isso.

Dica: os documentos oficiais da Apple estão - simplesmente - errados. O código que eles fornecem não funciona.

Eu escrevi aqui com instruções passo a passo:

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

Muito código no link, mas em resumo:

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

Isso tem sido uma grande ajuda - fiquei preso nas ervas daninhas por um bom tempo tentando seguir o exemplo do AVCAM.

Aqui está um projeto de trabalho completo com meus comentários que explicam o que está acontecendo. Isso ilustra como você pode usar o gerenciador de captura com várias saídas. Neste exemplo, existem duas saídas.

O primeiro é a saída de imagem estática do exemplo acima.

O segundo fornece acesso ao quadro a quadro ao vídeo que sai da câmera. Você pode adicionar mais código para fazer algo interessante com os quadros, se quiser. Neste exemplo, estou apenas atualizando um balcão de quadros na tela de dentro do retorno de chamada delegado.

https://github.com/tdsltm/iphonestubs/tree/master/Videocamcapturexample-redglassesblog/VideocamCaptureExample-redglasseSsesblog

Você deveria usar AdãoResposta, mas se você usa Swift (como a maioria de vocês provavelmente faz hoje em dia), aqui está uma porta Swift 1.2 do código dele:

  1. Assegure-se de que você import ImageIO
  2. Adicione uma propriedade private var stillImageOutput: AVCaptureStillImageOutput!
  3. Instanciado stillImageOutput antes da captureSession.startRunning():

Assim:

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

Em seguida, use este código para capturar uma imagem:

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

Tudo isso assume que você já tem um AVCaptureSession configurar e só preciso tirar um imóvel, assim como eu.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top