ソースを参照

Merge branch 'demo'

* demo:
  实现抛留功能
  修改撤销逻辑
  抛留时间段设置; 多素材准备
  多段录音的逻辑及UI渲染
  修改录音指示条;

# Conflicts:
#	BFRecordScreenKit/Classes/BFIntroduceToolView.swift
#	BFRecordScreenKit/Classes/BFVideoThumbProgressView.swift
#	BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenController.swift
#	BFRecordScreenKit/Classes/RecordScreen/View/BFIntroduceToolView.swift
#	BFRecordScreenKit/Classes/RecordScreen/View/BFVideoThumbProgressView.swift
#	BFRecordScreenKit/Classes/View/BFIntroduceToolView.swift
#	BFRecordScreenKit/Classes/View/BFVideoThumbProgressView.swift
harry 3 年 前
コミット
dfd7af3cc1
23 ファイル変更489 行追加267 行削除
  1. 1 1
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveall_h.imageset/Contents.json
  2. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveall_h.imageset/export_saveall_h@3x.png
  3. 1 1
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveall_n.imageset/Contents.json
  4. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveall_n.imageset/export_saveall_n@3x.png
  5. 1 1
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveonly_h.imageset/Contents.json
  6. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveonly_h.imageset/export_saveonly_h@3x.png
  7. 21 0
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveonly_n.imageset/Contents.json
  8. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveonly_n.imageset/export_saveonly_n@3x.png
  9. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/gou.imageset/gou@3x.png
  10. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/mic1.imageset/mic1@3x.png
  11. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/mic2.imageset/mic2@3x.png
  12. 1 1
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/preview_play.imageset/Contents.json
  13. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/preview_play.imageset/preview_play@3x.png
  14. 21 0
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/preview_sound_set.imageset/Contents.json
  15. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/preview_sound_set.imageset/preview_sound_set@3x.png
  16. BIN
      BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/xx.imageset/xx@3x.png
  17. 4 3
      BFRecordScreenKit/Classes/BFRSComm.swift
  18. 150 100
      BFRecordScreenKit/Classes/BFRecordExport.swift
  19. 50 0
      BFRecordScreenKit/Classes/BFRecordItemModel.swift
  20. 0 17
      BFRecordScreenKit/Classes/BFVoiceRecordManager.swift
  21. 204 127
      BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenController.swift
  22. 2 4
      BFRecordScreenKit/Classes/RecordScreen/View/BFSubtitleSettingView.swift
  23. 33 12
      BFRecordScreenKit/Classes/RecordScreen/View/BFVideoThumbProgressView.swift

+ 1 - 1
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/mic2.imageset/Contents.json → BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveall_h.imageset/Contents.json

@@ -9,7 +9,7 @@
       "scale" : "2x"
     },
     {
-      "filename" : "mic2@3x.png",
+      "filename" : "export_saveall_h@3x.png",
       "idiom" : "universal",
       "scale" : "3x"
     }

BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveall_h.imageset/export_saveall_h@3x.png


+ 1 - 1
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/mic1.imageset/Contents.json → BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveall_n.imageset/Contents.json

@@ -9,7 +9,7 @@
       "scale" : "2x"
     },
     {
-      "filename" : "mic1@3x.png",
+      "filename" : "export_saveall_n@3x.png",
       "idiom" : "universal",
       "scale" : "3x"
     }

BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveall_n.imageset/export_saveall_n@3x.png


+ 1 - 1
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/xx.imageset/Contents.json → BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveonly_h.imageset/Contents.json

@@ -9,7 +9,7 @@
       "scale" : "2x"
     },
     {
-      "filename" : "xx@3x.png",
+      "filename" : "export_saveonly_h@3x.png",
       "idiom" : "universal",
       "scale" : "3x"
     }

BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveonly_h.imageset/export_saveonly_h@3x.png


+ 21 - 0
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveonly_n.imageset/Contents.json

@@ -0,0 +1,21 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "export_saveonly_n@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/export_saveonly_n.imageset/export_saveonly_n@3x.png


BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/gou.imageset/gou@3x.png


BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/mic1.imageset/mic1@3x.png


BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/mic2.imageset/mic2@3x.png


+ 1 - 1
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/gou.imageset/Contents.json → BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/preview_play.imageset/Contents.json

@@ -9,7 +9,7 @@
       "scale" : "2x"
     },
     {
-      "filename" : "gou@3x.png",
+      "filename" : "preview_play@3x.png",
       "idiom" : "universal",
       "scale" : "3x"
     }

BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/preview_play.imageset/preview_play@3x.png


+ 21 - 0
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/preview_sound_set.imageset/Contents.json

@@ -0,0 +1,21 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "preview_sound_set@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/preview_sound_set.imageset/preview_sound_set@3x.png


BIN
BFRecordScreenKit/Assets/BFRecordScreenKit.xcassets/xx.imageset/xx@3x.png


+ 4 - 3
BFRecordScreenKit/Classes/BFRSComm.swift

@@ -19,6 +19,7 @@ func currentBundle() -> Bundle? {
     return Bundle.current(moduleName: "BFRecordScreenKit", isAssets: false)
 }
 
-func ThemeStyleGreen() -> UIColor {
-    return UIColor.hexColor(hexadecimal: "#28BE67")
-}
+//func ThemeStyleGreen() -> UIColor {
+//    return UIColor.hexColor(hexadecimal: "#28BE67")
+//}
+public var ThemeStyleColor = UIColor.hexColor(hexadecimal: "#28BE67")

+ 150 - 100
BFRecordScreenKit/Classes/BFRecordExport.swift

