swift 视频录制保存

swift 视频录制  

1 添加访问的权限

Privacy - Camera Usage Description

Privacy - Microphone Usage Description

Privacy - Photo Library Usage Description 

2  视频录制  

//  视频录制 

//  IWVideoRecordingController.swift

//  VideoRecording

//

//  Created by goWhere on 16/7/4.

//  Copyright © 2016年 iwhere. All rights reserved.

//

importUIKit

importAVFoundation

importPhotos

class IWVideoRecordingController: UIViewController {


    //  最常视频录制时间,单位 秒

    let MaxVideoRecordTime = 6000

    //  MARK: - Properties

    //  视频捕获会话,他是 input 和 output 之间的桥梁,它协调着 input 和 output 之间的数据传输

    let captureSession = AVCaptureSession()

    //  视频输入设备,前后摄像头

    var camera: AVCaptureDevice?

    //  展示界面

    var previewLayer: AVCaptureVideoPreviewLayer!

    //  HeaderView 

    var headerView: UIView!

    //  音频输入设备

    let audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)

    //  将捕获到的视频输出到文件

    let fileOut = AVCaptureMovieFileOutput()


    //  开始、停止按钮

    var startButton, stopButton: UIButton!

    //  前后摄像头转换、闪光灯 按钮

    var cameraSideButton, flashLightButton: UIButton!

    //  录制时间 Label

    var totolTimeLabel: UILabel!

    //  录制时间Timer

    vartimer:Timer?

    var secondCount = 0

    //  视频操作View

    var operatorView: IWVideoOperatorView!

    //  表示当时是否在录像中

    var isRecording = false



    //  MARK: - LifeCycle

    override func viewDidLoad() {

        super.viewDidLoad()


        //  录制视频基本设置

        setupAVFoundationSettings()

        //  UI 布局

        setupButton()

        setupHeaderView()

    }


    overridefuncviewWillAppear(_animated:Bool) {

        super.viewWillAppear(animated)

        navigationController?.isNavigationBarHidden = true

    }

    overridefuncviewWillDisappear(_animated:Bool) {

        super.viewWillDisappear(animated)

        navigationController?.isNavigationBarHidden = false

    }


    //  MARK: - Private Methods

    /// 对视频进行基本设置

    private func setupAVFoundationSettings() {

        // 相机

        camera = cameraWithPosition(position: AVCaptureDevice.Position.back)

        //  设置视频清晰度

        captureSession.sessionPreset = AVCaptureSession.Preset.vga640x480

        //  添加视频、音频输入设备

        ifletvideoInput =try?AVCaptureDeviceInput(device:camera!) {

            captureSession.addInput(videoInput)

        }

        ifaudioDevice!=nil,

           letaudioInput =try?AVCaptureDeviceInput(device:audioDevice!) {

            captureSession.addInput(audioInput)

        }

        //  添加视频捕获输出

        self.captureSession.addOutput(fileOut)


        //  使用 AVCaptureVideoPreviewLayer 可以将摄像头拍到的实时画面显示在 ViewController 上

        letvideoLayer =AVCaptureVideoPreviewLayer(session:self.captureSession)

        videoLayer.frame=view.bounds

        videoLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill

        view.layer.addSublayer(videoLayer)

        previewLayer= videoLayer

        //  启动 Session 回话

        self.captureSession.startRunning()

    }


    ///选择摄像头

    private func cameraWithPosition(position: AVCaptureDevice.Position) -> AVCaptureDevice? {

        let devices = AVCaptureDevice.devices(for: AVMediaType.video)

        foritemindevices {

            ifitem.position== position {

                returnitem

            }

        }

        returnnil

    }



    //  MARK: - UI Settings

    /**

     创建按钮

     */

    private func setupButton() {

        //  开始按钮

        startButton=prepareButtons(btnTitle:"开始",btnSize:CGSize(width:120,height:50),btnCenter:CGPoint(x:view.bounds.width/2-70,y:view.bounds.height-50))

        startButton.backgroundColor = UIColor.red

        startButton.addTarget(self, action: #selector(onClickedStartButton(startButton:)), for: .touchUpInside)


        //  结束按钮

        stopButton=prepareButtons(btnTitle:"结束",btnSize:CGSize(width:120,height:50),btnCenter:CGPoint(x:view.bounds.width/2+70,y:view.bounds.height-50))

        stopButton.backgroundColor = UIColor.lightGray

        stopButton.isUserInteractionEnabled = false

        stopButton.addTarget(self, action: #selector(onClickedEndButton(endButton:)), for: .touchUpInside)

    }


    //  开始、结束按钮风格统一

    privatefuncprepareButtons(btnTitletitle:String,btnSizesize:CGSize,btnCentercenter:CGPoint) ->UIButton{

        letbutton =UIButton(frame:CGRect(x:0,y:0,width: size.width,height: size.height))

        button.center= center

        button.clipsToBounds=true

        button.layer.cornerRadius=20

        button.setTitle(title,for: .normal)

        button.setTitleColor(UIColor.white, for: .normal)

        view.addSubview(button)

        returnbutton

    }


    //  headerView

    private func setupHeaderView() {

        headerView=UIView(frame:CGRect(x:0,y:0,width:view.bounds.width,height:64))

        headerView.backgroundColor = UIColor.black.withAlphaComponent(0.3)

        view.addSubview(headerView)


        letcenterY =headerView.center.y+5

        letdefaultWidth:CGFloat=40


        //  返回、摄像头调整、时间、闪光灯四个按钮

        letbackButton =UIButton(frame:CGRect(x:0,y:0,width:20,height:20))

        backButton.setBackgroundImage(UIImage(named: "iw_back"), for: .normal)

        backButton.addTarget(self,action:#selector(backAction),for: .touchUpInside)

        backButton.center=CGPoint(x:25,y: centerY)

        headerView.addSubview(backButton)


        cameraSideButton=UIButton(frame:CGRect(x:0,y:0,width: defaultWidth,height: defaultWidth *68/99.0))

        cameraSideButton.setBackgroundImage(UIImage(named: "iw_cameraSide"), for: .normal)

        cameraSideButton.center=CGPoint(x:100,y: centerY)

        cameraSideButton.addTarget(self, action: #selector(changeCamera(cameraSideButton:)), for: .touchUpInside)

        headerView.addSubview(cameraSideButton)


        totolTimeLabel=UILabel(frame:CGRect(x:0,y:0,width:100,height:20))

        totolTimeLabel.center=CGPoint(x:headerView.center.x,y: centerY)

        totolTimeLabel.textColor = UIColor.white

        totolTimeLabel.textAlignment = .center

        totolTimeLabel.font = UIFont.systemFont(ofSize: 19)

       // 时间的设置  

        totolTimeLabel.text = "00:00:00"

        view.addSubview(totolTimeLabel)


        flashLightButton=UIButton(frame:CGRect(x:0,y:0,width: defaultWidth,height: defaultWidth *68/99.0))

        flashLightButton.setBackgroundImage(UIImage(named: "iw_flashOn"), for: .selected)

        flashLightButton.setBackgroundImage(UIImage(named: "iw_flashOff"), for: .normal)

        flashLightButton.center=CGPoint(x:headerView.bounds.width-100,y: centerY)

        flashLightButton.addTarget(self, action: #selector(switchFlashLight(flashButton:)), for: .touchUpInside)

        headerView.addSubview(flashLightButton)


    }


    //  MARK: - UIButton Actions

    //  按钮点击事件

    //  点击开始录制视频

    @objc private func onClickedStartButton(startButton: UIButton) {

        hiddenHeaderView(isHidden: true)

        //  开启计时器

        timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(videoRecordingTotolTime), userInfo: nil, repeats: true)



        if!isRecording{

            //  记录状态: 录像中 ...

            isRecording=true

            captureSession.startRunning()


            //  设置录像保存地址,在 Documents 目录下,名为 当前时间.mp4

            let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)

            letdocumentDirectory = path[0]asString

            letfilePath:String? ="\(documentDirectory)/\(Date()).mp4"

            letfileUrl:URL? =URL(fileURLWithPath: filePath!)

            //  启动视频编码输出

            fileOut.startRecording(to: fileUrl!, recordingDelegate: self)


            //  开始、结束按钮改变颜色

            startButton.backgroundColor=UIColor.lightGray

            stopButton.backgroundColor = UIColor.red

            startButton.isUserInteractionEnabled=false

            stopButton.isUserInteractionEnabled = true

        }


    }


    //  点击停止按钮,停止了录像

    @objc private func onClickedEndButton(endButton: UIButton) {

        hiddenHeaderView(isHidden: false)


        //  关闭计时器

        timer?.invalidate()

        timer=nil

        secondCount=0


        ifisRecording{

            //  停止视频编码输出

            captureSession.stopRunning()


            //  记录状态: 录像结束 ...

            isRecording=false


            //  开始结束按钮颜色改变

            startButton.backgroundColor = UIColor.red

            stopButton.backgroundColor = UIColor.lightGray

            startButton.isUserInteractionEnabled = true

            stopButton.isUserInteractionEnabled = false

        }


        //  弹出View

        operatorView = (Bundle.main.loadNibNamed("IWVideoOperatorView", owner: self, options: nil)?.first as! IWVideoOperatorView)

        operatorView.getSuperViewController(superController: self)


        operatorView.frame = CGRect(x: 0, y: view.bounds.height, width: view.bounds.width, height: view.bounds.height)

        view.addSubview(operatorView)


        UIView.animate(withDuration: 0.3) {

            self.operatorView.frame.origin.y=0

        }

    }


    //  录制时间

    @objc private func videoRecordingTotolTime() {

        secondCount+=1


        //  判断是否录制超时

        if secondCount == MaxVideoRecordTime {

            timer?.invalidate()

            HsuAlert(title: "最常只能录制十分钟呢", message: nil, ensureTitle: "确定", cancleTitle: nil, ensureAction: nil, cancleAction: nil)

        }


        lethours =secondCount/3600

        letmintues = (secondCount%3600) /60

        letseconds =secondCount%60 


        totolTimeLabel.text=String(format:"%02d", hours) +":"+String(format:"%02d", mintues) +":"+String(format:"%02d", seconds)

    }


    //  是否隐藏 HeaderView

    func hiddenHeaderView(isHidden: Bool) {

        ifisHidden {

            UIView.animate(withDuration:0.2) {

               self.headerView.frame.origin.y-=64

            }

        }else{

            UIView.animate(withDuration:0.2) {

                self.headerView.frame.origin.y+=64

            }

        }

    }


    //  返回上一页

    @objc private func backAction() {

        self.navigationController?.popViewController(animated: true)

    }


    //  调整摄像头

    @objc private func changeCamera(cameraSideButton: UIButton) {

        cameraSideButton.isSelected= !cameraSideButton.isSelected

        captureSession.stopRunning()

        //  首先移除所有的 input

        iflet  allInputs =captureSession.inputsas? [AVCaptureDeviceInput] {

            forinputinallInputs {

                captureSession.removeInput(input)

            }

        }


        changeCameraAnimate()


        //  添加音频输出

        ifaudioDevice!=nil,

            letaudioInput =try?AVCaptureDeviceInput(device:audioDevice!) {

            self.captureSession.addInput(audioInput)

        }

        ifcameraSideButton.isSelected{

            camera = cameraWithPosition(position: .front)

            ifletinput =try?AVCaptureDeviceInput(device:camera!) {

                captureSession.addInput(input)

            }


            ifflashLightButton.isSelected{

                flashLightButton.isSelected=false

            }

        }else{

            camera = cameraWithPosition(position: .back)

            ifletinput =try?AVCaptureDeviceInput(device:camera!) {

                captureSession.addInput(input)

            }

        }

    }


    //  切换动画

    private func changeCameraAnimate() {

        letchangeAnimate =CATransition()

        changeAnimate.delegate=self

        changeAnimate.duration=0.4

        changeAnimate.type=CATransitionType(rawValue:"oglFlip")

        changeAnimate.subtype=CATransitionSubtype.fromRight

        previewLayer.add(changeAnimate,forKey:"changeAnimate")

    }


    //  开启闪光灯

    @objc private func switchFlashLight(flashButton: UIButton) {

        if self.camera?.position == AVCaptureDevice.Position.front {

            return

        }

        let camera = cameraWithPosition(position: .back)

        ifcamera?.torchMode==AVCaptureDevice.TorchMode.off{

            do{

                trycamera?.lockForConfiguration()

            }catchleterrorasNSError{

                print("开启闪光灯失败 : \(error)")

            }


            camera?.torchMode=AVCaptureDevice.TorchMode.on

            camera?.flashMode=AVCaptureDevice.FlashMode.on

            camera?.unlockForConfiguration()


            flashButton.isSelected=true

        }else{

            do{

                trycamera?.lockForConfiguration()

            }catchleterrorasNSError{

                print("关闭闪光灯失败: \(error)")

            }


            camera?.torchMode=AVCaptureDevice.TorchMode.off

            camera?.flashMode=AVCaptureDevice.FlashMode.off

            camera?.unlockForConfiguration()


            flashButton.isSelected=false

        }

    }


    override var shouldAutorotate: Bool {

        return false

    }


    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {

        return.portrait

    }

}

// MARK: - CAAnimationDelegate

extension IWVideoRecordingController: CAAnimationDelegate {

    ///动画开始

    func animationDidStart(_ anim: CAAnimation) {

        captureSession.startRunning()

    }


    ///动画结束

    funcanimationDidStop(_anim:CAAnimation,finishedflag:Bool) {

    }

}

// MARK: - AVCaptureFileOutputRecordingDelegate

extension IWVideoRecordingController: AVCaptureFileOutputRecordingDelegate {

    ///开始录制

    funcfileOutput(_output:AVCaptureFileOutput,didStartRecordingTofileURL:URL,fromconnections: [AVCaptureConnection]) {


    }


    ///结束录制

    funcfileOutput(_output:AVCaptureFileOutput,didFinishRecordingTooutputFileURL:URL,fromconnections: [AVCaptureConnection],error:Error?) {

        operatorView.getVideoUrl(videoUrl: outputFileURL)

    }

}


3 .  // 将视频保存 在本地  

private func saveVideoToAlbum(videoUrl: URL) {

        var info =""

        // 保存的方法 

        PHPhotoLibrary.shared().performChanges({

            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoUrl)

        }) { (success, error)in

            if success {

                info ="保存成功"

            }else{

                info ="保存失败,err = \(error.debugDescription)"

            }


            DispatchQueue.main.async{

                letalertVC =UIAlertController(title: info,message:nil,preferredStyle: .alert)

                alertVC.addAction(UIAlertAction(title:"OK",style: .default,handler:nil))

                self.controller.present(alertVC,animated:true,completion:nil)

            }

        }

    }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容