iOS音頻與視頻的開發(二)- 使用AVAudioRecorder進行錄製音頻

一、使用AVAudioRecorder錄製視頻

  AVAudioRecorder與AVAudioPlayer相似,它們都屬於AVFoundation的類。AVAudioRecorder的功能相似於一個錄音器,使用AVAudioRecorder錄製音頻十分簡單,當程序控制AVAudioRecorder對象建立完成以後,能夠調用AVAudioRecorder的以下方法進行錄製。session

  一、prepareToRecord:準備開始錄製。調用record方法時,若是音頻尚未準備好,程序會隱式先執行該方法。app

  二、record:開始或恢復錄製。調用該方法是,若是音頻尚未準備好,程序會隱式執行prepareToRecord方法。less

  三、recordAtTime:在指定時間點開始或恢復錄製。async

  四、record(atTime time: TimeInterval, forDuration duration: TimeInterval) -> Bool 在指定時間點開始或恢復錄製,並指定錄製的持續時間。ide

  五、pause:暫停。stop:中止url

  六、prepareToPlay:準備開始播放。若是play方法沒有準備好時,會隱式先執行該方法。spa

  使用AVAudioRecorder錄製視頻的步驟以下:

  一、建立AVAudioRecorder對象。在建立AVAudioRecorder對象以前,先準備一個Dictionary對象,該對象中封裝了音頻的相關設置信息。code

//建立字典,用於保存錄制屬性
        let recordSettings:[String:Any] = [
            //設置錄製音頻的格式
            AVFormatIDKey:kAudioFormatAppleLossless,
            AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue,
            AVEncoderBitRateKey: 32000,
             //設置錄製音頻的每一個樣點的通道數
            AVNumberOfChannelsKey: 2,
            //設置錄製音頻的採樣率
            AVSampleRateKey: 44100.0
        ]

  二、若是須要監聽錄製完成、錄製被中斷的事件,則應該爲AVAudioRecorder對象設置delegate對象,delegate對象須要實現AVAudioRecorderDelegate協議。orm

func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
        print("錄製完成")
        stopBtn.isEnabled = false
        playBtn.isEnabled = true
        recordBtn.setTitle("錄製", for: .normal)
        //彈窗選擇
        let alert = UIAlertController(title: "錄製", message: "錄製完成", preferredStyle:.alert)
        alert.addAction(UIAlertAction(title: "保存", style: .default, handler: {[unowned self] _ in
            self.recorder = nil
        }))
        alert.addAction(UIAlertAction(title: "刪除", style:.default, handler: { [unowned self] _ in
            self.recorder.deleteRecording()
        }))
        self.present(alert, animated: true, completion: nil)
    }
    
    func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder,
                                          error: Error?) {
        print("\(#function)")
        
        if let e = error {
            print("\(e.localizedDescription)")
        }
    }

  三、調用AVAudioRecorder對象的record方法錄製視頻。視頻

  案例代碼:

import UIKit
import AVFoundation
import AVKit
class NAPublishViewController : UIViewController {
    var recorder : AVAudioRecorder!
    var player : AVAudioPlayer!
    
    var meterTimer : Timer!
    var soundFileUrl : URL!
    
