iOS 三种录制视频方式

UIImagePickerController缓存

目前,将视频捕获集成到你的应用中的最简单的方法是使用 UIImagePickerController。这是一个封装了完整视频捕获管线和相机 UI 的 view controller。session

在实例化相机以前,首先要检查设备是否支持相机录制:app

1iphone

2ide

3性能

4优化

5ui

6编码

7url

8

if ([UIImagePickerController

       isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {

    NSArray *availableMediaTypes = [UIImagePickerController

      availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];

    if ([availableMediaTypes containsObject:(NSString *)kUTTypeMovie]) {

        // 支持视频录制

    }

}

而后建立一个 UIImagePickerController 对象,设置好代理便于进一步处理录制好的视频 (好比存到相册) 以及对于用户关闭相机做出响应:

1

2

3

4

UIImagePickerController *camera = [UIImagePickerController new];

camera.sourceType = UIImagePickerControllerSourceTypeCamera;

camera.mediaTypes = @[(NSString *)kUTTypeMovie];

camera.delegate = self;

这是你实现一个功能完善的摄像机所须要写的全部代码。

相机配置

UIImagePickerController 提供了额外的配置选项。

经过设置 cameraDevice 属性能够选择一个特定的相机。这是一个 UIImagePickerControllerCameraDevice 枚举,默认状况下是 UIImagePickerControllerCameraDeviceRear,你也能够把它设置为 UIImagePickerControllerCameraDeviceFront。每次都应事先确认你想要设置的相机是可用的:

1

2

3

4

UIImagePickerController *camera = …

if ([UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]) {

    [camera setCameraDevice:UIImagePickerControllerCameraDeviceFront];

}

videoQuality 属性用于控制录制视频的质量。它容许你设置一个特定的编码预设,从而改变视频的比特率和分辨率。如下是六种预设:

1

2

3

4

5

6

7

8

9

enum {

   UIImagePickerControllerQualityTypeHigh = 0,

   UIImagePickerControllerQualityTypeMedium = 1, // default value

   UIImagePickerControllerQualityTypeLow = 2,

   UIImagePickerControllerQualityType640x480 = 3,

   UIImagePickerControllerQualityTypeIFrame1280x720 = 4,

   UIImagePickerControllerQualityTypeIFrame960x540 = 5

};

typedef NSUInteger  UIImagePickerControllerQualityType;

前三种为相对预设 (low, medium, high)。这些预设的编码配置会因设备不一样而不一样。若是选择 high,那么你选定的相机会提供给你该设备所能支持的最高画质。后面三种是特定分辨率的预设 (640×480 VGA, 960×540 iFrame, 和 1280×720 iFrame)。

自定义 UI

就像上面提到的,UIImagePickerController 自带一套相机 UI,能够直接使用。然而,你也能够自定义相机的控件,经过隐藏默认控件,而后建立带有控件的自定义视图,并覆盖在相机预览图层上面:

1

2

3

UIView *cameraOverlay = …

picker.showsCameraControls = NO;

picker.cameraOverlayView = cameraOverlay;

而后你须要将你覆盖层上的控件关联上 UIImagePickerController 的控制方法 (好比,startVideoCapture 和 stopVideoCapture)。

AVFoundation

若是你想要更多关于处理捕获视频的方法,而这些方法是 UIImagePickerController 所不能提供的,那么你须要使用 AVFoundation。

AVFoundation 中关于视频捕获的主要的类是 AVCaptureSession。它负责调配影音输入与输出之间的数据流:

AVCaptureSession setup

使用一个 capture session,你须要先实例化,添加输入与输出,接着启动从输入到输出之间的数据流:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

AVCaptureSession *captureSession = [AVCaptureSession new];

AVCaptureDeviceInput *cameraDeviceInput = …

AVCaptureDeviceInput *micDeviceInput = …

AVCaptureMovieFileOutput *movieFileOutput = …

if ([captureSession canAddInput:cameraDeviceInput]) {

    [captureSession addInput:cameraDeviceInput];

}

if ([captureSession canAddInput:micDeviceInput]) {

    [captureSession addInput:micDeviceInput];

}

if ([captureSession canAddOutput:movieFileOutput]) {

    [captureSession addOutput:movieFileOutput];

}

[captureSession startRunning];

(为了简单起见,调度队列 (dispatch queue) 的相关代码已经从上面那段代码中省略了。全部对 capture session 的调用都是阻塞的,所以建议将它们分配到后台串行队列中。)

capture session 能够经过一个 sessionPreset

来进一步配置,这能够用来指定输出质量的等级。有 11 种不一样的预设模式:

1

2

3

4

5

6

7

8

9

10

11

NSString *const  AVCaptureSessionPresetPhoto;

NSString *const  AVCaptureSessionPresetHigh;

NSString *const  AVCaptureSessionPresetMedium;

NSString *const  AVCaptureSessionPresetLow;

NSString *const  AVCaptureSessionPreset352x288;

NSString *const  AVCaptureSessionPreset640x480;

NSString *const  AVCaptureSessionPreset1280x720;

NSString *const  AVCaptureSessionPreset1920x1080;

NSString *const  AVCaptureSessionPresetiFrame960x540;

NSString *const  AVCaptureSessionPresetiFrame1280x720;

NSString *const  AVCaptureSessionPresetInputPriority;

第一个表明高像素图片输出。 接下来的九个和以前咱们在设置 UIImagePickerController 的 videoQuality 时看到过的 UIImagePickerControllerQualityType 选项很是类似,不一样的是,这里有一些额外可用于 capture session 的预设。 最后一个 (AVCaptureSessionPresetInputPriority) 表明 capture session 不去控制音频与视频输出设置。而是经过已链接的捕获设备的 activeFormat 来反过来控制 capture session 的输出质量等级。在下一节,咱们将会看到更多关于设备和设备格式的细节。

  • 输入

AVCaptureSession 的输入其实就是一个或多个的 AVCaptureDevice 对象,这些对象经过 AVCaptureDeviceInput 链接上 capture session。

咱们可使用 [AVCaptureDevice devices] 来寻找可用的捕获设备。以 iPhone 6 为例:

1

2

3

4

5

(

    “<avcapturefigvideodevice: 0x136514db0 [back camera][com.apple.avfoundation.avcapturedevice.built-in_video:0]>”,

    “<avcapturefigvideodevice: 0x13660be80 [front camera][com.apple.avfoundation.avcapturedevice.built-in_video:1]>”,

    “<avcapturefigaudiodevice: 0x174265e80 [iphone microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]>”

)</avcapturefigaudiodevice: 0x174265e80 [iphone microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]></avcapturefigvideodevice: 0x13660be80 [front camera][com.apple.avfoundation.avcapturedevice.built-in_video:1]></avcapturefigvideodevice: 0x136514db0 [back camera][com.apple.avfoundation.avcapturedevice.built-in_video:0]>

  • 视频输入

配置相机输入,须要实例化一个 AVCaptureDeviceInput 对象,参数是你指望的相机设备,而后把它添加到 capture session:

1

2

3

4

5

6

7

AVCaptureSession *captureSession = …

AVCaptureDevice *cameraDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

NSError *error;

AVCaptureDeviceInput *cameraDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:cameraDevice error:&error];

if ([captureSession canAddInput:input]) {

    [captureSession addInput:cameraDeviceInput];

}

若是上面提到的 capture session 预设列表里能知足你的需求,那你就不须要作更多的事情了。若是不够,好比你想要高的帧率,你将须要配置具体的设备格式。一个视频捕获设备有许多设备格式,每一个都带有特定的属性和功能。下面是对于 iPhone6 的后置摄像头的一些例子 (一共有 22 种可用格式):

baioge600.jpg

  • 格式 = 像素格式

  • FPS = 支持帧数范围

  • HRSI = 高像素静态图片尺寸

  • FOV = 视角

  • VIS = 该格式支持视频防抖

  • Upscales = 加入数字 upscaling 时的放大比例

  • AF = 自动对焦系统(1 是反差对焦,2 是相位对焦)

  • ISO = 支持感光度范围

  • SS = 支持曝光时间范围

  • HDR = 支持高动态范围图像

经过上面的那些格式,你会发现若是要录制 240 帧每秒的视频的话,能够根据想要的像素格式选用第一个或第二个格式。另外如果要捕获 1920×1080 的分辨率的视频的话,是不支持 240 帧每秒的。

配置一个具体设备格式,你首先须要调用 lockForConfiguration: 来获取设备的配置属性的独占访问权限。接着你简单地使用 setActiveFormat: 来设置设备的捕获格式。这将会自动把 capture session 的预设设置为 AVCaptureSessionPresetInputPriority。

一旦你设置了预想的设备格式,你就能够在这种设备格式的约束参数范围内进行进一步的配置了。

对于视频捕获的对焦,曝光和白平衡的设置,与图像捕获时同样,具体可参考第 21 期“iOS 上的相机捕捉”。除了那些,这里还有一些视频特有的配置选项。

你能够用捕获设备的 activeVideoMinFrameDuration 和 activeVideoMaxFrameDuration 属性设置帧速率,一帧的时长是帧速率的倒数。设置帧速率以前,要先确认它是否在设备格式所支持的范围内,而后锁住捕获设备来进行配置。为了确保帧速率恒定,能够将最小与最大的帧时长设置成同样的值:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

NSError *error;

CMTime frameDuration = CMTimeMake(1, 60);

NSArray *supportedFrameRateRanges = [device.activeFormat videoSupportedFrameRateRanges];

BOOL frameRateSupported = NO;

for (AVFrameRateRange *range in supportedFrameRateRanges) {

    if (CMTIME_COMPARE_INLINE(frameDuration, >=, range.minFrameDuration) &&

        CMTIME_COMPARE_INLINE(frameDuration, <=, range.maxFrameDuration)) {

        frameRateSupported = YES;

    }

}

if (frameRateSupported && [device lockForConfiguration:&error]) {

    [device setActiveVideoMaxFrameDuration:frameDuration];

    [device setActiveVideoMinFrameDuration:frameDuration];

    [device unlockForConfiguration];

}

视频防抖 是在 iOS 6 和 iPhone 4S 发布时引入的功能。到了 iPhone 6,增长了更强劲和流畅的防抖模式,被称为影院级的视频防抖动。相关的 API 也有所改动 (目前为止并无在文档中反映出来,不过能够查看头文件)。防抖并非在捕获设备上配置的,而是在 AVCaptureConnection 上设置。因为不是全部的设备格式都支持所有的防抖模式,因此在实际应用中应事先确认具体的防抖模式是否支持:

1

2

3

4

5

6

AVCaptureDevice *device = ...;

AVCaptureConnection *connection = ...;

AVCaptureVideoStabilizationMode stabilizationMode = AVCaptureVideoStabilizationModeCinematic;

if ([device.activeFormat isVideoStabilizationModeSupported:stabilizationMode]) {

    [connection setPreferredVideoStabilizationMode:stabilizationMode];

}

iPhone 6 的另外一个新特性就是视频 HDR (高动态范围图像),它是“高动态范围的视频流,与传统的将不一样曝光度的静态图像合成成一张高动态范围图像的方法彻底不一样”,它是内建在传感器中的。有两种方法能够配置视频 HDR:直接将 capture device 的 videoHDREnabled 设置为启用或禁用,或者使用 automaticallyAdjustsVideoHDREnabled 属性来留给系统处理。

技术参考:iPhone 6 和 iPhone Plus 的新 AV Foundation 相机特性

  • 音频输入

以前展现的捕获设备列表里面只有一个音频设备,你可能以为奇怪,毕竟 iPhone 6 有 3 个麦克风。然而由于有时会放在一块儿使用,便于优化性能,所以可能被当作一个设备来使用。例如在 iPhone 5 及以上的手机录制视频时,会同时使用前置和后置麦克风,用于定向降噪。

Technical Q&A: AVAudioSession – Microphone Selection

大多数状况下,设置成默认的麦克风配置便可。后置麦克风会自动搭配后置摄像头使用 (前置麦克风则用于降噪),前置麦克风和前置摄像头也是同样。

然而想要访问和配置单独的麦克风也是可行的。例如,当用户正在使用后置摄像头捕获场景的时候,使用前置麦克风来录制解说也应是可能的。这就要依赖于 AVAudioSession。 为了变动要访问的音频,audio session 首先须要设置为支持这样作的类别。而后咱们须要遍历 audio session 的输入端口和端口数据来源,来找到咱们想要的麦克风:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// 配置 audio session

AVAudioSession *audioSession = [AVAudioSession sharedInstance];

[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];

[audioSession setActive:YES error:nil];

// 寻找指望的输入端口

NSArray* inputs = [audioSession availableInputs];

AVAudioSessionPortDescription *builtInMic = nil;

for (AVAudioSessionPortDescription* port in inputs) {

    if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {

        builtInMic = port;

        break;

    }

}

// 寻找指望的麦克风

for (AVAudioSessionDataSourceDescription* source in builtInMic.dataSources) {

    if ([source.orientation isEqual:AVAudioSessionOrientationFront]) {

        [builtInMic setPreferredDataSource:source error:nil];

        [audioSession setPreferredInput:builtInMic error:&error];

        break;

    }

}

除了设置非默认的麦克风配置,你也可使用 AVAudioSession 来配置其余音频设置,好比音频增益和采样率等。

  • 访问权限

有件事你须要记住,访问相机和麦克风须要先得到用户受权。当你给视频或音频建立第一个 AVCaptureDeviceInput 对象时,iOS 会自动弹出一次对话框,请求用户受权,但你最好仍是本身实现下。以后你就能够在尚未被受权的时候,使用相同的代码来提示用户进行受权。当用户未受权时,对于录制视频或音频的尝试,获得的将是黑色画面和无声。

  • 输出

输入配置完了,如今把咱们的注意力转向 capture session 的输出。

AVCaptureMovieFileOutput

将视频写入文件,最简单的选择就是使用 AVCaptureMovieFileOutput 对象。把它做为输出添加到 capture session 中,就能够将视频和音频写入 QuickTime 文件,这只需不多的配置。

1

2

3

4

5

6

7

AVCaptureMovieFileOutput *movieFileOutput = [AVCaptureMovieFileOutput new];

if([captureSession canAddOutput:movieFileOutput]){

    [captureSession addOutput:movieFileOutput];

}

// 开始录制

NSURL *outputURL = …

[movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];

当实际的录制开始或中止时,想要接收回调的话就必需要一个录制代理。当录制中止时,输出一般还在写入数据,等它完成以后会调用代理方法。

AVCaptureMovieFileOutput 有一些其余的配置选项,好比在某段时间后,在达到某个指定的文件尺寸时,或者当设备的最小磁盘剩余空间达到某个阈值时中止录制。若是你还须要更多设置,好比自定义视频音频的压缩率,或者你想要在写入文件以前,处理视频音频的样本,那么你须要一些更复杂的操做。

AVCaptureDataOutput 和 AVAssetWriter

若是你想要对影音输出有更多的操做,你可使用 AVCaptureVideoDataOutput 和 AVCaptureAudioDataOutput 而不是咱们上节讨论的 AVCaptureMovieFileOutput。

这些输出将会各自捕获视频和音频的样本缓存,接着发送到它们的代理。代理要么对采样缓冲进行处理 (好比给视频加滤镜),要么保持原样传送。使用 AVAssetWriter 对象能够将样本缓存写入文件:

Using an AVAssetWriter

配置一个 asset writer 须要定义一个输出 URL 和文件格式,并添加一个或多个输入来接收采样的缓冲。咱们还须要将输入的 expectsMediaInRealTime属性设置为 YES,由于它们须要从 capture session 实时得到数据。

1

2

3

4

5

6

7

8

9

10

11

12

NSURL *url = …;

AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeMPEG4 error:nil];

