CMSampleBufferGetImageBuffer()始终在我的AVCaptureStillImageOutput捕获回调块上返回nil

时间:2019-04-30 05:18:30

标签: objective-c macos avfoundation video-capture core-image

我正在为MacOS(最新事物,Obj-C)编写一个显微镜应用程序。 我使用AVCaptureSession从屏幕上的显微镜摄像机预览实时视频,但是我也想让用户在看到有趣的东西时拍摄静止图像。我希望将最高质量的图像保存到磁盘上的文件中。

为此,我尝试使用AVCaptureStillImageOutput,它是标题所需要的。我进行了设置,并像下面这样将其添加到我的捕获会话中:

self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];    
NSDictionary *compressionSettings = @{AVVideoQualityKey : @(1.0)};
NSDictionary *outputSettings = @{ AVVideoCodecKey : AVVideoCodecJPEG,
                                  AVVideoCompressionPropertiesKey : compressionSettings,
                                  AVVideoWidthKey : @(1024),
                                  AVVideoHeightKey : @(768)
                                  };
[self.stillImageOutput setOutputSettings:outputSettings];    
[self.session addOutput: stillImageOutput];

稍后,随着用户选择拍摄快照-我调用此方法尝试捕获静止图像:

// Capture a single video frame during a video capture
-(BOOL)captureStillImage: (CGRect)cropRect {
    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; }
    }
    if (videoConnection == nil) { return NO };

    [stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:
     ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {

         CVImageBufferRef imageBufferRef = CMSampleBufferGetImageBuffer(imageSampleBuffer); 
         if(imageBufferRef==nil) // <<< HERE I ALWAYS GET NULL
             return;

         NSCIImageRep *imageRep = [NSCIImageRep imageRepWithCIImage:[CIImage imageWithCVImageBuffer:imageBufferRef]];

         NSImage *compositeImage = [[NSImage alloc] initWithSize:[imageRep size]];
         NSImage *videoFrameImage = [[NSImage alloc] initWithSize:[imageRep size]];
         [videoFrameImage addRepresentation:imageRep];
         NSRect r = NSMakeRect(0.0f, 0.0f, [imageRep size].width, [imageRep size].height);
         [compositeImage lockFocus];
         [videoFrameImage drawAtPoint:NSMakePoint(0.0f, 0.0f) fromRect:r operation:NSCompositeSourceOver fraction:1.0f];
         [compositeImage unlockFocus];

         NSData *tiffImageData = [videoFrameImage TIFFRepresentation];
         NSBitmapImageRep *tiffRep = [NSBitmapImageRep imageRepWithData:tiffImageData];
         NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor];
         tiffImageData = [tiffRep representationUsingType:NSJPEGFileType properties:imageProps];

         // Save data to file
         static NSUInteger imageNumber = 0;
         NSString *imageFilePath = [@"/Users/me/Desktop/" stringByAppendingPathComponent: [NSString stringWithFormat:@"image_%lu", imageNumber++]];
         imageFilePath = [imageFilePath stringByAppendingPathExtension:@"JPG"];
         [tiffImageData writeToFile: imageFilePath atomically: NO];
    }];

    return YES;
}

我发现其他用户也有类似的问题-但是答案始终涉及“您没有应用我所做的“ OutputSettings”-所以我真的很困惑”。

请注意:如果我使用“简写” API:

     NSData *pixelData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];

在我的回调中-成功,但是pixelData包含较差的,低分辨率的小图像-当然不是我在预览中看到的质量。此外-它是JPEG压缩的-因此,如果要裁剪,则需要先解码,然后裁剪,然后重新压缩-从而进一步降低本来就很低的质量。这就是为什么我尝试使用AVCaptureStillImageOutput对象的原因。

任何人都可以阐明这一点,或将我定向到正确的文档上吗?我已经花了太多时间,而且涉及的过多框架(CoreMedia,CoreVideo,CoreImage,AVFoundation)确实没有得到很好的记录,或者没有任何可以掌握的合理架构。哎呀,我想念QuickTime组件和旧的APIS。

请帮助?有人吗?

1 个答案:

答案 0 :(得分:0)

CMSampleBufferGetImageBuffer返回nil,因为您通过使用AVVideoCodecJPEG请求了JPEG压缩的图像。样本缓冲区将不包含未压缩的原始图像缓冲区,而将包含单个包含jpeg数据的块缓冲区。

您不想在字典中使用AVVideoCodecKey,AVVideoCompressionPropertiesKey等,而是想指定CVPixelFormatType和其他设置,以便它返回未压缩的帧。请参阅AVVideoSettings.h标头顶部的讨论。