使用AVCaptureSession拍照、摄像、载图

2014-08-11 Xiaosong Gao 更多博文 » 博客 » GitHub »

iOS

原文链接 https://gaoxiaosong.github.io/2014/08/11/custom-avcapturesession.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


概要示意图:

详细示意图:

1、建立Session

1.1 声明Session

AVCaptureSession *session = [[AVCaptureSession alloc] init];
// Add inputs and outputs.
[session startRunning];

1.2 设置采集的质量

png1

if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
  session.sessionPreset = AVCaptureSessionPreset1280x720;
}
else {
  // Handle the failure.
}

1.3 重新设置Session

[session beginConfiguration];
// Remove an existing capture device.
// Add a new capture device.
// Reset the preset.
[session commitConfiguration];

2、添加Input

2.1 配置一个Device(查找前后摄像头)

NSArray *devices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in devices) {
  NSLog(@"Device name: %@", [device localizedName]);
  if ([device hasMediaType:AVMediaTypeVideo]) {
    if ([device position] == AVCaptureDevicePositionBack) {
      NSLog(@"Device position : back");
    }
    else {
      NSLog(@"Device position : front");
    }
  }
}

2.2 设备的前后切换

AVCaptureSession *session = <#A capture session#>;
[session beginConfiguration];
[session removeInput:frontFacingCameraDeviceInput];
[session addInput:backFacingCameraDeviceInput];
[session commitConfiguration];

2.3 添加输入设备到当前Session

NSError *error;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
  // Handle the error appropriately.
}
AVCaptureSession *captureSession = <#Get a capture session#>;
AVCaptureDeviceInput *captureDeviceInput = <#Get a capture device input#>;
if ([captureSession canAddInput:captureDeviceInput]) {
  [captureSession addInput:captureDeviceInput];
}
else {
  // Handle the failure.
}

3、添加输出设备到Session

  • AVCaptureMovieFileOutput:输出一个 视频文件。
  • AVCaptureVideoDataOutput:可以采集数据从指定的视频中。
  • AVCaptureAudioDataOutput:采集音频。
  • AVCaptureStillImageOutput:采集静态图片。

3.1 添加一个Output到Session

AVCaptureSession *captureSession = <#Get a capture session#>;
AVCaptureMovieFileOutput *movieOutput = <#Create and configure a movie output#>;
if ([captureSession canAddOutput:movieOutput]) {
  [captureSession addOutput:movieOutput];
}
else {
  // Handle the failure.
}

3.2 保存视频到文件

3.2.1 声明一个输出

AVCaptureMovieFileOutput *aMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
CMTime maxDuration = <#Create a CMTime to represent the maximum duration#>;
aMovieFileOutput.maxRecordedDuration = maxDuration;
aMovieFileOutput.minFreeDiskSpaceLimit = <#An appropriate minimum given the quality of the movie format and the duration#>;

3.2.2 配置写到指定的文件

AVCaptureMovieFileOutput *aMovieFileOutput = <#Get a movie file output#>;
NSURL *fileURL = <#A file URL that identifies the output location#>;
[aMovieFileOutput startRecordingToOutputFileURL:fileURL recordingDelegate:<#The delegate#>];

3.2.3 确定文件是否写成功

captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error: 实现这个方法

(void)captureOutput:(AVCaptureFileOutput *)captureOutput
    didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
    fromConnections:(NSArray *)connections
    error:(NSError *)error {
  BOOL recordedSuccessfully = YES;
  if ([error code] != noErr) {
    // A problem occurred: Find out if the recording was successful.
    id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
    if (value) {
      recordedSuccessfully = [value boolValue];
    }
  }
// Continue as appropriate...

3.3 对采集载图

3.3.1 设置采集图片的像素格式

说实话下面这段的像素格式我也似懂非懂, 感觉是不是像素的对像素的质量会有一些影响

You can use the videoSettings property to specify a custom output format. The video settings property is a dictionary; currently, the only supported key is kCVPixelBufferPixelFormatTypeKey. The recommended pixel format choices for iPhone 4 are kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange or kCVPixelFormatType_32BGRA; for iPhone 3G the recommended pixel format choices are kCVPixelFormatType_422YpCbCr8 or kCVPixelFormatType_32BGRA. Both Core Graphics and OpenGL work well with the BGRA format:

// Create a VideoDataOutput and add it to the session
AVCaptureVideoDataOutput *output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
[session addOutput:output];
// Configure your output.
dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
[output setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);
// Specify the pixel format
output.videoSettings = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey];

3.3.2 采集静态图片

AVCaptureStillImageOutput这个类可以采集静态图片。

png2

Pixel and Encoding Formats

Different devices support different image formats:

png3

可以自己指定想要捕捉的格式,下面就可以指定捕捉一个JPEG的图片:

AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = @{ AVVideoCodecKey : AVVideoCodecJPEG};
[stillImageOutput setOutputSettings:outputSettings];

如果使用JPEG图片格式,就不应该再指定其它的压缩了,output会自动压缩,这个压缩会使用硬件加速。而我们要使用这个图片数据时。可以使用jpegStillImageNSDataRepresentation:这个方法来获取相应的NSData,这个方法不会做重复压缩的动作。

4、捕捉图片

当想捕捉图片的时候,给output发送一个captureStillImageAsynchronouslyFromConnection:completionHandler:消息。第一个参数是捕捉需要用到的连接,需要查找哪一个input设备的端口是收集视频的:

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

第二个参数是一个block,返回的参数中imageSampleBuffer是一个CMSampleBuffer,包含了图像的数据。imageSampleBuffer中可能含有metadata,例如Exif dictionary,或者一个附件。

[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:
^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
  CFDictionaryRef exifAttachments =
  CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
  if (exifAttachments) {
    // Do something with the attachments.
  }
  // Continue as appropriate.
}];

5、为用户显示当前的录制状态

5.1 录制预览

AVCaptureSession *captureSession = <#Get a capture session#>;
CALayer *viewLayer = <#Get a layer from the view in which you want to present the preview#>;
AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
[viewLayer addSublayer:captureVideoPreviewLayer];

Video Gravity Modes

The preview layer supports three gravity modes that you set using videoGravity:

  • AVLayerVideoGravityResizeAspect: This preserves the aspect ratio, leaving black bars where the video does not fill the available screen area.
  • AVLayerVideoGravityResizeAspectFill: This preserves the aspect ratio, but fills the available screen area, cropping the video when necessary.
  • AVLayerVideoGravityResize: This simply stretches the video to fill the available screen area, even if doing so distorts the image.

6、结束捕捉

- (void)stopVideoCapture:(id)arg
{
  // 停止摄像头捕抓
  if (self->avCaptureSession) {
    [self->avCaptureSession stopRunning];
    self->avCaptureSession = nil;
    [labelStatesetText:@"Video capture stopped"];
  }
}