web-dev-qa-db-ja.com

iPhone SDK 4 AVFoundation-captureStillImageAsynchronouslyFromConnectionを正しく使用する方法は?

IPhoneで静止画を撮るのに新しいAVFoundation frameworkを使おうとしています。

ボタンを押すと、この方法が呼び出されます。シャッター音は聞こえますが、ログ出力が見えません。このメソッドを数回呼び出すと、カメラのプレビューがフリーズします。

captureStillImageAsynchronouslyFromConnectionの使い方に関するチュートリアルはありますか?

[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:
                [[self stillImageOutput].connections objectAtIndex:0]
                     completionHandler:^(CMSampleBufferRef imageDataSampleBuffer,
                            NSError *error) {
                                              NSLog(@"inside");
                            }];
-(void)initCapture {
 AVCaptureDeviceInput * captionInput = [AVCaptureDeviceInput 
 deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] 
 error:nil]; 
 [。 
 
 dispatch_queue_t queue; 
 queue = dispatch_queue_create( "cameraQueue"、NULL); 
 [captureOutput setSampleBufferDelegate:self queue:queue]; 
 dispatch_release (キュー); 
 
 NSString * key =(NSString *)kCVPixelBufferPixelFormatTypeKey; 
 NSNumber *値= [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 = AVLayerVideoGravityResizeAspect 
 
 [self.view.layer addSublayer:self.prevLayer]; 
 
 
 //デフォルトのファイル出力を設定します
 AVCaptureStillImageOutput * _stillImageOutput = [[[AVCaptureStillImageOutput alloc] init] autorelease]; 
 NSDictionary * outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
 AVVide oCodecJPEG、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]; 
 
} 
21
dan

4.0がまだベータ版であったときに、この問題が発生しました。私はかなりのことを試しました。ここに行きます:

  • AVCaptureStillImageOutputとAVCaptureVideoDataOutputは、互いにうまく機能していないようです。ビデオ出力が実行されている場合、画像出力は完了していないように見えます(電話をスリープ状態にしてセッションを一時停止するまで、単一の画像が出力されるように見えます)。
  • AVCaptureStillImageOutputは、AVCaptureSessionPresetPhotoでのみ適切に機能するようです。そうしないと、JPEGでエンコードされたビデオフレームを効果的に取得できます。高品質のBGRAフレームを使用することもできます(ちなみに、カメラのネイティブ出力はBGRAのように見えますが、2vuy/420vのカラーサブサンプリングはないようです)。
  • ビデオ(写真以外のすべて)と写真のプリセットは根本的に異なっているように見えます。セッションが写真モードの場合、ビデオフレームを取得することはありません(エラーも発生しません)。多分彼らはこれを変えました...
  • 2つのキャプチャセッション(1つはビデオプリセットとビデオ出力、もう1つは写真プリセットと画像出力)を持つことはできないようです。彼らはこれを修正したかもしれません。
  • セッションを停止し、プリセットを写真に変更し、セッションを開始して写真を撮り、写真が完成したら、停止してプリセットを元に戻し、再開することができます。これにはしばらく時間がかかり、ビデオプレビューレイヤーが停止してひどいように見えます(露出レベルを再調整します)。これもベータ版で時折デッドロックしました(-stopRunningを呼び出した後、session.runningはまだYESでした)。
  • AVCaptureConnectionを無効にできる場合があります(動作するのは想定です)。このデッドロックを覚えています。彼らはこれを修正したかもしれません。

結局、ビデオフレームをキャプチャするだけでした。 「写真を撮る」ボタンは単にフラグを設定します。ビデオフレームコールバックでは、フラグが設定されている場合、UIImage *の代わりにビデオフレームを返します。これは私たちの画像処理のニーズには十分でした—「写真を撮る」は主に存在するので、ユーザーは否定的な応答(およびバグレポートを送信するオプション)を得ることができます。 2/3/5メガピクセルの画像は処理に時間がかかるため、実際には必要ありません。

ビデオフレームが十分でない場合(つまり、高解像度の画像キャプチャの間にファインダーフレームをキャプチャしたい場合)、両方のプリセットを設定できる唯一の方法であるため、最初に複数のAVCaptureセッションを使用してそれらが修正されたかどうかを確認します。

おそらくバグを提出する価値があります。 4.0GMのリリースに関するバグを報告しました。 Appleいくつかのサンプルコードを求められましたが、それまでにビデオフレームの回避策を使用することにし、リリースするリリースがありました。

さらに、「低」プリセットはvery低解像度です(結果として低解像度、低フレームレートのビデオプレビューになります)。可能な場合は640x480を選択し、使用できない場合はMediumにフォールバックします。

16
tc.

たくさんの試行錯誤の末、私はこれを行う方法を考え出しました。

ヒント:Appleの公式ドキュメントは-単に-間違っています。彼らがあなたに与えるコードは実際には機能しません。

私はそれをステップバイステップの説明でここに書きました:

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

リンクにはたくさんのコードがありますが、要約すると:

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

これは大きな助けになりました-私はAVCamの例に従おうとしてかなり長い間雑草に閉じ込められました。

これは、何が起こっているのかを説明する私のコメントを含む完全な作業プロジェクトです。これは、複数の出力でキャプチャマネージャを使用する方法を示しています。この例では、2つの出力があります。

1つ目は、上記の例の静止画像出力です。

2つ目は、カメラから出力されるビデオへのフレームごとのアクセスを提供します。必要に応じて、フレームで何か面白いことをするためのコードを追加できます。この例では、デリゲートコールバック内から画面上のフレームカウンターを更新しています。

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

6
Tereus Scott

Appleには、これに関するいくつかのメモとサンプルコードがあります。

テクニカルQ&A QA1702:AV Foundationを使用してカメラからビデオフレームを画像としてキャプチャする方法

3
Michael Grinich

Adamの答えを使用する必要がありますが、Swift(最近のほとんどの人がおそらくそうしているように)を使用する場合は、ここにSwift =彼のコードの1.2ポート:

  1. 必ず_import ImageIO_
  2. プロパティを追加します_private var stillImageOutput: AVCaptureStillImageOutput!_
  3. captureSession.startRunning()の前にstillImageOutputをインスタンス化します:

このような:

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

次に、このコードを使用して画像をキャプチャします。

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

これはすべて、すでにAVCaptureSessionセットアップがあり、私と同じように、そこから静止画を取得する必要があることを前提としています。

0
RyJ