相信很多人都用过拍照功能,通常情况下我们使用 Picker 就可以实现简单地拍照,但是系统提供的简易相机功能真的太少,需要一些高级功能时往往显得十分无能。

概念解析

首先,我们需要引入 AVFoundation.framework 以便使用相关的类和方法。

输入设备

  • 我们使用 AVCaptureDevice 实例来得到输入设备,例如相机或麦克风。

  • 我们使用 AVCaptureDeviceInput 实例来配置输入设备。

    • 一个 AVCaptureDeviceInput 实例拥有多个输入端口,也就是 AVCaptureInputPort 实例。

输出设备

  • 我们使用 AVCaptureOutput 来管理输出内容,允许输出的有视频和静止图像。

    • 一个 AVCaptureOutput 可以接收来自一个或多个输入源的数据。

    • 输入设备分别有:

      • AVCaptureMovieFileOutput: 输出到文件。

      • AVCaptureVideoDataOutput: 可用于处理被捕获的视频帧。

      • AVCaptureAudioDataOutput: 可用于处理被捕获的音频数据。

      • AVCaptureStillImageOutput: 可用于捕获带有 MetaData 的静止图像。

      • 本文主要介绍 AVCaptureMovieFileOutputAVCaptureStillImageOutput

组织数据

  • 我们通过 AVCaptureSession 实例来协调和组织数据在输入与输出设备间的流动和传递。

    • 一个 AVCaptureSession 实例可以协调多个输入及输出设备。

    • 使用 AVCaptureSession 实例的 addInput 方法可以增加输入设备

    • 使用 AVCaptureSession 实例的 addOutput 方法可以增加输出设备

  • AVCaptureOutputAVCaptureInput 间存在 AVCaptureConnection 对象。

  • 当一个 AVCaptureOutputAVCaptureInput 加入 AVCaptureSession 中,会自动创建连接 (AVCaptureConnection),通常不需要手动管理。

这是以上内容的示例图:

inoutput

将该图进一步细化:

inoutputMore

(图片来自网络)

基本操作

视频录制

要进行视频录制,输出设备应为 AVCaptureMovieFileOutput,先启动 AVCaptureSession,然后使用它的子类 AVCaptureFileOutput 的方法 - (void)startRecordingToOutputFileURL:(NSURL *)outputFileURL recordingDelegate:(id < AVCaptureFileOutputRecordingDelegate >)delegate- (void)stopRecording 来分别启动和停止编码输出,整体步骤是:

  • 创建输入输出设备:
AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil];
AVCaptureDeviceInput *microphone = [AVCaptureDeviceInput deviceInputWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio] error:nil];
captureOutput = [[AVCaptureMovieFileOutput alloc] init];
  • 创建 AVCaptureSession 实例:
self.captureSession = [[AVCaptureSession alloc] init];
  • 添加输入输出设备:
[self.captureSession addInput:captureInput];
[self.captureSession addInput:microphone];
[self.captureSession addOutput:self.captureOutput];
  • 配置 AVCaptureSession 属性(此处使用中等质量):
[self.captureSession setSessionPreset:AVCaptureSessionPresetMedium]
类型质量说明
AVCaptureSessionPresetHigh最高质量,设备间有差异
AVCaptureSessionPresetMedium适合 Wi-Fi 分享,实际值可能有变
AVCaptureSessionPresetLow适合 3G 分享,实际值可能有变
AVCaptureSessionPreset640x480640x480VGA
AVCaptureSessionPreset1280x7201280x720720p HD
AVCaptureSessionPresetPhoto照片全分辨率照片,此项不支持视频输出
  • 构建方法用于启动编码(视频编码: H.264,音频编码: AAC):
- (void)startRecording {
	[captureOutput startRecordingToOutputFileURL:[self tempFileURL] recordingDelegate:self];
				}
  • 调用方法上述启动编码:
[self performSelector:@selector(startRecording) withObject:nil afterDelay:10.0];
[self.captureSession startRunning];
  • 遵守 AVCaptureFileOutputRecordingDelegate 协议:
<AVCaptureFileOutputRecordingDelegate>
  • 处理编码:
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections {
    NSLog(@"start record video");
}

- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error {
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock:^(NSURL *assetURL, NSError *error) {
		if (error) {
			NSLog(@"Error");
		} else {
			NSLog(@"%@", [assetURL path]);
		}
	}];
}
  • 停止编码:
[self.captureSession stopRecording];

拍摄照片

要进行照片拍摄,输出设备应为 AVCaptureStillImageOutput

  • 创建设备:
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
  • 配置输出参数:
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: AVVideoCodecJPEG, AVVideoCodecKey, nil];

[stillImageOutput setOutputSettings:outputSettings];
  • 捕获图像:

    • 获得链接与端口
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;
	}
}
* 处理图像
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:stillImageConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
	ALAssetsLibraryWriteImageCompletionBlock completionBlock = ^(NSURL *assetURL, NSError *error) {
     if (error) {
		}
   };

   if (imageDataSampleBuffer != NULL) {
    NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    UIImage *image = [[UIImage alloc] initWithData:imageData];
    [library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation]
    completionBlock:completionBlock];
   } else {
		completionBlock(nil, error);
	}

   if ([[self delegate] respondsToSelector:@selector(captureManagerStillImageCaptured:)]) {
		[[self delegate] captureManagerStillImageCaptured:self];
   }
}];