@@ -15,26 +15,23 @@ import GPUImage
 public class BFRecordExport {
     public var progress : ((Float)->Void)?
     public var exportCompletion : ((Error?, URL?)->Void)?
-    
-    // 视频素材
-    public var asset:AVURLAsset?
-    
-    // 录音段
-    public var voiceList:[PQVoiceModel]? {
-        didSet {
-//            audioAssets = voiceList?.map({ model in
-//                AVURLAsset(url: URL(fileURLWithPath: model.wavFilePath))
-//            })
-            
+        
+    public var data:[BFRecordItemModel]? {
+        didSet{
+            if data?.count ?? 0 > 0 {
+                for item in data! {
+                    item.generationTimeRanges()
+                }
+            }
         }
     }
 
     var count = 0
     
-    var audioAssets : [AVURLAsset]?
+    var stickerRanges = [CMTimeRange]()
     
     var exporter : PQCompositionExporter?
-    var mStickers = [PQEditVisionTrackMaterialsModel]()
+//    var mStickers = [PQEditVisionTrackMaterialsModel]()
     
     deinit {
     }
@@ -42,22 +39,43 @@ public class BFRecordExport {
 
     
     //MARK: -
-    public func startExprot(){
+    public func prepareData(){
+        if let data = data, data.count > 0{
+            _ = generationTimeRanges(voiceStickers: data.first!.voiceStickers)
+        }
+    }
+
+    /// synthesisAll: 合成所有还是只合成录音部分
+    public func startExprot(synthesisAll:Bool){
         // 1,背景视频素材
-        let bgMovieInfo: PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel()
-        bgMovieInfo.type = StickerType.VIDEO.rawValue
-        bgMovieInfo.locationPath = ((asset?.url.absoluteString)?.removingPercentEncoding ?? "").replacingOccurrences(of: "file://", with: "")
-        bgMovieInfo.timelineIn = 0
-        bgMovieInfo.timelineOut = CMTimeGetSeconds(asset?.duration ?? CMTime.zero)
-        bgMovieInfo.model_in = bgMovieInfo.timelineIn
-        bgMovieInfo.out = bgMovieInfo.timelineOut
-        bgMovieInfo.canvasFillType = stickerContentMode.aspectFitStr.rawValue
-        bgMovieInfo.volumeGain = 30
-        bgMovieInfo.aptDuration = bgMovieInfo.timelineOut
-        bgMovieInfo.duration = bgMovieInfo.timelineOut
-        mStickers.append(bgMovieInfo)
-    
-        beginExport(videoStickers: mStickers)
+        if let itemModels = data {
+            var totalDur = 0.0
+            for (index, itemModel) in itemModels.enumerated() {
+                let asset = itemModel.baseMaterial
+                if let dur = asset?.duration.seconds {
+                    if synthesisAll {
+                        let bgMovieInfo = splitBaseMaterial(timelineIn: totalDur, model_in: 0, duration: dur)
+                        bgMovieInfo.volumeGain = 0
+                        itemModel.videoStickers.append(bgMovieInfo)
+                        totalDur += dur
+                    } else {
+                        var subDur = 0.0
+                        let drangs = itemModel.dealedDurationRanges.filter { ranges in
+                            ranges.isRecord == true
+                        }
+                        for srange in drangs {
+                            let range = srange.range
+                            let sticker = splitBaseMaterial(timelineIn: (totalDur + subDur), model_in: range.start.seconds, duration: range.duration.seconds)
+                            sticker.volumeGain = 0
+                            itemModel.videoStickers.append(sticker)
+                            subDur += range.duration.seconds
+                        }
+                        totalDur += subDur
+                    }
+                }
+            }
+            beginExport(synthesisAll:synthesisAll)
+        }
     }
     
     public func cancelExport(){
@@ -78,39 +96,42 @@ public class BFRecordExport {
         
     }
     
-    func exprotVideo(){
-//        //重新创建GPUImageMovie用于保存
-//           saveMovie = [[GPUImageMovie alloc] initWithURL:self.pathURL];
-        let saveFilter = GPUImageFilter()
-        let saveMovie = GPUImageMovie(url: asset?.url)
-        saveMovie?.shouldRepeat = false
-        saveMovie?.addTarget(saveFilter)
+    func generationTimeRanges(voiceStickers:[PQVoiceModel]) -> [CMTimeRange]{
         
-        let filePath = getOutputFilePath()
-        let savewrite = GPUImageMovieWriter(movieURL: getOutputFilePath(), size: getVideoSize())
-        savewrite?.shouldPassthroughAudio = true
-        savewrite?.encodingLiveVideo = true
-        saveFilter.addTarget(savewrite)
-        
-        saveMovie?.enableSynchronizedEncoding(using: savewrite)
-//        saveMovie?.audioEncodingTarget = savewrite
-        savewrite?.startRecording()
-        saveMovie?.startProcessing()
-        savewrite?.completionBlock = {
-            DispatchQueue.main.async { [weak self, weak savewrite] in
-                saveFilter.removeTarget(savewrite)
-                savewrite?.finishRecording()
-                saveMovie?.cancelProcessing()
-                saveMovie?.removeTarget(saveFilter)
-                cShowHUB(superView: nil, msg: "合成成功")
-                self?.exportCompletion?(nil, filePath)
-                self?.saveVideoToPhoto(url: filePath)
+        var ranges = [CMTimeRange]()
+        var start : Double = 0
+        for model in voiceStickers {
+            if model.startTime > start{
+                let range = CMTimeRange(start: CMTime(seconds: start, preferredTimescale: 100), duration: CMTime(seconds: model.startTime - start, preferredTimescale: 100))
+                ranges.append(range)
+                
             }
+            ranges.append(CMTimeRange(start: CMTime(seconds: model.startTime, preferredTimescale: 100), end: CMTime(seconds: model.endTime, preferredTimescale: 100)))
+            start = model.endTime
         }
+        
+        return ranges
     }
-
     
-    func beginExport(videoStickers:[PQEditVisionTrackMaterialsModel]) {
+    func splitBaseMaterial(timelineIn:Double, model_in:Double, duration:Double) -> PQEditVisionTrackMaterialsModel{
+        let bgMovieInfo: PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel()
+        if let asset = data?.first?.baseMaterial {
+            bgMovieInfo.type = StickerType.VIDEO.rawValue
+            bgMovieInfo.locationPath = ((asset.url.absoluteString).removingPercentEncoding ?? "").replacingOccurrences(of: "file://", with: "")
+            bgMovieInfo.timelineIn = timelineIn
+            bgMovieInfo.timelineOut = timelineIn + duration
+            bgMovieInfo.model_in = model_in
+            bgMovieInfo.out = model_in + duration
+            bgMovieInfo.canvasFillType = stickerContentMode.aspectFitStr.rawValue
+            bgMovieInfo.volumeGain = 1
+            bgMovieInfo.aptDuration = bgMovieInfo.timelineOut
+            bgMovieInfo.duration = bgMovieInfo.timelineOut
+        }
+        
+        return bgMovieInfo
+    }
+    
+    func beginExport(synthesisAll:Bool) {
         // 输出视频地址
 //        exprotVideo()
 //        return;
@@ -122,29 +143,17 @@ public class BFRecordExport {
         let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
         BFLog(1, message: "导出视频地址 \(outPutMP4URL)")
         
+        guard let itemModel = data?.first else {
+            return
+        }
         // 处理导出
-        if (voiceList?.count ?? 0 ) > 0 || videoStickers.count > 1 {
-//            var audioUrl:URL?
-//            if audioAsset?.count ?? 0 > 0 {
-//                // 多音频合成
-//                if let list = voiceList?.map({ model in
-//                    URL(fileURLWithPath: model.wavFilePath)
-//                }){
-//                    if list.count == 1 {
-//                        audioUrl = list.first
-//                    }else {
-//                        let semaphore = DispatchSemaphore(value: 0)
-//                        PQPlayerViewModel.mergeAudios(urls: list) { completURL in
-//                            audioUrl = completURL
-//                            semaphore.signal()
-//                        }
-//                        _ = semaphore.wait(timeout: .now() + 5)
-//                    }
-//                }
-//            }
-            let (audioMix, composition) = mergeAudio(videoStickers: videoStickers, audios: voiceList)
+        let voiceList = itemModel.voiceStickers
+        let videoStickers = itemModel.videoStickers
+        if voiceList.count > 0 || videoStickers.count > 1 {
+
+            let (audioMix, composition) = mergeAudio(videoStickers: videoStickers, audios: voiceList,synthesisAll:synthesisAll)
             
-            let filter = mStickers.map { sticker in
+            let filter = videoStickers.map { sticker in
                 PQMovieFilter(movieSticker: sticker)
             }
             // 有
@@ -162,10 +171,12 @@ public class BFRecordExport {
 //            }
             exporter = PQCompositionExporter(asset: composition, videoComposition: nil, audioMix: audioMix, filters: filter, animationTool: nil, exportURL: outPutMP4URL)
             
-            let size = getVideoSize()
+            let asset = data?.first?.baseMaterial
+
+            let size = getVideoSize(asset: asset!)
             var orgeBitRate = Int(size.width * size.height * 3)
             
-            for stick in mStickers {
+            for stick in videoStickers {
                 if stick.type == StickerType.VIDEO.rawValue {
                     let asset = AVURLAsset(url: URL(fileURLWithPath: stick.locationPath), options: avAssertOptions)
                     
@@ -178,7 +189,7 @@ public class BFRecordExport {
             
             BFLog(message: "导出设置的码率为:\(orgeBitRate)")
             if exporter!.prepare(videoSize: size, videoAverageBitRate: orgeBitRate) {
-                exporter!.start(playeTimeRange: CMTimeRange(start: CMTime.zero, end: asset?.duration ?? CMTime.zero))
+                exporter!.start(playeTimeRange: CMTimeRange(start: CMTime.zero, end: composition.duration))
             }
             exporter?.progressClosure = { [weak self] _, _, progress in
                 //            BFLog(message: "正片合成进度 \(progress * 100)%")
@@ -208,7 +219,9 @@ public class BFRecordExport {
             }
         } else {
             // 没有处理,直接copy原文件
-            self.exportCompletion?(nil, self.asset?.url)
+            if let ass = data?.first?.baseMaterial{
+                self.exportCompletion?(nil, ass.url)
+            }
         }
 
     }
@@ -233,9 +246,9 @@ public class BFRecordExport {
 //        })
     }
     
-    func getVideoSize() -> CGSize{
+    func getVideoSize(asset:AVURLAsset) -> CGSize{
         var size = CGSize.zero
-        self.asset?.tracks.forEach({ track in
+        asset.tracks.forEach({ track in
             if track.mediaType == .video{
                 let realSize = __CGSizeApplyAffineTransform(track.naturalSize, track.preferredTransform)
                 size = CGSize(width: ceil(abs(realSize.width)), height: ceil(abs(realSize.height)))
@@ -247,7 +260,7 @@ public class BFRecordExport {
 }
 
 extension BFRecordExport {
-    func mergeAudio(videoStickers:[PQEditVisionTrackMaterialsModel], audios:[PQVoiceModel]?) -> (AVMutableAudioMix, AVMutableComposition){
+    func mergeAudio(videoStickers:[PQEditVisionTrackMaterialsModel], audios:[PQVoiceModel]?, synthesisAll:Bool) -> (AVMutableAudioMix, AVMutableComposition){
         let composition = AVMutableComposition()
         let audioMix = AVMutableAudioMix()
         var tempParameters = [AVMutableAudioMixInputParameters]()
@@ -259,30 +272,67 @@ extension BFRecordExport {
                 BFLog(message: "音频音量 为0 不添加")
                 continue
             }
-            sticker.volumeGain = 2
+            sticker.volumeGain = 50
             totalDuration = max(totalDuration, sticker.duration)
             tempParameters += PQVideoEditViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
         }
         if let voices = audios {
-            for model in voices {
-                if model.volume == 0 {
-                    // 如果添加了会有刺啦音
-                    BFLog(message: "音频音量 为0 不添加")
-                    continue
-                }
-                let sticker = PQEditVisionTrackMaterialsModel()
-                sticker.model_in = 0
-                sticker.timelineIn = model.startTime
-                sticker.out = model.endTime
-                sticker.aptDuration = model.endTime - model.startTime
-                sticker.duration = sticker.aptDuration
-                sticker.locationPath = model.wavFilePath
-                sticker.volumeGain = 100 //Float64(model.volume)
-                tempParameters += PQVideoEditViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
+            if synthesisAll {
+                tempParameters += mergeRecordVoiceAll(voices:voices, composition)
+            }else {
+                tempParameters += mergeRecordVoiceOnly(voices:voices, composition)
             }
         }
         audioMix.inputParameters = tempParameters
         return (audioMix, composition)
     }
     
+    func mergeRecordVoiceOnly(voices:[PQVoiceModel], _ composition:AVMutableComposition) -> [AVMutableAudioMixInputParameters] {
+        var tempParameters = [AVMutableAudioMixInputParameters]()
+        var totalDur:Double = 0.0
+        for model in voices {
+            if model.volume == 0 {
+                // 如果添加了会有刺啦音
+                BFLog(message: "音频音量 为0 不添加")
+                continue
+            }
+            let duration = model.endTime - model.startTime
+
+            let sticker = PQEditVisionTrackMaterialsModel()
+            sticker.model_in = 0
+            sticker.out = duration
+            sticker.timelineIn = totalDur
+            sticker.aptDuration = duration
+            sticker.duration = duration
+            sticker.locationPath = model.wavFilePath
+            sticker.volumeGain = 100 //Float64(model.volume)
+            tempParameters += PQVideoEditViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
+            totalDur += duration
+        }
+
+        return tempParameters
+    }
+    
+    func mergeRecordVoiceAll(voices:[PQVoiceModel], _ composition:AVMutableComposition) -> [AVMutableAudioMixInputParameters] {
+        var tempParameters = [AVMutableAudioMixInputParameters]()
+        for model in voices {
+            if model.volume == 0 {
+                // 如果添加了会有刺啦音
+                BFLog(message: "音频音量 为0 不添加")
+                continue
+            }
+            let sticker = PQEditVisionTrackMaterialsModel()
+            sticker.model_in = 0
+            sticker.timelineIn = model.startTime
+            sticker.out = model.endTime
+            sticker.aptDuration = model.endTime - model.startTime
+            sticker.duration = sticker.aptDuration
+            sticker.locationPath = model.wavFilePath
+            sticker.volumeGain = 100 //Float64(model.volume)
+            tempParameters += PQVideoEditViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
+        }
+        
+        return tempParameters
+    }
+    
 }

+ 50 - 0
BFRecordScreenKit/Classes/BFRecordItemModel.swift

@@ -0,0 +1,50 @@
+//
+//  BFRecordItemModel.swift
+//  BFRecordScreenKit
+//
+//  Created by 胡志强 on 2021/12/6.
+//
+
+import Foundation
+
+import BFFramework
+
+struct SplitRecordRange {
+    var isRecord:Bool = false
+    var range:CMTimeRange
+}
+
+public class BFRecordItemModel: NSObject {
+    var baseMaterial : AVURLAsset?
+    var dealedDurationRanges = [SplitRecordRange]()
+    public var voiceStickers = [PQVoiceModel]()
+    public var videoStickers = [PQEditVisionTrackMaterialsModel]()
+    public var imageStickers = [PQEditVisionTrackMaterialsModel]()
+    public var titleStickers = [PQEditSubTitleModel]()
+    public var index = 0
+    public var width = 0
+    public var height = 0
+    
+    func generationTimeRanges() {
+        
+        dealedDurationRanges.removeAll()
+        
+        var start : Double = 0
+        for model in voiceStickers {
+            if model.startTime > start{
+                //
+                let range = CMTimeRange(start: CMTime(seconds: start, preferredTimescale: 100), duration: CMTime(seconds: model.startTime - start, preferredTimescale: 100))
+                dealedDurationRanges.append(SplitRecordRange(isRecord: false, range: range))
+            }
+
+            let range = CMTimeRange(start: CMTime(seconds: model.startTime, preferredTimescale: 100), end: CMTime(seconds: model.endTime, preferredTimescale: 100))
+            dealedDurationRanges.append(SplitRecordRange(isRecord: true, range: range))
+            start = model.endTime
+        }
+        if start < baseMaterial?.duration.seconds ?? 0 {
+            let range = CMTimeRange(start: CMTime(seconds: start, preferredTimescale: 100), end: CMTime(seconds: baseMaterial?.duration.seconds ?? 0, preferredTimescale: 100))
+            dealedDurationRanges.append(SplitRecordRange(isRecord: false, range: range))
+        }
+        
+    }
+}

+ 0 - 17
BFRecordScreenKit/Classes/BFVoiceRecordManager.swift

@@ -119,25 +119,8 @@ class BFVoiceRecordManager {
                     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文件

+ 204 - 127
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenController.swift

@@ -14,13 +14,23 @@ import BFCommonKit
 import BFFramework
 import UIKit
 
+
+struct WithDrawModel {
+    var type:Int   // 0:拖动; 1:预览播放暂停 2: 录音结束
+    var timestamp:Double
+}
+
+
 public class BFRecordScreenController: BFBaseViewController {
     
     public var nextActionHandle:(()->Void)?
     public var closeActionHandle:(()->Void)?
     
     // MARK: - 录制参数
-    public var asset:PHAsset?
+    public var assets = [PHAsset]()
+    
+    var currItemModelIndex = 0
+    public var itemModels = [BFRecordItemModel]()
 //    var shouldPlayRecordIndex:Int = -1          // 当前应该播放的录音资源序号
     var currentPlayRecordIndex:Int = -1         // >= 0 :当前正在播放的录音资源序号; -3: 刚录音完,不需要播放录音; -1:初始化阶段
     var isRecording = false {                   // 是否正在录音
@@ -41,19 +51,24 @@ public class BFRecordScreenController: BFBaseViewController {
     var currentAssetProgress : CMTime = .zero   // 当前素材播放的进度
     // 视频素材
     public var avasset:AVURLAsset?
-    public var recordList:[PQVoiceModel] = [PQVoiceModel]()
+    
+//    public var recordList:[PQVoiceModel] = [PQVoiceModel]()
+    
     var assetPlayer:AVPlayer?       // 原视频音频播放器
     var isCompletePlay = true
     var hadPrepareToPlayRecord = false    // 录音播放器准备
     var recordPlayer:AVPlayer? // 录音音频播放器
     var movie :GPUImageMovie?       // 视频预览
     var playView :GPUImageView?     // 视频展示视图
-    var isDragingProgressSlder : Bool = false // 是否在拖动进度条
     
+    // MARK: 行为参数
+    var events = [WithDrawModel]()              // 行为记录,方便撤销
+    var isDragingProgressSlder : Bool = false   // 是否在拖动进度条
+    var isStopAtRecordRange = -1
     //定义音频的编码参数
-    let recordSettings:[String : Any] = [AVSampleRateKey : 44100.0, //声音采样率
-                            AVFormatIDKey : kAudioFormatLinearPCM,  //编码格式
-                    AVNumberOfChannelsKey : 1,                      //采集音轨
+    let recordSettings:[String : Any] = [AVSampleRateKey : 44100.0, // 声音采样率
+                            AVFormatIDKey : kAudioFormatLinearPCM,  // 编码格式
+                    AVNumberOfChannelsKey : 1,                      // 采集音轨
                   AVEncoderBitDepthHintKey: 16,                     // 位深
                  AVEncoderAudioQualityKey : AVAudioQuality.medium.rawValue] //音频质量
 
@@ -66,26 +81,29 @@ public class BFRecordScreenController: BFBaseViewController {
             
         }
         manager.endRecordHandle = {[weak self] (model, error) in
-            if let model = model, FileManager.default.fileExists(atPath: model.wavFilePath ?? ""){
+            if let sself = self, let model = model, FileManager.default.fileExists(atPath: model.wavFilePath ?? ""){
                 // 加入到语音数组里
-                let ass = AVURLAsset(url: URL(fileURLWithPath: model.wavFilePath))
-                
-                model.endTime = model.startTime + CMTimeGetSeconds(ass.duration)
-                
-                // TODO: 原逻辑要删除新录音后边的数据, 新逻辑是插入覆盖
-                while let m = self?.recordList.last{
-                    if model.startTime < m.startTime {
-                        self?.recordList.removeLast()
-                    }else if m.endTime > model.startTime {
-                        m.endTime = model.startTime
-                    }else{
+                // TODO: 原逻辑要删除新录音后边的数据, 新逻辑是覆盖则删除
+                var index = sself.itemModels[sself.currItemModelIndex].voiceStickers.count - 1
+                while index >= 0{
+                    let m = sself.itemModels[sself.currItemModelIndex].voiceStickers[index]
+                    index -= 1
+                    if model.endTime > m.startTime && model.endTime <= m.endTime
+                        || model.startTime <= m.startTime && model.startTime > m.endTime{
+                        sself.itemModels[sself.currItemModelIndex].voiceStickers.remove(at: index+1)
+                        continue
+                    }
+                    if model.startTime < m.endTime {
                         break
                     }
                 }
                 BFLog(1, message: "添加录音文件:\(model.startTime) -- \(model.endTime)")
-                self?.recordList.append(model)
-                self?.drawOrUpdateRecordProgessLable()
-                self?.currentPlayRecordIndex = -3 // 刚录音完,不需要播放录音
+                
+                sself.itemModels[sself.currItemModelIndex].voiceStickers.append(model)
+                
+                sself.drawOrUpdateRecordProgessLable()
+                
+                sself.currentPlayRecordIndex = -3 // 刚录音完,不需要播放录音
             }
             
         }
@@ -109,6 +127,9 @@ public class BFRecordScreenController: BFBaseViewController {
     
     lazy var playBtn:UIButton = {
         let btn = UIButton(frame: view.bounds)
+        btn.setImage(imageInRecordScreenKit(by: "preview_play"), for: .normal)
+        let vv = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
+        btn.setImage(vv.graphicsGetImage(), for: .selected)
         btn.addTarget(self, action: #selector(playVideo(btn:)), for: .touchUpInside)
         return btn
     }()
@@ -129,7 +150,7 @@ public class BFRecordScreenController: BFBaseViewController {
     
     lazy var recordBtn:UIButton = {
         let btn = UIButton(type: .custom)
-        btn.backgroundColor = ThemeStyleGreen()
+        btn.backgroundColor = ThemeStyleColor
         btn.setTitle("按住 说话", for: .normal)
         btn.adjustsImageWhenHighlighted = false
         btn.addTarget(self, action: #selector(startRecord), for: .touchDown)
@@ -138,13 +159,6 @@ public class BFRecordScreenController: BFBaseViewController {
         return btn
     }()
     
-    lazy var progessSildeBackV : UIView = {
-        let vv = UIView()
-        vv.backgroundColor = .orange // .clear
-        
-        return vv
-    }()
-    
     lazy var withDrawBtn:UIButton = {
         let btn = UIButton(type: .custom)
         btn.setImage(imageInRecordScreenKit(by: "withdraw_n"), for: .normal)
@@ -164,7 +178,7 @@ public class BFRecordScreenController: BFBaseViewController {
         btn.setImage(imageInRecordScreenKit(by: "changeVoice_h"), for: .highlighted)
         btn.setTitle("变声", for: .normal)
         btn.setTitleColor(.white, for: .normal)
-        btn.setTitleColor(ThemeStyleGreen(), for: .highlighted)
+        btn.setTitleColor(ThemeStyleColor, for: .highlighted)
         btn.titleLabel?.font = UIFont.systemFont(ofSize: 12)
         btn.contentVerticalAlignment = UIControl.ContentVerticalAlignment.center;
         btn.addTarget(self, action: #selector(changeVoiceAction), for: .touchUpInside)
@@ -241,15 +255,18 @@ public class BFRecordScreenController: BFBaseViewController {
      
     lazy var progressThumV : BFVideoThumbProgressView = {
         let vv = BFVideoThumbProgressView(frame: CGRect(x: 0, y: 54, width: cScreenWidth, height: 50))
-        vv.dragScrollProgressHandle = {[weak self] process in
+        vv.dragScrollProgressHandle = {[weak self] isStart, process in
             DispatchQueue.main.async {[weak self] in
                 guard let sself = self else {
                     return
                 }
+                if isStart {
+                    sself.events.append(WithDrawModel(type: 0, timestamp: sself.currentAssetProgress.seconds))
+                }
                 if sself.isNormalPlaying || sself.isRecording {
                     sself.pause()
-                    sself.isDragingProgressSlder = true
                 }
+                sself.isDragingProgressSlder = true
                 sself.changeProgress(progress: process)
             }
         }
@@ -258,11 +275,7 @@ public class BFRecordScreenController: BFBaseViewController {
             guard let sself = self else {
                 return
             }
-            sself.changeProgress(progress: process)
-
-            sself.isDragingProgressSlder = false
-            sself.currentPlayRecordIndex = -1
-            sself.hadPrepareToPlayRecord = false
+            sself.thumbViewEnded(progress: process)
         }
         vv.isHidden = true
         return vv
@@ -311,9 +324,8 @@ public class BFRecordScreenController: BFBaseViewController {
 //        view.addSubview(toolV)
         bottomeView.addSubview(recordBtn)
         bottomeView.addSubview(withDrawBtn)
-        bottomeView.addSubview(changeVoiceBtn)
+//        bottomeView.addSubview(changeVoiceBtn)
         bottomeView.addSubview(progressThumV)
-        progressThumV.addSubview(progessSildeBackV)
 
         if checkStatus() {
             try? AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: .defaultToSpeaker)
@@ -347,29 +359,18 @@ public class BFRecordScreenController: BFBaseViewController {
             make.width.height.equalTo(65)
             make.top.equalTo(128)
         }
-        changeVoiceBtn.snp.makeConstraints { make in
-            make.right.equalToSuperview()
-            make.top.width.height.equalTo(withDrawBtn)
-        }
+//        changeVoiceBtn.snp.makeConstraints { make in
+//            make.right.equalToSuperview()
+//            make.top.width.height.equalTo(withDrawBtn)
+//        }
         
         recordBtn.snp.makeConstraints { make in
             make.left.equalTo(withDrawBtn.snp.right)
-            make.right.equalTo(changeVoiceBtn.snp.left)
+            make.right.equalTo(-65)
             make.height.equalTo(42)
             make.top.equalTo(withDrawBtn).offset(6)
         }
-        
-        progessSildeBackV.snp.makeConstraints { make in
-            make.left.equalToSuperview()
-            make.right.equalToSuperview()
-            make.bottom.equalToSuperview()
-            make.height.equalTo(8)
-        }
- 
-//        progessSilde.snp.makeConstraints { make in
-//            make.left.right.centerY.equalTo(progessSildeBackV)
-//            make.height.equalTo(20)
-//        }
+
 //        openCameraBtn.snp.makeConstraints { make in
 //            make.right.equalToSuperview().offset(-12)
 //            make.top.equalToSuperview().offset(98)
@@ -493,9 +494,11 @@ public class BFRecordScreenController: BFBaseViewController {
         isRecording = true
 
         pause()
+        
+        events.append(WithDrawModel(type: 2, timestamp: self.currentAssetProgress.seconds))
 
         let model = PQVoiceModel()
-        model.startTime = CMTimeGetSeconds(self.currentAssetProgress)
+        model.startTime = self.currentAssetProgress.seconds
         model.volume = 100
 
 //        recorderManager.voiceModel = model
@@ -510,21 +513,20 @@ public class BFRecordScreenController: BFBaseViewController {
         }
  
         recorderManager.voiceModel = model
-        recorderManager.startRecord(index: recordList.count)
+        recorderManager.startRecord(index: 1)
 //        movie?.startProcessing()
         assetPlayer?.volume = 0
         assetPlayer?.play()
- 
+        playBtn.isSelected = true
     }
     
     @objc func endRecord(){
-        
     
         // 存储录音
- 
-     recorderManager.endRecord()
         isRecording = false
  
+        recorderManager.voiceModel?.endTime = self.currentAssetProgress.seconds
+        recorderManager.endRecord()
 
         pause()
         if(!avatarView.isHidden){
@@ -539,8 +541,32 @@ public class BFRecordScreenController: BFBaseViewController {
         
         pause()
     }
+    
     @objc func withdrawAction(){
         pause()
+        if let action = events.last {
+            var jumpTime = action.timestamp
+            if action.type == 2 {
+                // 撤销录制
+                if let modelIndex = itemModels[currItemModelIndex].voiceStickers.firstIndex(where: { mod in
+                    mod.startTime == action.timestamp
+                }) {
+                    let model = itemModels[currItemModelIndex].voiceStickers[modelIndex]
+                    itemModels[currItemModelIndex].voiceStickers.remove(at: modelIndex)
+                    drawOrUpdateRecordProgessLable()
+                    jumpTime = model.startTime
+                }
+            }else {
+            }
+            events.removeLast()
+            if let dur = itemModels[currItemModelIndex].baseMaterial?.duration.seconds,dur > 0 {
+                changeProgress(progress: Float(jumpTime / dur))
+                isDragingProgressSlder = false
+                currentPlayRecordIndex = -1
+                hadPrepareToPlayRecord = false
+                progressThumV.progress = jumpTime
+            }
+        }
     }
     
     @objc func changeVoiceAction(){
@@ -549,12 +575,19 @@ public class BFRecordScreenController: BFBaseViewController {
     }
     
     @objc func playVideo(btn:UIButton){
-        btn.isSelected ? pause() : play()
+        if btn.isSelected {
+            pause()
+            searchStopAtRecordRange()
+        }else {
+            events.append(WithDrawModel(type: 1, timestamp: self.currentAssetProgress.seconds))
+            play()
+        }
     }
 
     @objc func sliderTouchBegan(sender _: UISlider) {
         isDragingProgressSlder = true
         pause()
+
     }
 
     @objc func sliderTouchEnded(sender: UISlider) {
@@ -565,7 +598,30 @@ public class BFRecordScreenController: BFBaseViewController {
     }
     @objc func sliderValueDidChanged(sender: UISlider) {
         changeProgress(progress: sender.value)
-
+    }
+    
+    func thumbViewEnded(progress:Float) {
+        changeProgress(progress: progress)
+        isDragingProgressSlder = false
+        currentPlayRecordIndex = -1
+        hadPrepareToPlayRecord = false
+        
+        searchStopAtRecordRange()
+    }
+    
+    func searchStopAtRecordRange() {
+        // TODO: 判断是否停止录音区间,是则删除相关录音,画笔,头像,字幕
+        let elems = itemModels[currItemModelIndex].voiceStickers.enumerated().filter({ elem in
+            elem.1.startTime <= self.currentAssetProgress.seconds && elem.1.endTime > self.currentAssetProgress.seconds
+        })
+        if elems.count > 0{
+            //  TODO: 停在了录音区间,显示删除按钮
+            isStopAtRecordRange = elems.first!.0
+            BFLog(1, message: "停在了录音区间 里")
+        }else {
+            isStopAtRecordRange = -1
+            BFLog(1, message: "停在了录音区间 外")
+        }
     }
     
     // MARK: - 权限申请
@@ -618,7 +674,8 @@ public class BFRecordScreenController: BFBaseViewController {
         if currentPlayRecordIndex == -3 { // 刚录音完,不需要播放
             return
         }
-        let (shouldPlayRecordIndex, recordedAudio) = recordList.enumerated().first { model in
+        
+        let (shouldPlayRecordIndex, recordedAudio) = itemModels[currItemModelIndex].voiceStickers.enumerated().first { model in
             model.1.endTime > CMTimeGetSeconds(currentT)
         } ?? (-1, nil)
         
@@ -662,7 +719,7 @@ public class BFRecordScreenController: BFBaseViewController {
                 if currentPlayRecordIndex == -1 && self.isNormalPlaying{
                     let second = CMTimeGetSeconds(currentT) - recordedAudio.startTime
                     DispatchQueue.main.async {[weak self] in
-                        self?.recordPlayer?.seek(to: CMTime(value: CMTimeValue(second), timescale: 100), toleranceBefore: CMTime(value: 1, timescale: 1000), toleranceAfter: CMTime(value: 1, timescale: 1000), completionHandler: {[weak self] finished in
+                        self?.recordPlayer?.seek(to: CMTime(value: CMTimeValue(second), timescale: 100), toleranceBefore: CMTime(value: 1, timescale: 1000000), toleranceAfter: CMTime(value: 1, timescale: 1000000), completionHandler: {[weak self] finished in
                             if finished && (self?.isNormalPlaying ?? false) {
                                 self?.recordPlayer?.play()
                                 BFLog(1, message: "录音开始播放 playing, \(second), \(CMTimeGetSeconds(self?.recordPlayer?.currentItem?.duration ?? .zero))")
@@ -711,57 +768,69 @@ public class BFRecordScreenController: BFBaseViewController {
         assetPlayer?.pause()
         recordPlayer?.pause()
         
-        assetPlayer?.seek(to: self.currentAssetProgress , toleranceBefore: CMTime(value: 1, timescale: 1000), toleranceAfter: CMTime(value: 1, timescale: 1000), completionHandler: { finished in
+        assetPlayer?.seek(to: self.currentAssetProgress , toleranceBefore: CMTime(value: 1, timescale: 1000000), toleranceAfter: CMTime(value: 1, timescale: 1000000), completionHandler: { finished in
         })
     }
     
     func fetchVideo(){
-        if let asset = self.asset {
-            let options = PHVideoRequestOptions()
-            options.isNetworkAccessAllowed = true
-            options.deliveryMode = .automatic
+        if self.assets.count > 0 {
+
+            currItemModelIndex = 0
             
-            PHImageManager.default().requestPlayerItem(forVideo:asset, options: options, resultHandler: { [weak self] playerItem, info in
+            for (index, asset) in self.assets.enumerated() {
+                let itemModel = BFRecordItemModel()
+                itemModel.index = 0
+                itemModel.width = asset.pixelWidth
+                itemModel.height = asset.pixelHeight
+                
+                itemModels.append(itemModel)
+                
+                let options = PHVideoRequestOptions()
+                options.isNetworkAccessAllowed = true
+                options.deliveryMode = .automatic
+                
+                PHImageManager.default().requestPlayerItem(forVideo:asset, options: options, resultHandler: { [weak self] playerItem, info in
 
-                guard let item = playerItem else {
-                    cShowHUB(superView: nil, msg: "视频获取失败")
-                    return
-                }
-                self?.setAudioPlay(item: item)
-                self?.setVideoPlay(item: item)
+                    guard let item = playerItem else {
+                        cShowHUB(superView: nil, msg: "视频获取失败")
+                        return
+                    }
+                    if index == 0 {
+                        self?.setAudioPlay(item: item)
+                        self?.setVideoPlay(item: item)
+                    }
+                })
                 
-            })
-            
-            let option = PHImageRequestOptions()
-            option.isNetworkAccessAllowed = true //允许下载iCloud的图片
-            option.resizeMode = .fast
-            option.deliveryMode = .highQualityFormat
-            PHImageManager.default().requestImage(for: asset,
-                                           targetSize: self.view.bounds.size,
-                                          contentMode: .aspectFit,
-                                              options: option)
-            { (image, nil) in
-                 // 设置首帧/封面
-                if image != nil {
-                    let pic = GPUImagePicture(image: image)
-                    let filet = GPUImageFilter()
-                    pic?.addTarget(filet)
-                    filet.addTarget(self.playView)
-                    pic?.processImage()
-                }
-            }
-            
-            PHCachingImageManager().requestAVAsset(forVideo: asset, options: options, resultHandler: {[weak self] (asset: AVAsset?, audioMix: AVAudioMix?, info) in
-                if let urlass = asset as? AVURLAsset {
-                    self?.avasset = urlass
-                    DispatchQueue.main.async {[weak self] in
-                        self?.progressThumV.videoAsset = urlass
-                        self?.progressThumV.isHidden = false
-//                        self?.progessSildeBackV.setNeedsLayout()
-//                        self?.progessSildeBackV.layoutIfNeeded()
+//                let option = PHImageRequestOptions()
+//                option.isNetworkAccessAllowed = true //允许下载iCloud的图片
+//                option.resizeMode = .fast
+//                option.deliveryMode = .highQualityFormat
+//                PHImageManager.default().requestImage(for: asset,
+//                                               targetSize: self.view.bounds.size,
+//                                              contentMode: .aspectFit,
+//                                                  options: option)
+//                { (image, nil) in
+//                     // 设置首帧/封面
+//                    if image != nil {
+//                        let pic = GPUImagePicture(image: image)
+//                        let filet = GPUImageFilter()
+//                        pic?.addTarget(filet)
+//                        filet.addTarget(self.playView)
+//                        pic?.processImage()
+//                    }
+//                }
+                
+                PHCachingImageManager().requestAVAsset(forVideo: asset, options: options, resultHandler: {[weak self] (asset: AVAsset?, audioMix: AVAudioMix?, info) in
+                    if let urlasset = asset as? AVURLAsset {
+                        self?.avasset = urlasset
+                        itemModel.baseMaterial = urlasset
+                        DispatchQueue.main.async {[weak self] in
+                            self?.progressThumV.videoAsset = urlasset
+                            self?.progressThumV.isHidden = false
+                        }
                     }
-                }
-            })
+                })
+            }
         }
 
     }
@@ -790,21 +859,24 @@ public class BFRecordScreenController: BFBaseViewController {
             assetPlayer = AVPlayer(playerItem: item)
             avplayerTimeObserver = assetPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 100), queue: DispatchQueue.global()) {[weak self] time in
              //    进度监控
-                if !((self?.isNormalPlaying ?? false) || (self?.isRecording ?? false)) {
-                    return
-                }
                 
-                // 播放对应的录音音频
-                self?.playRecord(at: time)
-
                 self?.currentAssetProgress = time
                 BFLog(1, message: "curr:\(CMTimeGetSeconds(time))")
-                if CMTimeGetSeconds(item.duration) > 0, !(self?.isDragingProgressSlder ?? false) {
+                if CMTimeGetSeconds(item.duration) > 0 {
                     DispatchQueue.main.async { [weak self] in
                         self?.progreddL.text = String(format: "%.2f", CMTimeGetSeconds(time), CMTimeGetSeconds(item.duration))
-                        self?.progressThumV.progress = time.seconds
+                        if !(self?.isDragingProgressSlder ?? false){
+                            self?.progressThumV.progress = time.seconds
+                        }
                     }
                 }
+                
+                if (self?.isNormalPlaying ?? false) || (self?.isRecording ?? false) {
+                    // 播放对应的录音音频
+                    self?.playRecord(at: time)
+                }
+                
+
             } as? NSKeyValueObservation
         }
 
@@ -813,6 +885,8 @@ public class BFRecordScreenController: BFBaseViewController {
             BFLog(1, message: "AVPlayerItemDidPlayToEndTime = \(notify)")
             self?.isNormalPlaying = false
             self?.assetPlayer?.seek(to: CMTime.zero)
+            self?.progressThumV.progress = 0
+            
             self?.currentPlayRecordIndex = -1
             if self?.isRecording ?? false {
                 self?.endRecord()
@@ -843,7 +917,7 @@ public class BFRecordScreenController: BFBaseViewController {
                 self!.progreddL.text = String(format: "%.2f", CMTimeGetSeconds(self!.currentAssetProgress))
             }
 
-            assetPlayer!.seek(to: self.currentAssetProgress, toleranceBefore: CMTime(value: 1, timescale: 1000), toleranceAfter: CMTime(value: 1, timescale: 1000)) { finished in
+            assetPlayer!.seek(to: self.currentAssetProgress, toleranceBefore: CMTime(value: 1, timescale: 1000000), toleranceAfter: CMTime(value: 1, timescale: 1000000)) { finished in
 
             }
         }
@@ -851,18 +925,21 @@ public class BFRecordScreenController: BFBaseViewController {
     
     func drawOrUpdateRecordProgessLable(){
         DispatchQueue.main.async {[weak self] in
-            self?.progessSildeBackV.subviews.forEach { vv in
+            guard let sself = self else {
+                return
+            }
+            sself.progressThumV.progessIndicateBackV.subviews.forEach { vv in
                 vv.removeFromSuperview()
             }
-            if let totalDur = self?.asset?.duration, totalDur > 0, let list = self?.recordList {
-                let width = self?.progessSildeBackV.width ?? 0
-                let height = self?.progessSildeBackV.height ?? 0
-                list.forEach { model in
- 
-                    let lineV = UIView(frame: CGRect(x: model.startTime * Double(width) / totalDur , y: 0, width: (model.endTime - model.startTime) * Double(width) / totalDur, height: Double(height)))
-                    lineV.backgroundColor = ThemeStyleGreen()
 
-                    self?.progessSildeBackV.addSubview(lineV)
+            
+            if let totalDur = sself.itemModels[sself.currItemModelIndex].baseMaterial?.duration.seconds, totalDur > 0, sself.itemModels[sself.currItemModelIndex].voiceStickers.count > 0 {
+                let width = sself.progressThumV.progessIndicateBackV.width
+                let height = sself.progressThumV.progessIndicateBackV.height
+                sself.itemModels[sself.currItemModelIndex].voiceStickers.forEach { model in
+                    let lineV = UIView(frame: CGRect(x: model.startTime * width / totalDur , y: 0, width: (model.endTime - model.startTime) * width / totalDur, height: height))
+                    lineV.backgroundColor = ThemeStyleColor
+                    sself.progressThumV.progessIndicateBackV.addSubview(lineV)
                 }
             }
         }

+ 2 - 4
BFRecordScreenKit/Classes/RecordScreen/View/BFSubtitleSettingView.swift

@@ -50,10 +50,8 @@ class BFSubtitleSettingView: UIView {
         for i in 0...6 {
             let btn = UIButton(type: .custom)
             btn.setImage(imageInRecordScreenKit(by: "wordStyle\(i + 1)"), for: .normal)
-//            btn.setBackgroundImage(UIImage.init(color:
-//                                                    UIColor.hexColor(hexadecimal: "#28BE67")), for: .selected)
-            btn.setBackgroundImage(UIImage.init(color:
-                                                    .black), for: .normal)
+
+            btn.setBackgroundImage(UIImage.init(color:.black), for: .normal)
             btn.frame = CGRect.init(x: 18 + i * ( 40 + 10) , y: 22, width: 40, height: 40)
             if(i == 0){
                 btn.isSelected = true

+ 33 - 12
BFRecordScreenKit/Classes/RecordScreen/View/BFVideoThumbProgressView.swift

@@ -10,6 +10,7 @@ import UIKit
 import AVFoundation
 import BFCommonKit
 import SnapKit
+import BFUIKit
 
 class BFVideoThumbProgressView: UIView {
     var videoAsset : AVURLAsset? {
@@ -24,18 +25,31 @@ class BFVideoThumbProgressView: UIView {
                             self?.thumbImgs = images
                             DispatchQueue.main.async {[weak self] in
                                 if let sself = self{
+                                    var lastiv = UIImageView()
                                     for (i, img) in images.enumerated() {
                                         let iv = UIImageView(image: img)
                                         iv.contentMode = .scaleAspectFill
                                         iv.clipsToBounds = true
-                                        sself.progressView.addSubview(iv)
+                                        sself.progressView.contentView.addSubview(iv)
                                         iv.snp.makeConstraints { make in
-//                                            make.left.equalTo(CGFloat(i) * sself.thumbImageWidth + sself.width * 0.5)
-                                            make.top.height.equalToSuperview()
+                                            make.left.equalTo(CGFloat(i) * sself.thumbImageWidth + sself.width * 0.5)
+                                            make.top.bottom.equalToSuperview()
+                                            make.height.equalTo(50)
                                             make.width.equalTo(sself.thumbImageWidth)
                                         }
+                                        lastiv = iv
+                                    }
+                                    lastiv.snp.makeConstraints { make in
+                                        make.right.equalTo(sself.width * -0.5)
+                                    }
+                                    
+                                    sself.progressView.contentView.addSubview(sself.progessIndicateBackV)
+                                    sself.progessIndicateBackV.snp.makeConstraints { make in
+                                        make.left.equalTo(sself.width * 0.5)
+                                        make.right.equalTo(sself.width * -0.5)
+                                        make.bottom.equalToSuperview()
+                                        make.height.equalTo(6)
                                     }
-//                                    sself.progressView.contentSize = CGSize(width: CGFloat(images.count) *  sself.thumbImageWidth + sself.width, height: sself.height)
                                 }
                             }
                         }
@@ -46,7 +60,7 @@ class BFVideoThumbProgressView: UIView {
         }
     }
     
-    var dragScrollProgressHandle : ((Float) -> Void)?
+    var dragScrollProgressHandle : ((Bool, Float) -> Void)?
     var dragEndHandle : ((Float) -> Void)?
     var isDrag = false
     
@@ -58,15 +72,15 @@ class BFVideoThumbProgressView: UIView {
         didSet{
             if let second = self.videoAsset?.duration.seconds, second > 0 {
                 let w = progressView.contentSize.width - width
-//                progressView.contentOffset = CGPoint(x: progress * w / second, y: 0)
+                progressView.contentOffset = CGPoint(x: progress * w / second, y: 0)
             }
         }
     }
     
     var thumbImgs = [UIImage]()
     
-    lazy var progressView : UIScrollView = {
-        let sv = UIScrollView()
+    lazy var progressView : BFAutolayoutScrollView = {
+        let sv = BFAutolayoutScrollView()
         sv.bounces = false
         sv.backgroundColor = .clear
         sv.decelerationRate = .fast
@@ -75,11 +89,18 @@ class BFVideoThumbProgressView: UIView {
         
         return sv
     }()
+    
+    lazy var progessIndicateBackV : UIView = {
+        let vv = UIView()
+        return vv
+    }()
+    
+    //MARK: - 生命周期
 
     override init(frame: CGRect) {
         super.init(frame: frame)
         addSubview(progressView)
-        
+
         let line = UIView()
         line.backgroundColor = .white
         line.layer.shadowColor = UIColor.black.cgColor
@@ -98,6 +119,7 @@ class BFVideoThumbProgressView: UIView {
     
     override func layoutSubviews() {
         super.layoutSubviews()
+        
         progressView.snp.makeConstraints { make in
             make.edges.equalToSuperview()
         }
@@ -109,14 +131,14 @@ extension BFVideoThumbProgressView : UIScrollViewDelegate {
     func scrollViewDidScroll(_ scrollView: UIScrollView) {
         if isDrag{
             let dur = scrollView.contentOffset.x / (scrollView.contentSize.width - self.width)
-            self.dragScrollProgressHandle?(Float(dur))
+            self.dragScrollProgressHandle?(false, Float(dur))
         }
     }
     
     func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
         isDrag = true
         let dur = scrollView.contentOffset.x / (scrollView.contentSize.width - self.width)
-        self.dragScrollProgressHandle?(Float(dur))
+        self.dragScrollProgressHandle?(true, Float(dur))
     }
     
     func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
@@ -131,7 +153,6 @@ extension BFVideoThumbProgressView : UIScrollViewDelegate {
         let dur = scrollView.contentOffset.x / (scrollView.contentSize.width - self.width)
         isDrag = false
         dragEndHandle?(Float(dur))
-
     }
     
 }