    lazy var recordBtn : UIButton = {
        let recordBtn = UIButton()
        recordBtn.setTitle("錄音", for: .normal)
        recordBtn.setTitleColor(.black, for: .normal)
        recordBtn.addTarget(self, action: #selector(recordAction(sender:)), for: .touchUpInside)
        return recordBtn
    }()
    
    lazy var stopBtn : UIButton = {
        let stopBtn = UIButton()
        stopBtn.setTitle("中止", for: .normal)
        stopBtn.setTitleColor(.black, for: .normal)
        stopBtn.addTarget(self, action:#selector(stopBtnAction(sender:)) , for: .touchUpInside)
        return stopBtn
    }()
    
    lazy var playBtn : UIButton = {
        let playBtn = UIButton()
        playBtn.setTitle("播放", for: .normal)
        playBtn.setTitleColor(.black, for: .normal)
        playBtn.addTarget(self , action: #selector(playBtnAction(sender:)), for: .touchUpInside)
        return playBtn
    }()

    lazy var statusLabel : UILabel = {
        let statusLabel = UILabel()
        statusLabel.text = "00:00"
        statusLabel.textColor = .black
        return statusLabel
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setSubViewsConstraints()
        
        stopBtn.isEnabled = false
        playBtn.isEnabled = false
        
        setSessionPlayback()
        
    }
    
    @objc func playBtnAction(sender:UIButton) -> Void {
        var url: URL?
        if self.recorder != nil {
            url = self.recorder.url
        } else {
            url = self.soundFileUrl!
        }
        print("playing \(String(describing: url))")
        
        do {
            self.player = try AVAudioPlayer(contentsOf: url!)
            stopBtn.isEnabled = true
            player.delegate = self
            player.prepareToPlay()
            player.volume = 1.0
            player.play()
        } catch {
            self.player = nil
            print(error.localizedDescription)
        }
    }
    
    @objc func stopBtnAction(sender:UIButton) -> Void {
        recorder?.stop()
        player?.stop()
        
        meterTimer.invalidate()
        recordBtn.setTitle("錄音", for: .normal)
        let session = AVAudioSession.sharedInstance()
        do {
            try session.setActive(false)
            playBtn.isEnabled = true
            stopBtn.isEnabled = false
            recordBtn.isEnabled = true
        } catch  {
            print(error.localizedDescription)
        }
    }
    
    @objc func recordAction(sender:UIButton) -> Void {
        if player != nil && player.isPlaying {
            print("stopping")
            player.stop()
        }
        
        if recorder == nil {
            recordBtn.setTitle("暫停", for: .normal)
            playBtn.isEnabled = false
            stopBtn.isEnabled = true
            recordWithPermission(true)
            return
        }
        
        if recorder != nil && recorder.isRecording {
            recorder.pause()
            recordBtn.setTitle("繼續", for: .normal)
        } else {
            recordBtn.setTitle("暫停", for: .normal)
            playBtn.isEnabled = false
            stopBtn.isEnabled = true
            recordWithPermission(false)
        }
        
    }
    
    func recordWithPermission(_ setup: Bool) {
        AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
            if granted {
                DispatchQueue.main.async {
                    self.setSessionPlayAndRecord()
                    
                    if setup {
                        self.setupRecorder()
                    }
                    self.recorder.record()
                    
                    self.meterTimer = Timer.scheduledTimer(timeInterval: 0.1,
                                                           target: self,
                                                           selector: #selector(self.updateAudioMeter(_:)),
                                                           userInfo: nil,
                                                           repeats: true)
                }
            } else {
                print("Permission to record not granted")
            }
        }
        
        if  AVAudioSession.sharedInstance().recordPermission == .denied {
            print("permission denied")
        }
    }
    
    func setupRecorder() {
        let format = DateFormatter()
        format.dateFormat="yyyy-MM-dd-HH-mm-ss"
        let currentFileName = "recording-\(format.string(from: Date())).m4a"
        print(currentFileName)
        //獲取沙盒文件目錄
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        //拼接路徑
        self.soundFileUrl = documentsDirectory.appendingPathComponent(currentFileName)
        print("writing to soundfile url: '\(soundFileUrl!)'")
        
        if FileManager.default.fileExists(atPath: soundFileUrl.absoluteString) {
            print("soundfile \(soundFileUrl.absoluteString) 存在")
        }
        
        //建立字典,用於保存錄制屬性
        let recordSettings:[String:Any] = [
            //設置錄製音頻的格式
            AVFormatIDKey:kAudioFormatAppleLossless,
            AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue,
            AVEncoderBitRateKey: 32000,
             //設置錄製音頻的每一個樣點的通道數
            AVNumberOfChannelsKey: 2,
            //設置錄製音頻的採樣率
            AVSampleRateKey: 44100.0
        ]
        
        do {
            recorder = try AVAudioRecorder(url: soundFileUrl, settings: recordSettings)
            recorder.delegate = self
            recorder.isMeteringEnabled = true
            recorder.prepareToRecord()
        } catch {
            recorder = nil
            print(error.localizedDescription)
        }
        
    }
    func setSessionPlayAndRecord() {
        //獲取當前應用的音頻會話
        let session = AVAudioSession.sharedInstance()
        do {
            //設置音頻類別,PlayAndRecord - 這說明當前音頻會話既可播放,又可錄製
            try session.setCategory(AVAudioSession.Category.playAndRecord, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
        } catch {
            print(error.localizedDescription)
        }
        
        do {
            try session.setActive(true)
        } catch {
            print(error.localizedDescription)
        }
    }
    
    func setSessionPlayback()  {
        //獲取當前應用的音頻會話
        let session = AVAudioSession.sharedInstance()
        
        do {
            //設置音頻類別
            try session.setCategory(AVAudioSession.Category.playback, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
        } catch {
            print("不能設置session category")
            print(error.localizedDescription)
        }
        
        do {
            //激活當前應用的音頻會話
            try session.setActive(true, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation)
        } catch  {
            print("不能設置session active")
            print(error.localizedDescription)
        }
    }
    
    @objc func updateAudioMeter(_ timer: Timer) {
        
        if let recorder = self.recorder {
            if recorder.isRecording {
                let min = Int(recorder.currentTime / 60)
                let sec = Int(recorder.currentTime.truncatingRemainder(dividingBy: 60))
                let s = String(format: "%02d:%02d", min, sec)
                statusLabel.text = s
                recorder.updateMeters()
            }
        }
    }
    
    func setSubViewsConstraints() -> Void {
        view.addSubview(recordBtn)
        recordBtn.snp.makeConstraints { (make) in
            make.left.width.height.equalTo(50)
            make.top.equalTo(100)
        }
        
        view.addSubview(stopBtn)
        stopBtn.snp.makeConstraints { (make) in
            make.centerX.equalToSuperview()
            make.width.height.equalTo(50)
            make.top.equalTo(100)
        }
        
        view.addSubview(playBtn)
        playBtn.snp.makeConstraints { (make) in
            make.right.equalTo(-50)
            make.width.height.equalTo(50)
            make.top.equalTo(100)
        }
        
        view.addSubview(statusLabel)
        statusLabel.snp.makeConstraints { (make) in
            make.centerX.equalToSuperview()
            make.width.height.equalTo(50)
            make.top.equalTo(stopBtn.snp_bottom).offset(30)
            
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        recorder = nil
        player = nil
    }
}

extension NAPublishViewController : AVAudioRecorderDelegate {
    
    func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
        print("錄製完成")
        stopBtn.isEnabled = false
        playBtn.isEnabled = true
        recordBtn.setTitle("錄製", for: .normal)
        //彈窗選擇
        let alert = UIAlertController(title: "錄製", message: "錄製完成", preferredStyle:.alert)
        alert.addAction(UIAlertAction(title: "保存", style: .default, handler: {[unowned self] _ in
            self.recorder = nil
        }))
        alert.addAction(UIAlertAction(title: "刪除", style:.default, handler: { [unowned self] _ in
            self.recorder.deleteRecording()
        }))
        self.present(alert, animated: true, completion: nil)
    }
    
    func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder,
                                          error: Error?) {
        print("\(#function)")
        
        if let e = error {
            print("\(e.localizedDescription)")
        }
    }
}


extension NAPublishViewController : AVAudioPlayerDelegate {
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {

        recordBtn.isEnabled = true
        stopBtn.isEnabled = false
    }
    
    func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
        print("\(#function)")
        
        if let e = error {
            print("\(e.localizedDescription)")
        }
        
    }
}
使用AVAudioRecorder進行錄製視頻

   運行效果圖: 

相關文章
相關標籤/搜索