Browse Source

pull master

wenweiwei 3 years ago
parent
commit
1b212a762d

+ 90 - 70
BFRecordScreenKit/Classes/BFRecordExport.swift

@@ -50,44 +50,45 @@ public class BFRecordExport {
             // 切割视频素材
             for (_, itemModel) in itemModels.enumerated() {
                 itemModel.videoStickers.removeAll()
-                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
-                        var subDur = 0.0
-                        let drangs = itemModel.dealedDurationRanges
-                        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 = srange.isRecord ? 0 : 100
-                            itemModel.videoStickers.append(sticker)
-                            subDur += range.duration.seconds
-                        }
-                        totalDur += subDur
-//                        assert(totalDur == dur)
-                    } else {
-                        var subDur = 0.0
-                        let drangs = itemModel.dealedDurationRanges.filter { srange in
-                            srange.isRecord == true
-                        }
-                        let needSort = false
-//                        if needSort {
-//                            drangs.sort { range1, range2 in
-//                                <#code#>
-//                            }
-//                        }
-                        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
+                
+                if synthesisAll {
+                    // 保留全部
+                    //                        let bgMovieInfo = splitBaseMaterial(timelineIn: totalDur, model_in: 0, duration: dur)
+                    //                        bgMovieInfo.volumeGain = 0
+                    //                        itemModel.videoStickers.append(bgMovieInfo)
+                    //                        totalDur += dur
+                    var subDur = 0.0
+                    let drangs = itemModel.dealedDurationRanges
+                    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 = srange.isRecord ? 0 : 100
+                        itemModel.videoStickers.append(sticker)
+                        subDur += range.duration.seconds
+                    }
+                    totalDur += subDur
+                    //                        assert(totalDur == dur)
+                } else {
+                    var subDur = 0.0
+                    var drangs = itemModel.dealedDurationRanges.filter { srange in
+                        srange.isRecord == true
+                    }
+                    
+                    // 是否按录音顺序排列
+                    let needSort = false
+                    if needSort {
+                        drangs.sort { range1, range2 in
+                            range1.index < range2.index
                         }
-                        totalDur += subDur
                     }
+                    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)
@@ -98,6 +99,16 @@ public class BFRecordExport {
         self.exporter?.cancel()
     }
     