AVAssetWriterInput *videoInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:nil];

videoInput.expectsMediaDataInRealTime = YES;

AVAssetWriterInput *audioInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:nil];

audioInput.expectsMediaDataInRealTime = YES;

if ([assetWriter canAddInput:videoInput]) {

    [assetWriter addInput:videoInput];

}

if ([assetWriter canAddInput:audioInput]) {

    [assetWriter addInput:audioInput];

}

(这里推荐将 asset writer 派送到后台串行队列中调用。)

在上面的示例代码中,咱们将 asset writer 的 outputSettings 设置为 nil。这就意味着附加上来的样本不会再被从新编码。若是你确实想要从新编码这些样本,那么须要提供一个包含具体输出参数的字典。关于音频输出设置的键值被定义在这里, 关于视频输出设置的键值定义在这里。

为了更简单点,AVCaptureVideoDataOutput 和 AVCaptureAudioDataOutput 分别带有 recommendedVideoSettingsForAssetWriterWithOutputFileType: 和 recommendedAudioSettingsForAssetWriterWithOutputFileType: 方法,能够生成与 asset writer 兼容的带有所有键值对的字典。因此你能够经过在这个字典里调整你想要重写的属性,来简单地定义你本身的输出设置。好比,增长视频比特率来提升视频质量等。

