iOS - 直播系列一:视频采集

苹果官方文档-AVFoundationhtml

为了管理从相机或者麦克风等这样的设备捕获到的信息,咱们须要输入对象(input)和输出对象(output),而且使用一个会话(AVCaptureSession)来管理 input 和 output 以前的数据流:git

类名 简介
AVCaptureDevice 输入设备,例如 摄像头 麦克风
AVCaptureInput 输入端口 [使用其子类]
AVCaptureOutput 设备输出 [使用其子类],输出视频文件或者静态图像
AVCaptureSession 管理输入到输出的数据流
AVCaptureVideoPreviewLayer 展现采集 预览View

如图,经过单个 session,也能够管理多个 input 和 output 对象之间的数据流,从而获得视频、静态图像和预览视图 github

多个输入输出设备

如图,input 能够有一个或多个输入端口,output 也能够有一个或多个数据来源(如:一个 AVCaptureMovieFileOutput 对象能够接收视频数据和音频数据)缓存

当添加 input 和 output 到 session 中时,session 会自动创建起一个链接(AVCaptureConnection)。咱们可使用这个 connection 来设置从 input 或者 从 output 获得的数据的有效性,也能够用来监控在音频信道中功率的平均值和峰值。bash

AVCaptureConnection

使用 Session 来管理数据流

建立一个 session 用来管理捕获到的数据,须要先将 inputs 和 outputs 添加到 session 中,当 session 执行 [startRunning] 方法后就会开始将数据流发送至 session,经过执行[stopRunning] 方法来结束数据流的发送。微信

AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];

// 添加 inputs 和 outputs

[session startRunning];
复制代码

在 [session startRunning] 以前咱们须要进行一些基本的配置 (如:设备分辨率,添加输入输出对象等)session

设置分辨率

// 设置分辨率 720P 标清
if ([captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
    captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
}
复制代码

附苹果官方文档中可供配置的分辨率列表app

分辨率列表

其中高分辨率(AVCaptureSessionPresetHigh) 为默认值,会根据当前设备进行自适应,可是这样以后导出来的文件就会很大,通常状况下设置为标清(AVCaptureSessionPreset1280x720) 就能够了异步

输入对象

// 直接使用后置摄像头
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
复制代码
// 在这个方法中的 mediaType 有三个选项供咱们使用
// AVMediaTypeVideo 视频
// AVMediaTypeAudio 音频
// AVMediaTypeMuxed 混合(视频 + 音频)
+ (nullable AVCaptureDevice *)defaultDeviceWithMediaType:(AVMediaType)mediaType;
复制代码

可是这种方式只能获取到后置摄像头,若是想要获取前置摄像头,可以使用async

AVCaptureDevice *videoDevice;
NSArray *devices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in devices) {
   if(device.position == AVCaptureDevicePositionFront) {
        // 前置摄像头
        videoDevice = device;
   }
}
复制代码
// 经过设备获取输入对象
AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:nil];
// 给会话添加输入
if([captureSession canAddInput:videoInput]) {
    [captureSession addInput:videoInput];
}
复制代码

输出对象

// 视频输出:设置视频原数据格式:YUV, RGB 
// 苹果不支持YUV的渲染,只支持RGB渲染,这意味着: YUV => RGB
AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];

// videoSettings: 设置视频原数据格式 YUV FULL
videoOutput.videoSettings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)};

// 设置代理:获取帧数据
// 队列:串行/并行,这里使用串行,保证数据顺序 
dispatch_queue_t queue = dispatch_queue_create("LinXunFengSerialQueue", DISPATCH_QUEUE_SERIAL);
[videoOutput setSampleBufferDelegate:self queue:queue];

// 给会话添加输出对象
if([captureSession canAddOutput:videoOutput]) {
    // 给会话添加输入输出就会自动创建起链接
    [captureSession addOutput:videoOutput];
}
复制代码

在这里,输出对象能够设置帧率

// 帧率:1秒10帧就差很少比较流畅了
videoOutput.minFrameDuration = CMTimeMake(1, 10);
复制代码

输出对象在设置视频原数据格式时使用 videoSettings 属性,须要赋值的类型是字典 格式有两种,一种是YUV,另外一种是RGB(通常咱们都使用YUV,由于体积比RGB小)

// key
kCVPixelBufferPixelFormatTypeKey 指定解码后的图像格式

// value
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange  : YUV420 用于标清视频[420v]
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange   : YUV422 用于高清视频[420f] 
kCVPixelFormatType_32BGRA : 输出的是BGRA的格式,适用于OpenGL和CoreImage

区别:
一、前两种是相机输出YUV格式,而后转成RGBA,最后一种是直接输出BGRA,而后转成RGBA;
二、420v 输出的视频格式为NV12;范围: (luma=[16,235] chroma=[16,240])
三、420f 输出的视频格式为NV12;范围: (luma=[0,255] chroma=[1,255])
复制代码

预览图层

AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
previewLayer.frame = self.view.bounds;
[self.view.layer  addSublayer:previewLayer];
复制代码

实时显示摄像头捕获到的图像,但不适用于滤镜渲染

代理方法

#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate
/* CMSampleBufferRef: 帧缓存数据,描述当前帧信息 CMSampleBufferGetXXX : 获取帧缓存信息 CMSampleBufferGetDuration : 获取当前帧播放时间 CMSampleBufferGetImageBuffer : 获取当前帧图片信息 */
// 获取帧数据
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    // captureSession 会话若是没有强引用,这里不会获得执行
    
    NSLog(@"----- sampleBuffer ----- %@", sampleBuffer);
}
复制代码
// 获取帧播放时间
CMTime duration = CMSampleBufferGetDuration(sampleBuffer);
复制代码

在代理方法中,能够把 sampleBuffer 数据渲染出来去显示画面。适用于滤镜渲染

// 获取图片帧数据
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CIImage *ciImage = [CIImage imageWithCVImageBuffer:imageBuffer];
UIImage *image = [UIImage imageWithCIImage:ciImage];

dispatch_async(dispatch_get_main_queue(), ^{
    self.imageView.image = image;
});
复制代码

须要注意的是:代理方法中的全部动做所在队列都是在异步串行队列中,因此更新UI的操做须要回到主队列中进行!!

可是此时会发现,画面是向左旋转了90度,由于默认采集的视频是横屏的,须要咱们进一步作调整。如下步骤添加在[session startRunning];以前便可,可是必定要在添加了 input 和 output以后~

// 获取输入与输出之间的链接
AVCaptureConnection *connection = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
// 设置采集数据的方向
connection.videoOrientation = AVCaptureVideoOrientationPortrait;
// 设置镜像效果镜像
connection.videoMirrored = YES;
复制代码

Demo

LXFAudioVideo

微信公众号
相关文章
相关标签/搜索