+    public func clearFileCache(){
+        data?.forEach({ itemModel in
+            itemModel.voiceStickers.forEach { model in
+                if let localPath = model.wavFilePath{
+                    try? FileManager.default.removeItem(atPath: localPath)
+                }
+            }
+        })
+    }
+    
     
     enum DispatchError: Error {
         case timeout
@@ -115,19 +126,19 @@ public class BFRecordExport {
     
     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
-            BFLog(1, message: "hhh- timIn:\(timelineIn), modIn:\(model_in), dur:\(duration)")
+        bgMovieInfo.type = StickerType.VIDEO.rawValue
+        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
+        if let localPath = data?.first?.localPath {
+            bgMovieInfo.locationPath = localPath
         }
+        BFLog(1, message: "hhh- timIn:\(timelineIn), modIn:\(model_in), dur:\(duration)")
         
         return bgMovieInfo
     }
@@ -144,20 +155,40 @@ public class BFRecordExport {
         let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
         BFLog(1, message: "导出视频地址 \(outPutMP4URL)")
         
-        guard let itemModel = data?.first else {
+        guard let itemData = data else {
+            let error = NSError(domain: "err", code: -1, userInfo: ["msg":"voiceStickers count += nil"])
+            self.exportCompletion?(error as Error, nil)
             return
         }
         
         // 处理导出
-        let voiceList = itemModel.voiceStickers
-        let videoStickers = itemModel.videoStickers
+        var voiceList = [PQVoiceModel]()
+        var videoStickers = [PQEditVisionTrackMaterialsModel]()
+        var titleStickers = [PQEditSubTitleModel]()
+        for itemModel in itemData {
+            voiceList.append(contentsOf: itemModel.voiceStickers)
+            videoStickers.append(contentsOf: itemModel.videoStickers)
+            titleStickers.append(contentsOf: itemModel.titleStickers)
+        }
+        
         
         guard let voiceCount = data?.reduce(0, { partialResult, itemModell in
             itemModell.voiceStickers.count + partialResult
         }) else {
             BFLog(1, message: "voiceStickers count += nil")
+            let error = NSError(domain: "err", code: -1, userInfo: ["msg":"voiceStickers count += nil"])
+            self.exportCompletion?(error as Error, nil)
+            return
+        }
+        
+        guard let totalDuration = data?.reduce(0, { partialResult, itemModell in
+            (itemModell.materialDuraion ) + partialResult
+        }) else {
+            let error = NSError(domain: "err", code: -1, userInfo: ["msg":"时长计算出错"])
+            self.exportCompletion?(error as Error, nil)
             return
         }
+        
         // 有录音操作或者多个视频,就会进入合成步骤,否则就是一个没有处理的素材,直接导出就行了
         if voiceCount > 0 || videoStickers.count > 1 {
 
@@ -166,24 +197,12 @@ public class BFRecordExport {
             let filter = videoStickers.map { sticker in
                 PQMovieFilter(movieSticker: sticker)
             }
-            // 有
-//            if let completURL = audioUrl {
-//                let inputAsset = AVURLAsset(url: completURL, options: avAssertOptions)
-////                (audioMix, composition) = PQVideoEditViewModel.setupAudioMix(originAsset: inputAsset, bgmData: nil, videoStickers: videoStickers)
-//                //使用原视频无音版
-//                (audioMix, composition) = PQVideoEditViewModel.setupAudioMix(originAsset: inputAsset, bgmData: nil, videoStickers: nil)
-//
-//                if composition != nil {
-//                    exporter = PQCompositionExporter(asset: composition!, videoComposition: nil, audioMix: audioMix, filters: filter, animationTool: nil, exportURL: outPutMP4URL)
-//                }else {
-//                    exporter = PQCompositionExporter(asset: inputAsset, videoComposition: nil, audioMix: nil, filters: filter, animationTool: nil, exportURL: outPutMP4URL)
-//                }
-//            }
+  
             exporter = PQCompositionExporter(asset: composition, videoComposition: nil, audioMix: audioMix, filters: filter, animationTool: nil, exportURL: outPutMP4URL)
             
-            let asset = data?.first?.baseMaterial
+//            let asset = data?.first?.baseMaterial // 可能为空
 
-            let size = getVideoSize(asset: asset!)
+            let size = UIScreen.main.bounds.size //getVideoSize(asset: asset!)
             var orgeBitRate = Int(size.width * size.height * 3)
             
             for stick in videoStickers {
@@ -198,8 +217,9 @@ public class BFRecordExport {
             }
             
             BFLog(message: "导出设置的码率为:\(orgeBitRate)")
+ 
             if exporter!.prepare(videoSize: size, videoAverageBitRate: orgeBitRate) {
-                exporter!.start(playeTimeRange: CMTimeRange(start: CMTime.zero, end: synthesisAll ? asset?.duration as! CMTime : composition.duration))
+                exporter!.start(playeTimeRange: CMTimeRange(start: CMTime.zero, end: synthesisAll ? CMTime(seconds: totalDuration, preferredTimescale: 100) : composition.duration))
             }
             exporter?.progressClosure = { [weak self] _, _, progress in
                 //            BFLog(message: "正片合成进度 \(progress * 100)%")
@@ -217,7 +237,7 @@ public class BFRecordExport {
                     cShowHUB(superView: nil, msg: ( outSeconds == 0) ? "合成失败请重试。" : "合成成功")
                     self?.exportCompletion?(nil, url)
 
-                }else{
+                } else {
                     let error = NSError(domain: "err", code: -1, userInfo: nil)
                     self?.exportCompletion?(error as Error, nil)
                     cShowHUB(superView: nil, msg: "导出失败")
@@ -228,8 +248,8 @@ public class BFRecordExport {
             }
         } else {
             // 没有处理,直接copy原文件
-            if let ass = data?.first?.baseMaterial{
-                self.exportCompletion?(nil, ass.url)
+            if let localPath = data?.first?.localPath{
+                self.exportCompletion?(nil, URL(fileURLWithPath: localPath))
             }
         }
 

+ 75 - 7
BFRecordScreenKit/Classes/BFRecordItemModel.swift

@@ -6,7 +6,9 @@
 //
 
 import Foundation
+import Photos
 import BFMediaKit
+import BFCommonKit
 
 struct SplitRecordRange {
     var isRecord:Bool = false
@@ -15,16 +17,82 @@ struct SplitRecordRange {
 }
 
 public class BFRecordItemModel: NSObject {
-    var baseMaterial : AVURLAsset?
-    var dealedDurationRanges = [SplitRecordRange]()
+//    var baseMaterial : AVURLAsset?
+    var localPath : String?
+    var materialDuraion: Double = 0.0
+    var fetchCoverImg : ((UIImage)->Void)?
+    var fetchAVUrlAsset : ((AVURLAsset)->Void)?
+    var fetchPlayItem : ((AVPlayerItem)->Void)?
+    var dealedDurationRanges = [SplitRecordRange]()                     // 录音切割的时间区间,合成导出时计算
     public var voiceStickers = [PQVoiceModel]()                         //
-    public var videoStickers = [PQEditVisionTrackMaterialsModel]()
-    public var imageStickers = [PQEditVisionTrackMaterialsModel]()
-    public var titleStickers = [PQEditSubTitleModel]()
+    public var videoStickers = [PQEditVisionTrackMaterialsModel]()      // 合成导出时计算
+    public var imageStickers = [PQEditVisionTrackMaterialsModel]()      //
+    public var titleStickers = [PQEditSubTitleModel]()                  // 字幕贴纸
+    public var coverImg : UIImage?
     public var index = 0
     public var width = 0
     public var height = 0
     
+    func initOriginData(phasset:PHAsset){
+        width = phasset.pixelWidth
+        height = phasset.pixelHeight
+        
+        fetchCoverImage(phasset)
+        fetchPlayItem(phasset)
+        fetchAVUrlAsset(phasset)
+        
+    }
+    
+    func fetchCoverImage(_ phasset:PHAsset) {
+        let option = PHImageRequestOptions()
+        option.isNetworkAccessAllowed = true //允许下载iCloud的图片
+        option.resizeMode = .fast
+        option.deliveryMode = .highQualityFormat
+        PHImageManager.default().requestImage(for: phasset,
+                                       targetSize: CGSize(width: width, height: height),
+                                      contentMode: .aspectFit,
+                                          options: option)
+        { [weak self] (image, nil) in
+             // 设置首帧/封面
+            if image != nil {
+                self?.fetchCoverImg?(image!)
+                self?.coverImg = image
+            }
+        }
+    }
+
+    func fetchPlayItem(_ phasset:PHAsset) {
+        let options = PHVideoRequestOptions()
+        options.isNetworkAccessAllowed = true
+        options.deliveryMode = .automatic
+        if phasset.mediaType == .image {
+            
+        }else if phasset.mediaType == .video{
+            PHImageManager.default().requestPlayerItem(forVideo:phasset, options: options, resultHandler: { [weak self] playerItem, info in
+                
+                guard let item = playerItem else {
+                    cShowHUB(superView: nil, msg: "视频获取失败:\(self?.index ?? 0)")
+                    return
+                }
+                self?.fetchPlayItem?(item)
+            })
+        }
+    }
+    
+    func fetchAVUrlAsset(_ phasset:PHAsset){
+        let options = PHVideoRequestOptions()
+        options.isNetworkAccessAllowed = true
+        options.deliveryMode = .automatic
+
+        PHCachingImageManager().requestAVAsset(forVideo: phasset, options: options, resultHandler: {[weak self] (asset: AVAsset?, audioMix: AVAudioMix?, info) in
+            if let urlAsset = asset as? AVURLAsset {
+                self?.materialDuraion = urlAsset.duration.seconds
+                self?.localPath = ((urlAsset.url.absoluteString).removingPercentEncoding)?.replacingOccurrences(of: "file://", with: "")
+                self?.fetchAVUrlAsset?(urlAsset)
+            }
+        })
+    }
+    
     func generationTimeRanges(needSort:Bool = false) {
         
         dealedDurationRanges.removeAll()
@@ -49,8 +117,8 @@ public class BFRecordItemModel: NSObject {
             dealedDurationRanges.append(SplitRecordRange(isRecord: true, range: range, index: ind ?? -1))
             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))
+        if start < materialDuraion {
+            let range = CMTimeRange(start: CMTime(seconds: start, preferredTimescale: 100), end: CMTime(seconds: materialDuraion, preferredTimescale: 100))
             dealedDurationRanges.append(SplitRecordRange(isRecord: false, range: range, index: -1))
         }
         

+ 26 - 52
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenController.swift

@@ -53,8 +53,7 @@ public class BFRecordScreenController: BFBaseViewController {
         }
     }
     var currentAssetProgress : CMTime = .zero   // 当前素材播放的进度
-    // 视频素材
-    public var avasset:AVURLAsset?
+
     
     //    public var recordList:[PQVoiceModel] = [PQVoiceModel]()
     
@@ -681,7 +680,8 @@ public class BFRecordScreenController: BFBaseViewController {
             }else {
             }
             events.removeLast()
-            if let dur = itemModels[currItemModelIndex].baseMaterial?.duration.seconds,dur > 0 {
+            let dur = itemModels[currItemModelIndex].materialDuraion
+            if dur > 0 {
                 changeProgress(progress: Float(jumpTime / dur))
                 isDragingProgressSlder = false
                 currentPlayRecordIndex = -1
@@ -922,64 +922,38 @@ public class BFRecordScreenController: BFBaseViewController {
             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
+                itemModel.initOriginData(phasset: asset)
+                if index == 0 {
+                    itemModel.fetchCoverImg = {[weak self] img in
+                        self?.setCoverImage(img: img)
                     }
-                    if index == 0 {
+                    itemModel.fetchAVUrlAsset = { [weak self] urlAsset in
+                        DispatchQueue.main.async {[weak self] in
+                            self?.progressThumV.videoAsset = urlAsset
+                            self?.progressThumV.isHidden = false
+                        }
+                    }
+                    itemModel.fetchPlayItem = { [weak self] item in
                         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()
-                    }
                 }
-
+                itemModels.append(itemModel)
                 
-                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
-                            if index == 0 {
-                                self?.progressThumV.videoAsset = urlasset
-                            }
-                            self?.progressThumV.isHidden = false
-                        }
-                    }
-                })
             }
         }
         
     }
     
+    func setCoverImage(img:UIImage) {
+        if let pic = GPUImagePicture(image: img) {
+            let filter = GPUImageFilter()
+            pic.addTarget(filter)
+            filter.addTarget(self.playView)
+            pic.processImage()
+        }
+    }
+    
     func setVideoPlay(item:AVPlayerItem){
         if movie != nil {
             cleanMovieTarget()
@@ -1081,8 +1055,8 @@ public class BFRecordScreenController: BFBaseViewController {
                 vv.removeFromSuperview()
             }
             
-            
-            if let totalDur = sself.itemModels[sself.currItemModelIndex].baseMaterial?.duration.seconds, totalDur > 0, sself.itemModels[sself.currItemModelIndex].voiceStickers.count > 0 {
+            let totalDur = sself.itemModels[sself.currItemModelIndex].materialDuraion
+            if  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

+ 15 - 0
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenImageController.swift

@@ -0,0 +1,15 @@
+//
+//  BFRecordScreenImageController.swift
+//  BFRecordScreenKit
+//
+//  Created by 胡志强 on 2021/12/14.
+//
+
+import Foundation
+
+class BFRecordScreenImageController: BFRecordScreenController {
+    override func startRecord() {
+        super.startRecord()
+        
+    }
+}

+ 15 - 0
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenVideoController.swift

@@ -0,0 +1,15 @@
+//
+//  BFRecordScreenVideoController.swift
+//  BFRecordScreenKit
+//
+//  Created by 胡志强 on 2021/12/14.
+//
+
+import Foundation
+
+class BFRecordScreenVideoController: BFRecordScreenController {
+    override func startRecord() {
+        super.startRecord()
+        
+    }
+}