0%

使用AVCaptureSession采集视频

本文将前文的继续Kcamera项目,并使用AVCaptureSession获取摄像头采集到的视频.但是实际的视频录制和照片拍摄将在后面的文章中才会实现.

实现步骤

创建预览视图类

新建PreView类继承自UIView,PreView用来展示一个摄像头当前拍摄内容的实时预览视图.更具体的说,我们将使用AVCaptureVedioPreviewLayer方法实现这一预览.

打开Main.storyboard,修改根view的层次结构如下:

其中preView和controlView与父视图上下左右约束都为0,CameraModeView的约束与之前相同,但其父视图修改为controlView.

并在preView的identity inspector面板中将Custom Class修改为我们创建的PreView类.

打开PreView.swift,添加如下代码修改Preview的图层类型:

override class func layerClass()->AnyClass
{
    return AVCaptureVideoPreviewLayer.self
}

重写session属性的访问方法,保证采集到的数据直接输出到图层中:

var session:AVCaptureSession!{
    get{
        return (self.layer as! AVCaptureVideoPreviewLayer).session
    }set(newValue){
        (self.layer as! AVCaptureVideoPreviewLayer).session = newValue
    }
}

设置音频会话类型

在AppDelegate的application(application:didFinishLaunchingWithOptions:)方法返回前加入如下代码:

    let session = AVAudioSession.sharedInstance()
    try! session.setCategory(AVAudioSessionCategoryPlayAndRecord)
    try! session.setActive(true)    

创建捕捉模型

新建CaptureModel类继承自NSObject,用来处理视频采集的具体行为.

初始化捕捉会话

在CaptureModel类中添加一个捕捉会话:

//捕捉会话
var captureSession:AVCaptureSession!

设置捕捉对话的代码如下:

//捕捉会话
func setupSession() -> Bool {
    //1. 创建捕捉会话
    self.captureSession = AVCaptureSession()
    //2. 设置视频采集质量
    self.captureSession.sessionPreset = AVCaptureSessionPresetHigh
    //3. 设置视频采集设备(默认返回后置摄像头)
    let videoDevice = AVCaptureDevice .defaultDeviceWithMediaType(AVMediaTypeVideo)
    //4. 把采集设备封装为一个AVCaptureDeviceInput对象(这里忽略了错误处理)
    let videoInput = try? AVCaptureDeviceInput(device: videoDevice)
    //4. 在捕捉会话中添加AVCaptureDeviceInput对象
    if videoInput != nil {
        if self.captureSession.canAddInput(videoInput) {
            self.captureSession.addInput(videoInput)
        }
    }else{
        return false
    }
    
    //5. 参照设置视频采集输入的步骤,将音频采集输入添加到捕捉会话中
    let audioDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio)
    let audioInput = try? AVCaptureDeviceInput(device: audioDevice)
    if audioInput != nil {
        if self.captureSession .canAddInput(audioInput) {
            self.captureSession.addInput(audioInput)
        }
    }else{
        return false
    }
    return true
    
}

启动捕捉会话

在设置捕捉会话之后,如果需要进行实际的视频采集,还需要启动会话,用来启动数据流并使其处于准备捕捉图片或视频的状态.

在CaptureModel类中增加一个属性:

//由于捕捉会话的启动是一个同步调用,所以需要异步调用
let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

启动捕捉会话的方法如下:

//启动捕捉会话
func startSession() -> Void {
    if !self.captureSession.running{
        dispatch_sync(self.globalQueue, { 
            self.captureSession.startRunning()
        })
    }
}    

停止捕捉会话

//停止捕捉会话
func stopSession() -> Void {
    if self.captureSession.running {
        dispatch_sync(self.globalQueue, { 
            self.captureSession.stopRunning()
        })
    }
}

处理隐私需求

当应用程序试图访问麦克风或相机时,系统会弹出一个对话框询问用户是否同意授权.但当对话框出现时,系统并没有中断来等待用户响应,而是立即返回一个设备,比如调用音频设备则返回静音的设备,如果是相机则返回黑白帧.直到用户同意了授权,才会实际开始捕捉视频或音频.如果用户不允许授权,则这个会话期间不会记录任何内容.

所以我们需要在初始化捕捉会话时,判断当前程序对相机和麦克风的访问状态,在初始化捕捉会话的setupSession()方法最前面加入如下代码:

    //0. 判断权限
    let videoAuthStatus = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
    if videoAuthStatus == .Denied || videoAuthStatus == .Restricted {
        UIAlertView(title: "提示", message: "请在iPhone的“设置-隐私-相机”选项中,允许访问相机 ", delegate: nil, cancelButtonTitle: "确定").show()
        
        return false
    }
    let audioAuthStatus = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
    if audioAuthStatus == .Denied || audioAuthStatus == .Restricted {
        UIAlertView(title: "提示", message: "请在iPhone的“设置-隐私-相机”选项中,允许访问麦克风 ", delegate: nil, cancelButtonTitle: "确定").show()
        return false
    }

测试

打开Main.storyboard,将preView连接为ViewController的@IBOutlet属性:

    @IBOutlet weak var preView: PreView!

接着在viewDidLoad()方法中添加如下代码:

    self.captureModel = CaptureModel()
    if self.captureModel.setupSession() {
        self.preView.session = self.captureModel.captureSession
        self.captureModel.startSession()
    }
}

运行程序,就可以看到后置摄像头采集到的视频内容了.