瀏覽代碼

完成录音

harry 3 年之前
父節點
當前提交
ccf6159214

+ 0 - 27
BFRecordManager.swift

@@ -1,27 +0,0 @@
-//
-//  BFRecordManager.swift
-//  BFRecordScreenKit
-//
-//  Created by 胡志强 on 2021/11/24.
-//
-
-import Foundation
-
-
-class BFRecordManager: NSObject {
-    
-    /// 录制音频
-    func startRecord(){
-        
-    }
-    
-    /// 取消音频录制
-    func cancleRecord(){
-        
-    }
-    
-    /// 结束音频录制
-    func endRecord(){
-        
-    }
-}

+ 6 - 1
BFRecordScreenKit.podspec

@@ -29,7 +29,10 @@ TODO: Add long description of the pod here.
   # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
 
   s.ios.deployment_target = '10.0'
-
+  s.static_framework = true
+  s.requires_arc = true
+  s.swift_versions = ['5.0']
+  
   s.source_files = 'BFRecordScreenKit/Classes/**/*'
   s.resource_bundles = {
      'BFRecordScreenKit_Resources' => ['BFRecordScreenKit/Assets/*']
@@ -41,6 +44,8 @@ TODO: Add long description of the pod here.
   # s.public_header_files = 'Pod/Classes/**/*.h'
   # s.frameworks = 'UIKit', 'MapKit'
   s.dependency 'BFCommonKit'
+  s.dependency 'BFFramework'
+  s.dependency 'BFVideoEditKit'
   s.dependency 'BFUIKit'
   s.dependency 'GPUImage'
 

+ 1 - 1
BFRecordScreenKit/Classes/BFRSComm.swift

@@ -16,5 +16,5 @@ public func imageInRecordScreenKit(by name: String) -> UIImage? {
 }
 
 func currentBundle() -> Bundle? {
-    return Bundle.current(moduleName: "BFRecordScreenKit", isAssets: true)
+    return Bundle.current(moduleName: "BFRecordScreenKit", isAssets: false)
 }

+ 97 - 0
BFRecordScreenKit/Classes/BFRecordExport.swift.swift

@@ -0,0 +1,97 @@
+//
+//  BFRecordExport.swift.swift
+//  BFRecordScreenKit
+//
+//  Created by 胡志强 on 2021/11/25.
+//
+
+import Foundation
+import AVFoundation
+import BFFramework
+
+public class BFRecordExport {
+    public var progress : ((Float)->Void)?
+    var count = 0
+    var timerr : Timer?
+    
+    var asset:AVAsset?
+    var voiceList:[PQVoiceModel]? {
+        didSet {
+            audioAsset = voiceList?.map({ model in
+                AVURLAsset(url: URL(fileURLWithPath: model.wavFilePath))
+            })
+            
+        }
+    }
+    var audioAsset : [AVURLAsset]?
+    
+    var exporter : PQCompositionExporter?
+    var mStickers = [PQEditVisionTrackMaterialsModel]()
+    
+    deinit {
+        timerr?.invalidate()
+        timerr = nil
+    }
+    public init(){}
+
+    
+    //MARK: -
+    public func start(){
+        guard timerr == nil else {
+            return
+        }
+
+        timerr = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
+        RunLoop.current.add(timerr!, forMode: .common)
+        timerr?.fire()
+        
+        
+        // 1,背景视频素材
+        let bgMovieInfo: PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel()
+        bgMovieInfo.type = StickerType.VIDEO.rawValue
+        bgMovieInfo.locationPath = (asset as? AVURLAsset)?.url.absoluteString ?? ""
+        bgMovieInfo.timelineIn = 0
+        bgMovieInfo.timelineOut = CMTimeGetSeconds(asset?.duration ?? CMTime.zero)
+        bgMovieInfo.model_in = bgMovieInfo.timelineIn
+        bgMovieInfo.out = bgMovieInfo.timelineOut
+        bgMovieInfo.canvasFillType = stickerContentMode.aspectFitStr.rawValue
+        mStickers.append(bgMovieInfo)
+    
+        beginExport(videoStickers: mStickers, audioAsset: [(self.asset as! AVURLAsset)])
+    }
+    
+    func beginExport(videoStickers:[PQEditVisionTrackMaterialsModel], audioAsset: [AVURLAsset]) {
+ 
+        
+    }
+    
+    func dealAsset(){
+//        asset?.tracks.first(where: { track in
+//            if track.mediaType == .audio{
+//
+//            }
+//        })
+    }
+    
+    @objc func updateCounter() {
+        if self.count == 100 {
+            timerr?.invalidate()
+            timerr = nil
+            return
+        }
+        self.count += 1
+        self.progress?(Float(self.count)/100.0)
+    }
+    
+    func getVideoSize() -> CGSize{
+        var size = CGSize.zero
+        self.asset?.tracks.forEach({ track in
+            if track.mediaType == .video{
+                let realSize = __CGSizeApplyAffineTransform(track.naturalSize, track.preferredTransform)
+                size = CGSize(width: abs(realSize.width), height: abs(realSize.height))
+            }
+        })
+        
+        return size
+    }
+}

+ 167 - 0
BFRecordScreenKit/Classes/BFRecordManager.swift

@@ -0,0 +1,167 @@
+//
+//  BFRecordManager.swift
+//  BFRecordScreenKit
+//
+//  Created by 胡志强 on 2021/11/24.
+//
+
+import Foundation
+import BFCommonKit
+import BFFramework 
+import BFVideoEditKit
+
+class BFRecordManager {
+    
+    // 录音相关
+    var audioRecorder: NXAudioRecorder?
+    var limitedDuration:Double = 600       // 限制录制时长
+    var endRecordHandle : ((Bool, PQVoiceModel?) -> Void)?
+    var cancelRecordHandle : ((Error?) -> Void)?
+
+    var recorderFilePath : String = ""
+    var beginRecordTime:Date = Date()
+    var voiceModel : PQVoiceModel?
+    
+    init(voideModeL:PQVoiceModel) {
+        self.voiceModel = voideModeL
+    }
+    /// 录制音频
+    func startRecord(index:Int){
+        
+        recorderFilePath = exportAudiosDirectory
+
+        if !directoryIsExists(dicPath: recorderFilePath) {
+            BFLog(message: "文件夹不存在 \(recorderFilePath)")
+            createDirectory(path: recorderFilePath)
+        }
+        recorderFilePath.append("recorder_\(index)_\(Date().timeIntervalSince1970).wav")
+        BFLog(1, message: "开始录音 \(recorderFilePath)")
+        do {
+            try audioRecorder = NXAudioRecorder(path: recorderFilePath)
+
+        } catch {
+            BFLog(message: "录音准备失败,当前无法录音 \(error))")
+            self.cancelRecordHandle?(error)
+            return
+        }
+        var count: Int = 0
+        audioRecorder?.recorderProgross = { [weak self] timer in
+            // 所有小段加在一起 > 10 min 自动取消
+            let sumTime = timer + (self?.getAudioFileslDuration() ?? 0)
+//            BFLog(1, message: "timer === \(timer),sumTime == \(sumTime)")
+//            27.0003125,sumTime == 27.0003125
+            if sumTime >= (self?.limitedDuration ?? 600) {
+                cShowHUB(superView: nil, msg: "最长可录音10分钟")
+                self?.stopRecord(isCancel: false)
+                return
+            }
+
+//            self?.timeLab.text = sumTime.formatDurationToMS()
+
+            count = count + 1
+            BFLog(message: "count == \(count)")
+            
+        }
+
+        audioRecorder?.startRecord()
+        beginRecordTime = Date()
+
+//        beginRecordTime = Date().timeIntervalSince1970
+    }
+    
+    /// 取消音频录制
+    func cancleRecord(){
+        stopRecord(isCancel: true)
+        cancelRecordHandle?(nil)
+    }
+    
+    /// 结束音频录制
+    func endRecord(){
+        stopRecord(isCancel: false)
+    }
+    
+    // 取所有声音文件的时长
+    func getAudioFileslDuration() -> Float64 {
+        let duration: Float64 = 0.0
+//        if recorderPart.cacheRecorderCount == 0 { return duration }
+//        for fileURL in recorderPart.cacheRecorderFiles {
+//            duration = duration + AVURLAsset(url: fileURL, options: avAssertOptions).duration.seconds
+//            BFLog(message: "duration === \(duration)")
+//        }
+        return duration
+    }
+    
+    /// 停止录制 1,正常停止 2,取消停止
+    /// - Parameter isCancel: 是否为取消
+    func stopRecord(isCancel: Bool) {
+        if !(audioRecorder?.recorder.isRecording ?? false) {
+            BFLog(message: "不是录制状态")
+            return
+        }
+
+        audioRecorder?.stopRecord { [weak self] isSuccess, url in
+            guard let strongSelf = self else { return }
+
+            if strongSelf.getAudioFileslDuration() < 599, (Date().timeIntervalSince( strongSelf.beginRecordTime)) < 1 {
+                cShowHUB(superView: nil, msg: "说话时间太短")
+            }
+
+            let duration = Date().timeIntervalSince( strongSelf.beginRecordTime)
+            if isSuccess && !isCancel && duration > 1 {
+                BFLog(message: "结束录音  结果:\(isSuccess) \n url is \(url)")
+
+                // 处理降噪
+                let noiseFilePath = url.replacingOccurrences(of: ".wav", with: "_noise_\(1)_.wav")
+                BFLog(message: "降噪后地址:\(noiseFilePath)")
+                NXNoiseReduction().denoise(url, outFile: noiseFilePath)
+                let model = PQVoiceModel()
+                model.wavFilePath = noiseFilePath
+                model.duration = String(format: "%f", duration)
+                self?.endRecordHandle?(true, model)
+
+//
+//                strongSelf.recorderPart.cacheRecorderFiles.append(URL(fileURLWithPath: noiseFilePath))
+
+                // 删除录制的原文件
+                do {
+                    try FileManager.default.removeItem(atPath: url)
+                    print("Success to remove recorder file. \(url)")
+                } catch {
+                    print("Failed to remove recorder file. \(url)")
+                }
+
+//                strongSelf.recorderPart.cacheRecorderCount = strongSelf.recorderPart.cacheRecorderCount + 1
+//                strongSelf.recorderPart.audioPowers.append(strongSelf.partGap)
+//
+//                strongSelf.mergeToMP3file()
+            }
+
+//            if strongSelf.recorderPart.cacheRecorderCount == 0 {
+//                strongSelf.timeLab.text = "0'"
+//                strongSelf.recorderPart.audioPowers = []
+//                strongSelf.showWater()
+//            }
+
+            // ui恢复
+
+//            if strongSelf.recorderPart.cacheRecorderCount > 0 {
+//                strongSelf.startBtn.setTitle("长按继续录制", for: .normal)
+//            } else {
+//                strongSelf.startBtn.setTitle("长按录音", for: .normal)
+//            }
+        }
+    }
+    // 合并 成 MP3文件
+    func mergeToMP3file() {
+//        isMergeIng = true
+//        PQPlayerViewModel.mergeAudios(urls: recorderPart.cacheRecorderFiles) { [weak self] completURL in
+//
+//            BFLog(message: "completURL is \(String(describing: completURL))")
+//
+//            if completURL?.absoluteString.count ?? 0 > 0 {
+//                self?.recorderPart.compliteMP3AudioFile = completURL?.relativePath ?? ""
+//                self?.isMergeIng = false
+//            }
+//        }
+    }
+}

+ 111 - 29
BFRecordScreenKit/Classes/BFRecordScreenController.swift

@@ -11,21 +11,64 @@ import BFUIKit
 import GPUImage
 import Photos
 import BFCommonKit
+import BFFramework
 
 public class BFRecordScreenController: BFBaseViewController {
     
+    public var nextActionHandle:(()->Void)?
+    public var closeActionHandle:(()->Void)?
     public var asset:PHAsset?
     
-    var audioPlayer:AVPlayer?       // 原视频音频播放
+    var assetPlayer:AVPlayer?       // 原视频音频播放器
+    lazy var recordPlayer:AVAudioPlayer = {// 录音音频播放器
+        let player = AVAudioPlayer()
+        player.volume = 1
+        return player
+        
+    }()
     var movie :GPUImageMovie?       // 视频预览
     var playView :GPUImageView?     // 视频展示视图
     var isDragingProgressSlder : Bool = false
     
+    //定义音频的编码参数
+    let recordSettings:[String : Any] = [AVSampleRateKey : 44100.0, //声音采样率
+                            AVFormatIDKey : kAudioFormatLinearPCM,  //编码格式
+                    AVNumberOfChannelsKey : 1,                      //采集音轨
+                  AVEncoderBitDepthHintKey: 16,                     // 位深
+                 AVEncoderAudioQualityKey : AVAudioQuality.medium.rawValue] //音频质量
+
+    
     // 录音相关
-    var record:AVAudioRecorder?
+    lazy var recorderManager : BFRecordManager = {
+        
+        let manager = BFRecordManager(voideModeL: PQVoiceModel())
+        manager.cancelRecordHandle = { error in
+            
+        }
+        manager.endRecordHandle = {[weak self] (isTimeout, model) in
+            if FileManager.default.fileExists(atPath: model?.wavFilePath ?? ""){
+                // 加入到语音数组里
+                while let m = self?.recordList.last{
+                    if m.startTime < model!.startTime {
+                        self?.recordList.removeLast()
+                    }else if m.endTime < model!.startTime {
+                        m.endTime = model!.startTime
+                    }else{
+                        break
+                    }
+                }
+                self?.recordList.append(model!)
+                self?.drewRecordProgessLable()
+            }
+            
+        }
+        return manager
+    }()
+
     var beginOnStartBtn:Bool = false
     var touchStart:CGPoint = CGPoint(x: 0, y: 0)
     var avplayerTimeObserver: NSKeyValueObservation?
+    var recordList:[PQVoiceModel] = [PQVoiceModel]()
 
     lazy var playBtn:UIButton = {
         let btn = UIButton(frame: view.bounds)
@@ -58,13 +101,16 @@ public class BFRecordScreenController: BFBaseViewController {
     
     lazy var progessSilde:BFPlayerSlider = {
         let sliderView = BFPlayerSlider()
-        let thbImage = UIImage(named: "icon_point")
+        let tjbV = UIView(frame: CGRect(x: 0, y: 0, width: 4, height: 16))
+        tjbV.backgroundColor = .white
+        let thbImage = tjbV.graphicsGetImage()//UIImage(named: "icon_point")
         sliderView.setMinimumTrackImage(thbImage, for: .normal)
         sliderView.setMaximumTrackImage(thbImage, for: .normal)
         sliderView.setThumbImage(thbImage, for: .highlighted)
         sliderView.setThumbImage(thbImage, for: .normal)
         sliderView.maximumTrackTintColor = UIColor.hexColor(hexadecimal: "#303030")
-        sliderView.minimumTrackTintColor = UIColor.hexColor(hexadecimal: "#FA6400")
+        sliderView.minimumTrackTintColor = UIColor.hexColor(hexadecimal: "#303030")
+//        sliderView.minimumTrackTintColor = UIColor.hexColor(hexadecimal: "#FA6400")
         sliderView.addTarget(self, action: #selector(sliderTouchBegan(sender:)), for: .touchDown)
         sliderView.addTarget(self, action: #selector(sliderTouchEnded(sender:)), for: .touchUpInside)
         sliderView.addTarget(self, action: #selector(sliderTouchEnded(sender:)), for: .touchUpOutside)
@@ -75,7 +121,7 @@ public class BFRecordScreenController: BFBaseViewController {
     lazy var closeBtn:UIButton = {
         let btn = UIButton(type: .custom)
         btn.setImage(imageInRecordScreenKit(by: "xx"), for: .normal)
-        btn.contentHorizontalAlignment = .right
+//        btn.contentHorizontalAlignment = .right
         btn.addTarget(self, action: #selector(backBtnClick), for: .touchUpInside)
         return btn
     }()
@@ -83,7 +129,7 @@ public class BFRecordScreenController: BFBaseViewController {
     lazy var nextBtn:UIButton = {
         let btn = UIButton(type: .custom)
         btn.setImage(imageInRecordScreenKit(by: "gou"), for: .normal)
-        btn.contentHorizontalAlignment = .left
+//        btn.contentHorizontalAlignment = .left
         btn.addTarget(self, action: #selector(nextAction), for: .touchUpInside)
         return btn
     }()
@@ -93,7 +139,10 @@ public class BFRecordScreenController: BFBaseViewController {
         cleanMovieTarget()
         NotificationCenter.default.removeObserver(self)
         avplayerTimeObserver?.invalidate()
-
+        recorderManager.stopRecord(isCancel: true)
+        assetPlayer?.pause()
+        recordPlayer.pause()
+        
     }
     
     public override func viewWillAppear(_ animated: Bool) {
@@ -120,7 +169,10 @@ public class BFRecordScreenController: BFBaseViewController {
         bottomeView.addSubview(closeBtn)
         bottomeView.addSubview(nextBtn)
         
-        _ = checkStatus()
+        if checkStatus() {
+            try? AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: .defaultToSpeaker)
+        }
+        
     }
     
     public override func viewWillLayoutSubviews() {
@@ -147,8 +199,8 @@ public class BFRecordScreenController: BFBaseViewController {
         }
         
         progessSilde.snp.makeConstraints { make in
-            make.left.equalTo(closeBtn.snp.right).offset(6)
-            make.right.equalTo(nextBtn.snp.left).offset(-6)
+            make.left.equalTo(closeBtn.snp.right).offset(16)
+            make.right.equalTo(nextBtn.snp.left).offset(-16)
             make.centerY.equalTo(closeBtn)
             make.height.equalTo(20)
         }
@@ -201,24 +253,32 @@ public class BFRecordScreenController: BFBaseViewController {
     @objc func startRecord(btn:UIButton){
         btn.setImage(imageInRecordScreenKit(by: "mic2"), for: .normal)
         BFLog(1, message: "start \(UIControl.Event.touchDown)")
+        let model = PQVoiceModel()
+        model.startTime = CMTimeGetSeconds(assetPlayer?.currentItem?.currentTime() ?? CMTime.zero)
+        recorderManager.voiceModel? = model
+        recorderManager.startRecord(index: recordList.count)
+        movie?.startProcessing()
+        assetPlayer?.volume = 0
+        assetPlayer?.play()
     }
     
     @objc func endRecord(btn:UIButton){
-        cancleRecord()
+        recordBtn.setImage(imageInRecordScreenKit(by: "mic1"), for: .normal)
         // 存储录音
+        recorderManager.endRecord()
+        pause()
     }
     
     func cancleRecord(){
         recordBtn.setImage(imageInRecordScreenKit(by: "mic1"), for: .normal)
-        BFLog(1, message: "cancel ")
     }
     @objc func close(){
         pause()
-        backBtnClick()
+        closeActionHandle?()
     }
     
     @objc func nextAction(){
-        pause()
+        nextActionHandle?()
     }
     
     @objc func playVideo(btn:UIButton){
@@ -233,14 +293,14 @@ public class BFRecordScreenController: BFBaseViewController {
     }
 
     @objc public func sliderTouchBegan(sender _: UISlider) {
-        isDragingProgressSlder = true
-        pause()
+//        isDragingProgressSlder = true
+//        pause()
     }
 
     @objc public func sliderTouchEnded(sender: UISlider) {
-        changeProgress(progress: sender.value)
-        isDragingProgressSlder = false
-        play()
+//        changeProgress(progress: sender.value)
+//        isDragingProgressSlder = false
+//        play()
     }
     
     // MARK: - 权限申请
@@ -259,7 +319,7 @@ public class BFRecordScreenController: BFBaseViewController {
                 remindView.confirmBtn.setTitle("去设置", for: .normal)
                 UIApplication.shared.keyWindow?.addSubview(remindView)
                 remindView.remindData = remindData
-                remindView.remindBlock = { [weak self] item, _ in
+                remindView.remindBlock = { item, _ in
                     if item.tag == 2 {
                         openAppSetting()
                     }
@@ -289,17 +349,29 @@ public class BFRecordScreenController: BFBaseViewController {
     }
     
     // MARK: - 音视频处理
+    func playRecord(at duration:CMTime){
+        if recordList.count > 0, let player = try? AVAudioPlayer(contentsOf: URL(fileURLWithPath: (recordList.first?.wavFilePath)!)) {
+            self.recordPlayer = player
+            self.recordPlayer.volume = 1
+            self.recordPlayer.play()
+        }else{
+            self.recordPlayer.pause()
+        }
+    }
     
     func play(){
         cShowHUB(superView: nil, msg: "开始播放")
+        assetPlayer?.volume = 0.5
         movie?.startProcessing()
-        audioPlayer?.play()
+        assetPlayer?.play()
+        playRecord(at: CMTime.zero)
     }
     
     func pause(){
         cShowHUB(superView: nil, msg: "暂停播放")
         movie?.cancelProcessing()
-        audioPlayer?.pause()
+        assetPlayer?.pause()
+        recordPlayer.pause()
     }
     
     func fetchVideo(){
@@ -383,12 +455,12 @@ public class BFRecordScreenController: BFBaseViewController {
     
     func setAudioPlay(item:AVPlayerItem){
 
-        if let playItem = audioPlayer?.currentItem {
+        if let playItem = assetPlayer?.currentItem {
             NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: playItem)
-            audioPlayer?.replaceCurrentItem(with: item)
+            assetPlayer?.replaceCurrentItem(with: item)
         }else {
-            audioPlayer = AVPlayer(playerItem: item)
-            avplayerTimeObserver = audioPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 10), queue: DispatchQueue.global()) {[weak self] time in
+            assetPlayer = AVPlayer(playerItem: item)
+            avplayerTimeObserver = assetPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 10), queue: DispatchQueue.global()) {[weak self] time in
              //    进度监控
                 BFLog(1, message: "curr:\(CMTimeGetSeconds(time))")
                 if CMTimeGetSeconds(item.duration) > 0, !(self?.isDragingProgressSlder ?? false) {
@@ -400,9 +472,9 @@ public class BFRecordScreenController: BFBaseViewController {
         }
 
         
-        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: audioPlayer?.currentItem, queue: .main) { [weak self] notify in
+        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: assetPlayer?.currentItem, queue: .main) { [weak self] notify in
             BFLog(message: "AVPlayerItemDidPlayToEndTime = \(notify)")
-            self?.audioPlayer?.seek(to: kCMTimeZero)
+            self?.assetPlayer?.seek(to: CMTime.zero)
             self?.playBtn.isSelected = false
         }
     }
@@ -419,7 +491,7 @@ public class BFRecordScreenController: BFBaseViewController {
     }
     
     func changeProgress(progress:Float) {
-        if let item = audioPlayer?.currentItem {
+        if let item = assetPlayer?.currentItem {
             let duration = CMTimeGetSeconds(item.duration)
             item.seek(to: CMTime(value: CMTimeValue(progress * Float(duration) * 100), timescale: 100)) { finished in
                 if finished{
@@ -428,6 +500,12 @@ public class BFRecordScreenController: BFBaseViewController {
             }
         }
     }
+    
+    //MARK: - 录音对应图像绘制
+    
+    func drewRecordProgessLable(){
+        
+    }
 }
 
 extension BFRecordScreenController:GPUImageMovieDelegate {
@@ -436,3 +514,7 @@ extension BFRecordScreenController:GPUImageMovieDelegate {
         
     }
 }
+
+extension BFRecordScreenController:AVAudioRecorderDelegate {
+    
+}

+ 12 - 6
Example/BFRecordScreenKit.xcodeproj/project.pbxproj

@@ -7,8 +7,9 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		4A426DA5274F982600B3733B /* VideoExportController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A426DA4274F982600B3733B /* VideoExportController.swift */; };
+		4A426DA7274FA4E000B3733B /* IntroduceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A426DA6274FA4E000B3733B /* IntroduceController.swift */; };
 		4A96F094274CF8DC008657DA /* PhotoVideoListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A96F093274CF8DC008657DA /* PhotoVideoListController.swift */; };
-		4A96F09C274DDD26008657DA /* blg.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 4A96F09B274DDD26008657DA /* blg.mp4 */; };
 		607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
 		607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; };
 		607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
@@ -22,8 +23,9 @@
 		142F0A79F3F81D559C975EA3 /* Pods-BFRecordScreenKit_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BFRecordScreenKit_Tests.debug.xcconfig"; path = "Target Support Files/Pods-BFRecordScreenKit_Tests/Pods-BFRecordScreenKit_Tests.debug.xcconfig"; sourceTree = "<group>"; };
 		1CDC9754F414D402A7A92B91 /* Pods-BFRecordScreenKit_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BFRecordScreenKit_Example.debug.xcconfig"; path = "Target Support Files/Pods-BFRecordScreenKit_Example/Pods-BFRecordScreenKit_Example.debug.xcconfig"; sourceTree = "<group>"; };
 		27B6EE71C97A2E92661B8CA2 /* BFRecordScreenKit.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = BFRecordScreenKit.podspec; path = ../BFRecordScreenKit.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
+		4A426DA4274F982600B3733B /* VideoExportController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoExportController.swift; sourceTree = "<group>"; };
+		4A426DA6274FA4E000B3733B /* IntroduceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntroduceController.swift; sourceTree = "<group>"; };
 		4A96F093274CF8DC008657DA /* PhotoVideoListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoVideoListController.swift; sourceTree = "<group>"; };
-		4A96F09B274DDD26008657DA /* blg.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = blg.mp4; sourceTree = "<group>"; };
 		5E394037E3C86B6D51A62719 /* Pods_BFRecordScreenKit_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BFRecordScreenKit_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		607FACD01AFB9204008FA782 /* BFRecordScreenKit_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BFRecordScreenKit_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -87,7 +89,8 @@
 				607FACD51AFB9204008FA782 /* AppDelegate.swift */,
 				607FACD71AFB9204008FA782 /* ViewController.swift */,
 				4A96F093274CF8DC008657DA /* PhotoVideoListController.swift */,
-				4A96F09B274DDD26008657DA /* blg.mp4 */,
+				4A426DA6274FA4E000B3733B /* IntroduceController.swift */,
+				4A426DA4274F982600B3733B /* VideoExportController.swift */,
 				607FACD91AFB9204008FA782 /* Main.storyboard */,
 				607FACDC1AFB9204008FA782 /* Images.xcassets */,
 				607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
@@ -210,7 +213,6 @@
 				607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */,
 				607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */,
 				607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */,
-				4A96F09C274DDD26008657DA /* blg.mp4 in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -250,7 +252,6 @@
 				"${BUILT_PRODUCTS_DIR}/AliyunOSSiOS/AliyunOSSiOS.framework",
 				"${BUILT_PRODUCTS_DIR}/BFCommonKit/BFCommonKit.framework",
 				"${BUILT_PRODUCTS_DIR}/BFNetRequestKit/BFNetRequestKit.framework",
-				"${BUILT_PRODUCTS_DIR}/BFRecordScreenKit/BFRecordScreenKit.framework",
 				"${BUILT_PRODUCTS_DIR}/BFUIKit/BFUIKit.framework",
 				"${BUILT_PRODUCTS_DIR}/FDFullscreenPopGesture/FDFullscreenPopGesture.framework",
 				"${BUILT_PRODUCTS_DIR}/GPUImage/GPUImage.framework",
@@ -274,7 +275,6 @@
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AliyunOSSiOS.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BFCommonKit.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BFNetRequestKit.framework",
-				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BFRecordScreenKit.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BFUIKit.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FDFullscreenPopGesture.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GPUImage.framework",
@@ -306,11 +306,15 @@
 				"${PODS_ROOT}/Target Support Files/Pods-BFRecordScreenKit_Example/Pods-BFRecordScreenKit_Example-resources.sh",
 				"${PODS_CONFIGURATION_BUILD_DIR}/BFFramework/BFFramework_Resources.bundle",
 				"${PODS_CONFIGURATION_BUILD_DIR}/BFMaterialKit/BFMaterialKit_Resources.bundle",
+				"${PODS_CONFIGURATION_BUILD_DIR}/BFRecordScreenKit/BFRecordScreenKit_Resources.bundle",
+				"${PODS_CONFIGURATION_BUILD_DIR}/BFVideoEditKit/BFVideoEditKit_Resources.bundle",
 			);
 			name = "[CP] Copy Pods Resources";
 			outputPaths = (
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/BFFramework_Resources.bundle",
 				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/BFMaterialKit_Resources.bundle",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/BFRecordScreenKit_Resources.bundle",
+				"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/BFVideoEditKit_Resources.bundle",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
@@ -326,7 +330,9 @@
 			files = (
 				4A96F094274CF8DC008657DA /* PhotoVideoListController.swift in Sources */,
 				607FACD81AFB9204008FA782 /* ViewController.swift in Sources */,
+				4A426DA7274FA4E000B3733B /* IntroduceController.swift in Sources */,
 				607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */,
+				4A426DA5274F982600B3733B /* VideoExportController.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 3 - 0
Example/BFRecordScreenKit/AppDelegate.swift

@@ -18,6 +18,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         // Override point for customization after application launch.
         if let tbc = self.window?.rootViewController as? UITabBarController {
             tbc.selectedIndex = 1
+            tbc.tabBar.barTintColor = .black
+            tbc.tabBar.backgroundColor = .black
+            tbc.tabBar.tintColor = .black
         }
         return true
     }

+ 36 - 0
Example/BFRecordScreenKit/IntroduceController.swift

@@ -0,0 +1,36 @@
+//
+//  IntroduceController.swift
+//  BFRecordScreenKit_Example
+//
+//  Created by 胡志强 on 2021/11/25.
+//  Copyright © 2021 CocoaPods. All rights reserved.
+//
+
+import Foundation
+import BFUIKit
+import BFRecordScreenKit
+import Photos
+
+class IntroduceController : BFBaseViewController {
+    
+    var asset:PHAsset?
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        let vc = BFRecordScreenController()
+        vc.asset = asset
+        vc.nextActionHandle = {
+            DispatchQueue.main.async { [weak self] in
+                let controller = VideoExportController()
+                self?.navigationController?.pushViewController(controller, animated: true)
+            }
+        }
+        vc.closeActionHandle = {
+            self.backBtnClick()
+        }
+        vc.view.frame = self.view.frame
+        addChildViewController(vc)
+        view.addSubview(vc.view)
+    }
+}

+ 1 - 1
Example/BFRecordScreenKit/PhotoVideoListController.swift

@@ -57,7 +57,7 @@ class PhotoVideoListController: BFBaseViewController {
     
     override func rightBtnClick(sender _: UIButton) {
         if let asset = self.chosedAsset{
-            let vc = BFRecordScreenController()
+            let vc = IntroduceController()
             vc.asset = asset
             navigationController?.pushViewController(vc, animated: true)
         }else{

+ 40 - 0
Example/BFRecordScreenKit/VideoExportController.swift

@@ -0,0 +1,40 @@
+//
+//  VideoExportController.swift
+//  BFRecordScreenKit_Example
+//
+//  Created by 胡志强 on 2021/11/25.
+//  Copyright © 2021 CocoaPods. All rights reserved.
+//
+
+import Foundation
+import BFRecordScreenKit
+import BFUIKit
+import UIKit
+
+class VideoExportController: BFBaseViewController{
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        let backV = UIView(frame: CGRect(x: 10, y: 100, width: cScreenWidth - 20, height: 30))
+        backV.backgroundColor = .gray
+        view.addSubview(backV)
+
+        let progressView = UIView(frame: CGRect(x: 10, y: 100, width: 0, height: 30))
+        progressView.backgroundColor = .red
+        view.addSubview(progressView)
+        
+        let la = UILabel(frame: backV.frame)
+        la.textColor = .white
+        la.text = "0%"
+        la.font = UIFont.systemFont(ofSize: 18)
+        view.addSubview(la)
+        
+        let export = BFRecordExport()
+        export.progress = { progress in
+            progressView.frame = CGRect(x: 10, y: 100, width: (cScreenWidth - 20) * CGFloat(progress), height: 30)
+            la.text = String(format: "%d", Int(progress*100))
+        }
+        export.start()
+    }
+}

+ 12 - 1
Example/Podfile

@@ -11,6 +11,17 @@ target 'BFRecordScreenKit_Example' do
   pod 'BFNetRequestKit',  :path => '../../BFNetRequestKit/'
   pod 'BFFramework',      :path => '../../BFFramework/'
   pod 'BFUIKit',          :path => '../../BFUIKit/'
+  pod 'BFVideoEditKit',   :path => '../../BFVideoEditKit/'
+  pod 'BFAliyunNlsSDK-Swift',   :path => '../../BFAliyunNlsSDK-Swift/'
+  
+  post_install do |installer|
+    installer.pods_project.targets.each do |target|
+      #if target.name =="App" || target.name =="App1"
+      target.build_configurations.each do |config|
+        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0'
+      end
+      #end
+    end
+  end
   
-  pod 'GPUImage'
 end

+ 21 - 4
Example/Podfile.lock

@@ -1,6 +1,7 @@
 PODS:
   - Alamofire (5.4.4)
   - AliyunOSSiOS (2.10.10)
+  - BFAliyunNlsSDK-Swift (0.1.0)
   - BFCommonKit (1.5.1):
     - BFCommonKit/BFBase (= 1.5.1)
     - BFCommonKit/BFCategorys (= 1.5.1)
@@ -45,7 +46,9 @@ PODS:
     - Alamofire (= 5.4.4)
   - BFRecordScreenKit (0.1.0):
     - BFCommonKit
+    - BFFramework
     - BFUIKit
+    - BFVideoEditKit
     - GPUImage
   - BFUIKit (0.1.1):
     - BFCommonKit
@@ -86,6 +89,13 @@ PODS:
     - RealmSwift (= 10.7.6)
     - SnapKit (~> 5.0)
     - SVProgressHUD (~> 2.0)
+  - BFVideoEditKit (0.1.0):
+    - BFAliyunNlsSDK-Swift
+    - BFCommonKit
+    - BFFramework
+    - BFMaterialKit
+    - BFNetRequestKit
+    - BFUIKit
   - Bugly (2.5.90)
   - FDFullscreenPopGesture (1.1)
   - GPUImage (0.1.7)
@@ -119,13 +129,14 @@ PODS:
   - WechatOpenSDK-Swift (1.8.7.1)
 
 DEPENDENCIES:
+  - BFAliyunNlsSDK-Swift (from `../../BFAliyunNlsSDK-Swift/`)
   - BFCommonKit (from `../../BFCommonKit/`)
   - BFFramework (from `../../BFFramework/`)
   - BFMaterialKit (from `../../BFMaterialKit/`)
   - BFNetRequestKit (from `../../BFNetRequestKit/`)
   - BFRecordScreenKit (from `../`)
   - BFUIKit (from `../../BFUIKit/`)
-  - GPUImage
+  - BFVideoEditKit (from `../../BFVideoEditKit/`)
 
 SPEC REPOS:
   https://github.com/CocoaPods/Specs.git:
@@ -151,6 +162,8 @@ SPEC REPOS:
     - WechatOpenSDK-Swift
 
 EXTERNAL SOURCES:
+  BFAliyunNlsSDK-Swift:
+    :path: "../../BFAliyunNlsSDK-Swift/"
   BFCommonKit:
     :path: "../../BFCommonKit/"
   BFFramework:
@@ -163,16 +176,20 @@ EXTERNAL SOURCES:
     :path: "../"
   BFUIKit:
     :path: "../../BFUIKit/"
+  BFVideoEditKit:
+    :path: "../../BFVideoEditKit/"
 
 SPEC CHECKSUMS:
   Alamofire: f3b09a368f1582ab751b3fff5460276e0d2cf5c9
   AliyunOSSiOS: b8f1dfc229cd9abf68c8ee0cb245c2d66e00dd96
-  BFCommonKit: 697a9d162da8ee14ec75cc0f5d8e26a38a6faafe
+  BFAliyunNlsSDK-Swift: 44049d173720cf858729d3b011c07e0c33c90fd2
+  BFCommonKit: fbebd7d46eaa7adaf5311aae2230b68ab5e99788
   BFFramework: f77b349fadfcee2ca279d7826b06f65d9e06e67f
   BFMaterialKit: a10f33e7748689a3eeffff3b18df9c350241ba8d
   BFNetRequestKit: 6b200205bd1a9491c04f5a3e95301d37a547f96b
-  BFRecordScreenKit: 1397a4e40a5b7a02bb2b495473858f4f56657867
+  BFRecordScreenKit: ebe9e2888a1a139274c807cf28171ca8112da9a4
   BFUIKit: 982c86edec8883b3e6b60cb3ee1297eae23d9e4a
+  BFVideoEditKit: 7f86556c7604f221a591abfb3d80739be03448c3
   Bugly: 88bc32c0acc6fef7b74d610f0319ee7560d6b9fe
   FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0
   GPUImage: 733a5f0fab92df9de1c37ba9df520a833ccb406d
@@ -192,6 +209,6 @@ SPEC CHECKSUMS:
   TXLiteAVSDK_Player: 300e6fc7262ae095ee13b18d7d821c5fae0996f9
   WechatOpenSDK-Swift: 18a8f7b12e745c30acc013f72a9f8a25aad6e216
 
-PODFILE CHECKSUM: 73db905959405c43b432632a6186a85549c89648
+PODFILE CHECKSUM: fecae16510aa8ec73993940aae423db47aa4af21
 
 COCOAPODS: 1.11.2