或者,你也可使用 AVOutputSettingsAssistant 来配置输出设置的字典,可是从个人经验来看,使用上面的方法会更好,它们会提供更实用的输出设置,好比视频比特率。另外,AVOutputSettingsAssistant 彷佛存在一些缺点,例如,当你改变但愿的视频的帧速率时,视频的比特率并不会改变。

实时预览

当使用 AVFoundation 来作图像捕获时,咱们必须提供一套自定义的用户界面。其中一个关键的相机交互组件是实时预览图。最简单的实现方式是经过把 AVCaptureVideoPreviewLayer 对象做为一个 sublayer 加到相机图层上去:

1

2

3

4

5

AVCaptureSession *captureSession = ...;

AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];

UIView *cameraView = ...;

previewLayer.frame = cameraView.bounds;

[cameraView.layer addSublayer:previewLayer];

若是你想要更进一步操做,好比,在实时预览图加滤镜,你须要将 AVCaptureVideoDataOutput 对象加到 capture session,而且使用 OpenGL 展现画面,具体可查看该文“iOS 上的相机捕捉”

总结

有许多不一样的方法能够给 iOS 上的视频捕获配置管线,从最直接的 UIImagePickerController,到精密配合的 AVCaptureSession 与 AVAssetWriter。如何抉择取决于你的项目要求,好比指望的视频质量和压缩率,或者是你想要展现给用户的相机控件。

相关文章
相关标签/搜索