Browse Source

Merge branch 'master' of https://git.yishihui.com/iOS/BFRecordScreenKit
合并代码

jsonwang 3 years ago
parent
commit
c467d3f290

+ 57 - 59
BFRecordScreenKit/Classes/BFRecordExport.swift

@@ -13,13 +13,13 @@ import GPUImage
 import Photos
 import UIKit
 
-public enum ExportError : Int {
-    case FileNotExist   = -31001
-    case DataLost       = -31002
-    case VoiceLost      = -31003
-    case TotalDurError  = -31004
-    case ExportExcept   = -31005
-    case DiskNoSpace    = -31006
+public enum ExportError: Int {
+    case FileNotExist = -31001
+    case DataLost = -31002
+    case VoiceLost = -31003
+    case TotalDurError = -31004
+    case ExportExcept = -31005
+    case DiskNoSpace = -31006
 }
 
 let testFor = true
@@ -27,10 +27,10 @@ let testFor = true
 public class BFRecordExport {
     public var progress: ((Float) -> Void)?
     public var exportCompletion: ((Error?, URL?) -> Void)?
-    
-    public var originSoundVolumn : Float = 1.0            // 无录音时原声大小
-    public var originSoundInRecordVolumn : Float = 0.0    // 录音时原声大小
-    
+
+    public var originSoundVolumn: Float = 1.0 // 无录音时原声大小
+    public var originSoundInRecordVolumn: Float = 0.0 // 录音时原声大小
+
     public var data: [BFRecordItemModel]? {
         didSet {
             if data?.count ?? 0 > 0 {
@@ -69,25 +69,24 @@ public class BFRecordExport {
                 // 如果需要排序,则排视频的顺序;否则排音频的顺序
                 let needSort = false
                 if needSort {
-                    
-                }else{
+                } else {
                     // 音频排序
                     itemModel.voiceStickers.sort { m1, m2 in
-                        m1.startTime < m2.startTime
+                        m1.startCMTime.seconds < m2.startCMTime.seconds
                     }
                     // 字幕排序
                     itemModel.titleStickers.sort { model1, model2 in
                         model1.timelineIn < model2.timelineIn
                     }
                 }
-                
+
                 if itemModel.mediaType == .IMAGE {
                     // 图片素材
-                    if !synthesisAll && itemModel.voiceStickers.count == 0 {
+                    if !synthesisAll, itemModel.voiceStickers.count == 0 {
                         // 图片无录音在保留模式里不合成
                         continue
                     }
-                    
+
                     var duration = itemModel.materialDuraion
                     if itemModel.voiceStickers.count == 0 {
                         // 图片无录音保持2s
@@ -99,7 +98,7 @@ public class BFRecordExport {
 //                        voice.voiceType = VOICETYPT.None.rawValue
                         voice.volumeGain = 100
                         voiceList.append(voice)
-                    }else{
+                    } else {
                         //
                         for mod in itemModel.voiceStickers {
                             let sticker = PQEditVisionTrackMaterialsModel()
@@ -114,14 +113,14 @@ public class BFRecordExport {
                             voiceList.append(sticker)
                         }
                     }
-                    
+
                     let sticker = splitBaseMaterial(timelineIn: totalDur, model_in: 0, duration: duration)
                     sticker.originalData = itemModel.coverImg?.pngData()
                     sticker.volumeGain = 0
                     sticker.type = StickerType.IMAGE.rawValue
                     videoStickers.append(sticker)
                     BFLog(1, message: "image sticker - timIn:\(sticker.timelineIn), modIn:\(sticker.model_in), dur:\(duration)")
-                    
+
                     for titleS in itemModel.titleStickers {
 //                        let leng = titleS.timelineOut - titleS.timelineIn
                         let newTitleSticker = PQEditSubTitleModel()
@@ -131,11 +130,11 @@ public class BFRecordExport {
                         newTitleSticker.timelineIn = totalDur + titleS.timelineIn
                         newTitleSticker.timelineOut = totalDur + titleS.timelineOut
                     }
-                    
+
                     totalDur += duration
                     continue
                 }
-                
+
                 // 视频处理
                 if let localPath = itemModel.localPath {
                     if !FileManager.default.fileExists(atPath: localPath) {
@@ -143,9 +142,9 @@ public class BFRecordExport {
                         exportCompletion?(error as Error, nil)
                         return
                     }
-                   
+
 //                    voiceList.append(contentsOf: itemModel.voiceStickers)
-                    
+
                     if synthesisAll {
                         var subDur = 0.0
                         let drangs = itemModel.dealedDurationRanges
@@ -153,15 +152,15 @@ public class BFRecordExport {
                             let range = srange.range
                             let sticker = splitBaseMaterial(timelineIn: totalDur + subDur, model_in: range.start.seconds, duration: range.duration.seconds)
                             sticker.locationPath = localPath
-                            sticker.volumeGain = Float64(srange.isRecord ? originSoundInRecordVolumn*100 : originSoundVolumn*100)
+                            sticker.volumeGain = Float64(srange.isRecord ? originSoundInRecordVolumn * 100 : originSoundVolumn * 100)
                             videoStickers.append(sticker)
                             subDur += range.duration.seconds
-                            
+
                             if srange.isRecord {
                                 // 处理voice
                                 if let mod = itemModel.voiceStickers.first(where: { m in
-                                    m.startTime == range.start.seconds
-                                }){
+                                    m.startCMTime.seconds == range.start.seconds
+                                }) {
                                     let sticker = PQEditVisionTrackMaterialsModel()
                                     sticker.model_in = 0
                                     sticker.out = mod.endCMTime.seconds - mod.startCMTime.seconds
@@ -175,7 +174,7 @@ public class BFRecordExport {
                                 }
                             }
                         }
-                        
+
                         for titleS in itemModel.titleStickers {
                             let newTitleSticker = PQEditSubTitleModel()
                             titleStickers.append(newTitleSticker)
@@ -184,7 +183,7 @@ public class BFRecordExport {
                             newTitleSticker.timelineIn = totalDur + titleS.timelineIn
                             newTitleSticker.timelineOut = totalDur + titleS.timelineOut
                         }
-                        
+
                         totalDur += subDur
                     } else {
                         // 只保留录音部分
@@ -192,20 +191,20 @@ public class BFRecordExport {
                         var drangs = itemModel.dealedDurationRanges.filter { srange in
                             srange.isRecord == true
                         }
-                        
+
                         if needSort {
                             drangs.sort { range1, range2 in
                                 range1.index < range2.index
                             }
                         }
-                        
+
                         for (index, srange) in drangs.enumerated() {
                             let range = srange.range
                             let sticker = splitBaseMaterial(timelineIn: totalDur + subDur, model_in: range.start.seconds, duration: range.duration.seconds)
                             sticker.locationPath = localPath
-                            sticker.volumeGain = Float64(srange.isRecord ? originSoundInRecordVolumn*100 : originSoundVolumn*100)
+                            sticker.volumeGain = Float64(srange.isRecord ? originSoundInRecordVolumn * 100 : originSoundVolumn * 100)
                             videoStickers.append(sticker)
-                            
+
                             let voiceSticker = itemModel.voiceStickers[index]
                             let voice = PQEditVisionTrackMaterialsModel()
                             voice.model_in = 0
@@ -217,10 +216,10 @@ public class BFRecordExport {
                             voice.locationPath = voiceSticker.wavFilePath
                             voice.volumeGain = 100 // Float64(model.volume)
                             voiceList.append(voice)
-                            
-                            let titleModels = itemModel.titleStickers.filter({ mod in
+
+                            let titleModels = itemModel.titleStickers.filter { mod in
                                 mod.audioFilePath == voiceSticker.wavFilePath
-                            })
+                            }
                             for titleS in titleModels {
                                 let newTitleSticker = PQEditSubTitleModel()
                                 titleStickers.append(newTitleSticker)
@@ -231,13 +230,12 @@ public class BFRecordExport {
                                 BFLog(1, message: "timein - \(newTitleSticker.timelineIn)")
                             }
                             subDur += range.duration.seconds
-
                         }
                         totalDur += subDur
                     }
                 }
             }
-            beginExport(synthesisAll: synthesisAll, videoStickers: videoStickers, voiceList:voiceList, titleStickers:titleStickers)
+            beginExport(synthesisAll: synthesisAll, videoStickers: videoStickers, voiceList: voiceList, titleStickers: titleStickers)
         }
     }
 
@@ -319,17 +317,17 @@ public class BFRecordExport {
         guard let totalDuration = data?.reduce(0.0, { partialResult, itemModell in
             var modelDuraion = 0.0
             if itemModell.mediaType == .IMAGE {
-                if itemModell.voiceStickers.count == 0 && synthesisAll {
+                if itemModell.voiceStickers.count == 0, synthesisAll {
                     modelDuraion += 2
-                }else {
+                } else {
                     modelDuraion = itemModell.materialDuraion
                 }
-            }else if itemModell.mediaType == .VIDEO{
+            } else if itemModell.mediaType == .VIDEO {
                 modelDuraion = itemModell.dealedDurationRanges.reduce(0.0) { partialResult, srange in
 //                    partialResult + (!synthesisAll && srange.isRecord) ?
                     if synthesisAll {
                         return partialResult + srange.range.duration.seconds
-                    }else {
+                    } else {
                         return partialResult + (srange.isRecord ? srange.range.duration.seconds : 0)
                     }
                 }
@@ -340,31 +338,31 @@ public class BFRecordExport {
             exportCompletion?(error as Error, nil)
             return
         }
+
         // MARK: - 声音合成
+
         // 有录音操作或者多个视频,就会进入合成步骤,否则就是一个没有处理的素材,直接导出就行了
         if voiceCount > 0 || videoStickers.count > 1 {
             let (audioMix, composition) = mergeAudio(videoStickers: videoStickers, audios: voiceList, synthesisAll: synthesisAll)
 
-            var filters:[PQBaseFilter] =  Array.init()
+            var filters: [PQBaseFilter] = Array()
             for sticker in videoStickers {
                 if sticker.type == StickerType.IMAGE.rawValue {
                     filters.append(PQImageFilter(sticker: sticker))
-                }else if sticker.type == StickerType.VIDEO.rawValue {
+                } else if sticker.type == StickerType.VIDEO.rawValue {
                     filters.append(PQMovieFilter(movieSticker: sticker))
                 }
-                
             }
-            let outputSize:CGSize = CGSize(width: 1080, height: Int(1080 * CGFloat(UIScreen.main.bounds.size.height / UIScreen.main.bounds.size.width)))
+            let outputSize: CGSize = CGSize(width: 1080, height: Int(1080 * CGFloat(UIScreen.main.bounds.size.height / UIScreen.main.bounds.size.width)))
             BFLog(message: "输出视频大小:\(outputSize)")
-            
-            //add by ak 有字幕数据 & 显示字幕开关打开 添加字幕filter
-            if(titleStickers.count > 0 && ( titleStickers.first?.setting.subtitleIsShow ?? true)){
-                filters.append(PQSubTitleFilter.init(st: titleStickers, inputSize: outputSize))
+
+            // add by ak 有字幕数据 & 显示字幕开关打开 添加字幕filter
+            if titleStickers.count > 0, titleStickers.first?.setting.subtitleIsShow ?? true {
+                filters.append(PQSubTitleFilter(st: titleStickers, inputSize: outputSize))
             }
-            
-            
+
             exporter = PQCompositionExporter(asset: composition, videoComposition: nil, audioMix: audioMix, filters: filters, animationTool: nil, exportURL: outPutMP4URL)
- 
+
             var orgeBitRate = Int(outputSize.width * outputSize.height * 3)
 
             for stick in videoStickers {
@@ -378,12 +376,12 @@ public class BFRecordExport {
                 }
             }
 
-            BFLog(1, message: String(format: "导出设置的码率为:%.3f MB", Double(orgeBitRate)/1024.0/1024.0/8.0))
-            let preSize = Double(orgeBitRate) * totalDuration / (1024*1024*8)
+            BFLog(1, message: String(format: "导出设置的码率为:%.3f MB", Double(orgeBitRate) / 1024.0 / 1024.0 / 8.0))
+            let preSize = Double(orgeBitRate) * totalDuration / (1024 * 1024 * 8)
             let freeSize = PQBridgeObject.getPhoneDiskFreeSize()
             if preSize + 100.0 > freeSize { // 存储完后磁盘剩余至少100M
-                let error = NSError(domain: "err", code: ExportError.DiskNoSpace.rawValue, userInfo: ["msg":"需要\(Int(preSize))MB,可用\(Int(freeSize))MB"])
-                self.exportCompletion?(error as Error, nil)
+                let error = NSError(domain: "err", code: ExportError.DiskNoSpace.rawValue, userInfo: ["msg": "需要\(Int(preSize))MB,可用\(Int(freeSize))MB"])
+                exportCompletion?(error as Error, nil)
                 return
             }
             let tempBeginExport = Date().timeIntervalSince1970
@@ -412,7 +410,7 @@ public class BFRecordExport {
                     self?.exportCompletion?(nil, url)
 
                 } else {
-                    let error = NSError(domain: "err", code: ExportError.ExportExcept.rawValue, userInfo: ["msg":"导出异常失败"])
+                    let error = NSError(domain: "err", code: ExportError.ExportExcept.rawValue, userInfo: ["msg": "导出异常失败"])
                     self?.exportCompletion?(error as Error, nil)
                     cShowHUB(superView: nil, msg: "导出失败")
                 }

+ 3 - 5
BFRecordScreenKit/Classes/BFRecordItemModel.swift

@@ -117,16 +117,14 @@ public class BFRecordItemModel: NSObject {
 
         var list: [PQVoiceModel]
         list = voiceStickers.sorted { model1, model2 in
-            model1.startTime < model2.startTime
+            model1.startCMTime.seconds < model2.endCMTime.seconds
         }
 
         for model in list {
-            if model.startTime > start {
-                //
-                let range = CMTimeRange(start: CMTime(seconds: start, preferredTimescale: 1000), duration: CMTime(seconds: model.startTime - start, preferredTimescale: 1000))
+            if model.startCMTime.seconds > start {
+                let range = CMTimeRange(start: CMTime(seconds: start, preferredTimescale: 1000), duration: CMTime(seconds: model.startCMTime.seconds - start, preferredTimescale: 1000))
                 dealedDurationRanges.append(SplitRecordRange(isRecord: false, range: range, index: -1))
             }
-
             let ind = voiceStickers.firstIndex(of: model)
             let range = CMTimeRange(start: model.startCMTime, end: model.endCMTime)
             dealedDurationRanges.append(SplitRecordRange(isRecord: true, range: range, index: ind ?? -1))

+ 6 - 3
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenController.swift

@@ -1369,7 +1369,6 @@ public class BFRecordScreenController: BFBaseViewController {
             return
         }
         BFLog(1, message: "当前时间:\(CMTimeGetSeconds(currentT)), 找到的音频:\(recordedAudio.startCMTime.seconds) ~ \(recordedAudio.endCMTime.seconds), \(recordedAudio.wavFilePath ?? "")")
-
         // 创建播放器
         if recordPlayer == nil || (recordPlayer?.currentItem?.asset as? AVURLAsset)?.url.lastPathComponent != (recordedAudio.wavFilePath as NSString?)?.lastPathComponent {
             let newItem = AVPlayerItem(url: URL(fileURLWithPath: recordedAudio.wavFilePath))
@@ -1396,8 +1395,8 @@ public class BFRecordScreenController: BFBaseViewController {
                 didPlayToEndTime((shouldPlayRecordIndex, recordedAudio) as? (Int, PQVoiceModel), newItem)
             }
             avplayerTimeObserver?.invalidate()
-            avplayerTimeObserver = recordPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 1000), queue: DispatchQueue.global()) { [weak self] time in
-                BFLog(3, message: "当前播放---\(time),\(time.seconds)")
+            avplayerTimeObserver = recordPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 1000), queue: DispatchQueue.global()) { [weak self, weak recordPlayer] time in
+                BFLog(3, message: "当前播放---\(time),\(time.seconds),\(recordPlayer?.currentItem?.currentTime().seconds ?? 0),\(recordPlayer?.currentItem?.duration.seconds ?? 0)")
                 if CMTimeGetSeconds(self?.currenStartPlayTime ?? CMTime.zero) <= 0 {
                     BFLog(message: "重新更新开始播放进度\(#function)-\(self?.currenStartPlayTime.seconds ?? 0)")
                     self?.currenStartPlayTime = time
@@ -1405,6 +1404,10 @@ public class BFRecordScreenController: BFBaseViewController {
                 let progressTime = CMTime(seconds: CMTimeGetSeconds(time) - CMTimeGetSeconds(self?.currenStartPlayTime ?? CMTime.zero), preferredTimescale: 1000)
                 BFLog(message: "progressTime进度\(#function)-\(progressTime.seconds)")
                 periodicTimeObserver(progressTime, newItem)
+                if (recordPlayer?.currentItem?.currentTime().seconds ?? 0) > (recordPlayer?.currentItem?.duration.seconds ?? 0) - 0.1 {
+                    recordPlayer?.volume = 0
+                    self?.assetPlayer?.volume = self?.noSpeakVolume ?? 0
+                }
             } as? NSKeyValueObservation
         }
         if itemModels[currItemModelIndex].mediaType == .VIDEO {

+ 36 - 34
BFRecordScreenKit/Classes/RecordScreen/View/BFAudioSettingView.swift

@@ -5,19 +5,18 @@
 //  Created by ak on 2021/12/15.
 //  功能:设置视频素材的音量
 
-import Foundation
-import BFMediaKit
 import BFCommonKit
+import BFMediaKit
 import BFUIKit
+import Foundation
 
-typealias BFAudioSettingViewCallBack = (_ haveSpeak: Float,_ noHaveSpeak:Float) -> Void
+typealias BFAudioSettingViewCallBack = (_ haveSpeak: Float, _ noHaveSpeak: Float) -> Void
 
 class BFAudioSettingView: UIView {
-
     var callBack: BFAudioSettingViewCallBack?
-    
-    //操作板背景
-    let backView = UIView.init()
+
+    // 操作板背景
+    let backView = UIView()
     // 标题
     public lazy var haveSpeakLab: UILabel = {
         let haveSpeakLab = UILabel()
@@ -28,8 +27,8 @@ class BFAudioSettingView: UIView {
         haveSpeakLab.backgroundColor = .clear
         return haveSpeakLab
     }()
-    
-    //有录音
+
+    // 有录音
     lazy var haveSpeakSlider: BFUISlider = {
         let haveSpeakSlider = BFUISlider()
         let thbImage = UIImage.moduleImage(named: BFConfig.shared.silderPinUsedImageName, moduleName: "BFFramework", isAssets: false)
@@ -47,10 +46,14 @@ class BFAudioSettingView: UIView {
         haveSpeakSlider.isMovePoint = false
         haveSpeakSlider.valueTextColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
         haveSpeakSlider.value = 0.0
-
+        haveSpeakSlider.valueChangedHandle = { [weak self] sender in
+            if self?.callBack != nil {
+                self?.callBack!(sender.value, self?.noSpeakSlider.value ?? 0)
+            }
+        }
         return haveSpeakSlider
     }()
-    
+
     // 标题
     public lazy var noSpeakLab: UILabel = {
         let noSpeakLab = UILabel()
@@ -60,7 +63,7 @@ class BFAudioSettingView: UIView {
         noSpeakLab.textColor = .white
         return noSpeakLab
     }()
-    
+
     lazy var noSpeakSlider: BFUISlider = {
         let noSpeakSlider = BFUISlider()
         let thbImage = UIImage.moduleImage(named: BFConfig.shared.silderPinUsedImageName, moduleName: "BFFramework", isAssets: false)
@@ -78,17 +81,20 @@ class BFAudioSettingView: UIView {
         noSpeakSlider.unit = "%"
         noSpeakSlider.valueTextColor = UIColor.hexColor(hexadecimal: BFConfig.shared.styleColor.rawValue)
         noSpeakSlider.value = 100
-
+        noSpeakSlider.valueChangedHandle = { [weak self] sender in
+            if self?.callBack != nil {
+                self?.callBack!(self?.haveSpeakSlider.value ?? 0, sender.value)
+            }
+        }
         return noSpeakSlider
     }()
 
     override init(frame: CGRect) {
         super.init(frame: frame)
-        
-        self.backgroundColor = UIColor.clear
+
+        backgroundColor = UIColor.clear
         addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hidden(tap:))))
-        
-       
+
         backView.backgroundColor = .black
         addSubview(backView)
         backView.snp.makeConstraints { make in
@@ -97,14 +103,12 @@ class BFAudioSettingView: UIView {
             make.width.equalTo(cScreenWidth)
             make.height.equalTo(220)
         }
- 
-        
+
         backView.addSubview(haveSpeakSlider)
         backView.addSubview(noSpeakSlider)
         backView.addSubview(haveSpeakLab)
         backView.addSubview(noSpeakLab)
-        
-        
+
         haveSpeakLab.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(16)
             make.right.equalToSuperview().offset(-16)
@@ -115,7 +119,7 @@ class BFAudioSettingView: UIView {
             make.right.equalToSuperview().offset(-16)
             make.top.equalTo(haveSpeakLab).offset(24)
         }
-        
+
         noSpeakLab.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(16)
             make.right.equalToSuperview().offset(-16)
@@ -127,24 +131,22 @@ class BFAudioSettingView: UIView {
             make.top.equalTo(noSpeakLab).offset(24)
         }
     }
-    
+
     override func layoutSubviews() {
         super.layoutSubviews()
-        self.backView.addCorner(roundingCorners: [.topLeft, .topRight], corner: 10)
+        backView.addCorner(roundingCorners: [.topLeft, .topRight], corner: 10)
     }
-    
-    required init?(coder: NSCoder) {
+
+    required init?(coder _: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
-    
-    @objc func hidden(tap:UITapGestureRecognizer){
-        if !backView.bounds.contains(tap.location(in: backView)){
-            self.isHidden = true
-            if(callBack != nil){
-                callBack!(haveSpeakSlider.value,noSpeakSlider.value)
+
+    @objc func hidden(tap: UITapGestureRecognizer) {
+        if !backView.bounds.contains(tap.location(in: backView)) {
+            isHidden = true
+            if callBack != nil {
+                callBack!(haveSpeakSlider.value, noSpeakSlider.value)
             }
         }
     }
-    
 }
-