瀏覽代碼

Merge branch 'master' of https://git.yishihui.com/iOS/BFFramework

jsonwang 3 年之前
父節點
當前提交
87cf7e86fa
共有 22 個文件被更改,包括 334 次插入235 次删除
  1. 2 2
      BFFramework/Classes/Base/View/PQAssetCategoryCell.swift
  2. 1 1
      BFFramework/Classes/Base/View/PQBaseVideoInfoView.swift
  3. 1 1
      BFFramework/Classes/Base/View/PQSectionHeadView.swift
  4. 3 0
      BFFramework/Classes/PModels/editDarftModels/PQEditVisionTrackMaterialsModel.swift
  5. 4 0
      BFFramework/Classes/PQGPUImage/Source/FramebufferCache.swift
  6. 2 1
      BFFramework/Classes/PQGPUImage/akfilters/PQImageFilter.swift
  7. 7 5
      BFFramework/Classes/PQGPUImage/akfilters/PQMovieFilter.swift
  8. 0 1
      BFFramework/Classes/PQGPUImage/akfilters/PQMovieInput.swift
  9. 3 1
      BFFramework/Classes/PQGPUImage/akfilters/PQTextFilter.swift
  10. 6 6
      BFFramework/Classes/PQGPUImage/akfilters/Tools/PQCompositionExporter.swift
  11. 40 36
      BFFramework/Classes/Stuckpoint/Controller/PQStuckPointEditerController.swift
  12. 225 148
      BFFramework/Classes/Stuckpoint/Controller/PQStuckPointPublicController.swift
  13. 6 6
      BFFramework/Classes/Stuckpoint/View/PQCustomSpeedSettingView.swift
  14. 2 2
      BFFramework/Classes/Stuckpoint/View/PQEditPublicCoverImageView.swift
  15. 2 2
      BFFramework/Classes/Stuckpoint/View/PQEditPublicTitleView.swift
  16. 6 6
      BFFramework/Classes/Stuckpoint/View/PQSelecteMusicView.swift
  17. 2 2
      BFFramework/Classes/Stuckpoint/View/PQStuckPointCuttingView.swift
  18. 15 8
      BFFramework/Classes/Stuckpoint/View/PQStuckPointLoadingView.swift
  19. 1 1
      BFFramework/Classes/Stuckpoint/View/PQStuckPointMaterialHeadView.swift
  20. 2 2
      BFFramework/Classes/Stuckpoint/View/PQStuckPointMusicContentCell.swift
  21. 3 3
      BFFramework/Classes/Stuckpoint/View/PQStuckPointSearchEmptyCell.swift
  22. 1 1
      BFFramework/Classes/Stuckpoint/ViewModel/PQStuckPointViewModel.swift

+ 2 - 2
BFFramework/Classes/Base/View/PQAssetCategoryCell.swift

@@ -87,12 +87,12 @@ public class PQAssetCategoryCell: UICollectionViewCell {
             make.centerY.equalToSuperview()
         }
         categoryNameLab.snp.makeConstraints { make in
-            make.left.equalTo(videoImageView.snp_right).offset(margin)
+            make.left.equalTo(videoImageView.snp.right).offset(margin)
             make.width.lessThanOrEqualTo(maxW)
             make.centerY.equalToSuperview()
         }
         countLab.snp.remakeConstraints { make in
-            make.left.equalTo(categoryNameLab.snp_right)
+            make.left.equalTo(categoryNameLab.snp.right)
             make.centerY.equalToSuperview()
             make.width.equalTo(countW)
         }

+ 1 - 1
BFFramework/Classes/Base/View/PQBaseVideoInfoView.swift

@@ -75,7 +75,7 @@ open class PQBaseVideoInfoView: UIView {
         }
         titleLab.snp.makeConstraints { make in
             make.top.equalTo(imageView)
-            make.left.equalTo(imageView.snp_right).offset(margin)
+            make.left.equalTo(imageView.snp.right).offset(margin)
             make.right.equalToSuperview().offset(-margin)
         }
     }

+ 1 - 1
BFFramework/Classes/Base/View/PQSectionHeadView.swift

@@ -86,7 +86,7 @@ public class PQSectionHeadView: UIView {
             make.centerY.equalToSuperview()
         }
         titleLab.snp.makeConstraints { make in
-            make.left.equalTo(lineView.snp_right).offset(lineW * 4)
+            make.left.equalTo(lineView.snp.right).offset(lineW * 4)
             make.centerY.equalToSuperview()
         }
         clearBtn.snp.makeConstraints { make in

+ 3 - 0
BFFramework/Classes/PModels/editDarftModels/PQEditVisionTrackMaterialsModel.swift

@@ -109,6 +109,9 @@ public class PQEditVisionTrackMaterialsModel: PQEditBaseModel {
     //视频素材被切分的段数,视频素材才有效,默认为1 不切分
     public var clipCount:Int = 1
 
+    deinit {
+        BFLog(1, message: "sticker release")
+    }
    public required init() {
         super.init()
         materialDurationFit = PQEditmaterialDurationFitModel()

+ 4 - 0
BFFramework/Classes/PQGPUImage/Source/FramebufferCache.swift

@@ -49,6 +49,10 @@ public class FramebufferCache {
 
     public func purgeAllUnassignedFramebuffers() {
         framebufferCache.removeAll()
+#if os(iOS)
+        CVOpenGLESTextureCacheFlush(sharedImageProcessingContext.coreVideoTextureCache, 0);
+        print("release buffer")
+#endif
     }
 
     func returnToCache(_ framebuffer: Framebuffer) {

+ 2 - 1
BFFramework/Classes/PQGPUImage/akfilters/PQImageFilter.swift

@@ -18,13 +18,14 @@ open class PQImageFilter: PQBaseFilter {
     var mSticker: PQEditVisionTrackMaterialsModel?
     var newImage: UIImage?
     deinit {
-        BFLog(message: "image filter deinit 析构掉~")
+        BFLog(1, message: "image filter deinit 析构掉~")
         newImage = nil
 
         if imageTexture != 0 {
             glDeleteTextures(1, &imageTexture)
             imageTexture = 0
         }
+        
     }
 
     init(sticker: PQEditVisionTrackMaterialsModel, isExport: Bool = true, showUISize: CGSize = .zero) {

+ 7 - 5
BFFramework/Classes/PQGPUImage/akfilters/PQMovieFilter.swift

@@ -113,7 +113,7 @@ class PQMovieFilter: PQBaseFilter {
     var framebufferIndex:Int = 0
 
     deinit {
-//        BFLog(1, message: "movie filter deinit")
+        BFLog(1, message: "movie filter release")
         clearData()
     }
 
@@ -401,8 +401,8 @@ class PQMovieFilter: PQBaseFilter {
             lastImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer!)!
 //            }
 
-            sharedImageProcessingContext.runOperationSynchronously {
-                self.renderPixelBuffler(movieFrame: CMSampleBufferGetImageBuffer(sampleBuffer!)!, withSampleTime: currentTime)
+            sharedImageProcessingContext.runOperationSynchronously { [weak self] in
+                self?.renderPixelBuffler(movieFrame: CMSampleBufferGetImageBuffer(sampleBuffer!)!, withSampleTime: currentTime)
             }
             return
         } else {
@@ -423,8 +423,10 @@ class PQMovieFilter: PQBaseFilter {
                     BFLog(2, message: "处理显示定帧")
                     let currTime = CMTimeGetSeconds(currentTime)
                     BFLog(2, message: "process time is \(currTime)")
-                    sharedImageProcessingContext.runOperationSynchronously {
-                        renderPixelBuffler(movieFrame: lastImageBuffer!, withSampleTime: currentTime)
+                    sharedImageProcessingContext.runOperationSynchronously { [weak self] in
+                        if let imgBuffer = self?.lastImageBuffer {
+                            renderPixelBuffler(movieFrame: imgBuffer, withSampleTime: currentTime)
+                        }
                     }
                 }
             }

+ 0 - 1
BFFramework/Classes/PQGPUImage/akfilters/PQMovieInput.swift

@@ -518,7 +518,6 @@ public class PQMovieInput: ImageSource {
          case cancelled = 4
          */
         BFLog(2, message: "音频解码状态\(assetReader.status.rawValue)")
-        BFLog(1, message:  "音频解码状态\(assetReader.status.rawValue)")
         
         autoreleasepool {
             guard let sampleBuffer = audioTrackOutput.copyNextSampleBuffer(),CMSampleBufferIsValid(sampleBuffer) else {

+ 3 - 1
BFFramework/Classes/PQGPUImage/akfilters/PQTextFilter.swift

@@ -74,7 +74,9 @@ open class PQTextFilter: PQBaseFilter {
                 let size: CGSize = subtitleLab.bounds.size
                 //如果传0,则这个参数会用设备的scale,
                 UIGraphicsBeginImageContextWithOptions(size, false, 2)
-                subtitleLab.layer.render(in: UIGraphicsGetCurrentContext()!)
+                if let context = UIGraphicsGetCurrentContext() {
+                    subtitleLab.layer.render(in: context)
+                }
 
                 self?.subtitleImage = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
                 UIGraphicsEndImageContext()

+ 6 - 6
BFFramework/Classes/PQGPUImage/akfilters/Tools/PQCompositionExporter.swift

@@ -14,7 +14,7 @@ import BFCommonKit
 public class PQCompositionExporter {
     public var progressClosure: ((_ currTime: Float, _ duration: Float, _ progress: Float) -> Void)?
 
-    public var completion: ((_ url: URL) -> Void)?
+    public var completion: ((_ url: URL?) -> Void)?
 
     public let asset: AVAsset
     public let videoComposition: AVVideoComposition?
@@ -297,14 +297,14 @@ public class PQCompositionExporter {
 
     public func start(playeTimeRange:CMTimeRange = CMTimeRange.init()) {
         input?.completion = { [unowned self] in
-            self.output?.finishRecording { [unowned self] in
+            self.output?.finishRecording { [weak self] in
                 BFLog(message: "导出视频完成发通知")
                 DispatchQueue.main.async {
-                    if let url = self.tmpExportURL {
-                        self.handleCaption(for: AVURLAsset(url: url, options: nil))
+                    if let url = self?.tmpExportURL {
+                        self?.handleCaption(for: AVURLAsset(url: url, options: nil))
                     } else {
-                        self.progressClosure?(0, 0, 1)
-                        self.completion?(self.exportURL)
+                        self?.progressClosure?(0, 0, 1)
+                        self?.completion?(self?.exportURL)
                     }
                 }
             }

+ 40 - 36
BFFramework/Classes/Stuckpoint/Controller/PQStuckPointEditerController.swift

@@ -601,6 +601,7 @@ class PQStuckPointEditerController: PQBaseViewController {
 //        }
 
         self.musicEditBGView.pausePlayer()
+        sharedImageProcessingContext.framebufferCache.purgeAllUnassignedFramebuffers()
 
         if sender == jumpPointBtn && selectedTotalDuration < 6 && selectedDataCount != selectedImageDataCount && reCreateVideoData == nil{
             cShowHUB(superView: view, msg: "素材时长需要大于6秒才\n可选择“跳跃卡点”模式")
@@ -619,7 +620,7 @@ class PQStuckPointEditerController: PQBaseViewController {
             speedSettingView.snp.remakeConstraints { make in
                 make.left.equalToSuperview().offset(16)
                 make.right.equalToSuperview()
-                make.top.equalTo(onlyMusicBtn.snp_bottom).offset(10)
+                make.top.equalTo(onlyMusicBtn.snp.bottom).offset(10)
                 make.height.equalTo(sender.tag == 1 ? 44 : 30)
             }
             speedSettingView.isHidden = false
@@ -639,7 +640,7 @@ class PQStuckPointEditerController: PQBaseViewController {
             speedSettingView.snp.remakeConstraints { make in
                 make.left.equalToSuperview().offset(16)
                 make.right.equalToSuperview()
-                make.top.equalTo(onlyMusicBtn.snp_bottom).offset(10)
+                make.top.equalTo(onlyMusicBtn.snp.bottom).offset(10)
                 make.height.equalTo(30)
             }
 
@@ -813,30 +814,30 @@ class PQStuckPointEditerController: PQBaseViewController {
 
         musicEditerBtn.snp.makeConstraints { make in
             make.right.equalToSuperview().offset(-100)
-            make.top.equalTo(pointEditerBtn.snp_top)
+            make.top.equalTo(pointEditerBtn.snp.top)
             make.height.equalTo(24)
             make.width.equalTo(24)
         }
         pointEditBGView.snp.makeConstraints { make in
             make.left.right.equalTo(view)
-            make.bottom.equalTo(pointEditerBtn.snp_top).offset(-7)
+            make.bottom.equalTo(pointEditerBtn.snp.top).offset(-7)
             make.height.equalTo(290)
         }
         musicEditBGView.snp.makeConstraints { make in
             make.left.right.equalToSuperview()
-            make.bottom.equalTo(pointEditerBtn.snp_top).offset(-7)
+            make.bottom.equalTo(pointEditerBtn.snp.top).offset(-7)
             make.height.equalTo(290)
         }
         
         optionlineView.snp.makeConstraints { make in
             make.left.right.equalToSuperview()
-            make.bottom.equalTo(pointEditBGView.snp_bottom).offset(-1)
+            make.bottom.equalTo(pointEditBGView.snp.bottom).offset(-1)
             make.height.equalTo(1)
         }
 
         stuckPointCuttingView.snp.makeConstraints { make in
             make.left.right.equalToSuperview()
-            make.bottom.equalTo(musicEditBGView.snp_bottom).offset(-1)
+            make.bottom.equalTo(musicEditBGView.snp.bottom).offset(-1)
             make.height.equalTo(85)
         }
         pointEditRemindLab.snp.makeConstraints { make in
@@ -847,7 +848,7 @@ class PQStuckPointEditerController: PQBaseViewController {
         }
         speedStuckBtn.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(16)
-            make.top.equalTo(pointEditRemindLab.snp_bottom).offset(8)
+            make.top.equalTo(pointEditRemindLab.snp.bottom).offset(8)
             make.height.equalTo(80)
             make.width.equalTo(80)
         }
@@ -858,8 +859,8 @@ class PQStuckPointEditerController: PQBaseViewController {
         }
 
         jumpPointBtn.snp.makeConstraints { make in
-            make.left.equalTo(speedStuckBtn.snp_right).offset(10)
-            make.top.equalTo(speedStuckBtn.snp_top)
+            make.left.equalTo(speedStuckBtn.snp.right).offset(10)
+            make.top.equalTo(speedStuckBtn.snp.top)
             make.height.equalTo(80)
             make.width.equalTo(80)
         }
@@ -870,8 +871,8 @@ class PQStuckPointEditerController: PQBaseViewController {
         }
 
         onlyMusicBtn.snp.makeConstraints { make in
-            make.left.equalTo(jumpPointBtn.snp_right).offset(10)
-            make.top.equalTo(speedStuckBtn.snp_top)
+            make.left.equalTo(jumpPointBtn.snp.right).offset(10)
+            make.top.equalTo(speedStuckBtn.snp.top)
             make.height.equalTo(80)
             make.width.equalTo(64)
         }
@@ -879,7 +880,7 @@ class PQStuckPointEditerController: PQBaseViewController {
         speedSettingView.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(16)
             make.right.equalToSuperview()
-            make.top.equalTo(onlyMusicBtn.snp_bottom).offset(10)
+            make.top.equalTo(onlyMusicBtn.snp.bottom).offset(10)
             make.height.equalTo(44)
         }
         speedTitleLab.snp.makeConstraints { make in
@@ -890,7 +891,7 @@ class PQStuckPointEditerController: PQBaseViewController {
         }
         sustomSwitchView.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(16)
-            make.top.equalTo(speedTitleLab.snp_bottom).offset(8)
+            make.top.equalTo(speedTitleLab.snp.bottom).offset(8)
             make.height.equalTo(30)
             make.width.equalTo(180)
         }
@@ -913,6 +914,10 @@ class PQStuckPointEditerController: PQBaseViewController {
         if materialVC != nil, materialVC?.isToPublicHandle != nil {
             materialVC?.isToPublicHandle!(isReCreate, selectedTotalDuration, selectedDataCount, selectedImageDataCount, mStickers, stuckPointMusicData, tempModel)
         } else {
+            if finallyStuckPoints.count == 0 {
+                cShowHUB(superView: nil, msg: "无卡点信息,返回重新选择音乐")
+                return
+            }
             let videoExporter = PQStuckPointPublicController()
             videoExporter.rhythmMode = currentCreateStickersModel
             videoExporter.syncedUpVideoSpeedMin = modelSpeed_maxSpeed
@@ -976,11 +981,14 @@ class PQStuckPointEditerController: PQBaseViewController {
     // 计算拼接音乐的开始和结束点
     func getClipAudioRange() -> CMTimeRange {
         // 设置音乐的拼接范围,开始:推荐的卡点  结束:点到的倒数第二位
-        let lastSecondPoint = Float((stuckPointMusicData!.rhythmSdata[0].pointTimes[stuckPointMusicData!.rhythmSdata[0].pointTimes.count - 2])) / Float(BASE_FILTER_TIMESCALE)
-        let clipAudioRange =
-            CMTimeRange(start: CMTime(value: CMTimeValue(Float(stuckPointMusicData?.startTime ?? 0) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: CMTimeValue((Float(lastSecondPoint)) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE))
-
-        return clipAudioRange
+        if stuckPointMusicData!.rhythmSdata.count > 0 && stuckPointMusicData?.rhythmSdata[0].pointTimes.count ?? 0 > 2 {
+            let lastSecondPoint = Float((stuckPointMusicData!.rhythmSdata[0].pointTimes[stuckPointMusicData!.rhythmSdata[0].pointTimes.count - 2])) / Float(BASE_FILTER_TIMESCALE)
+            let clipAudioRange =
+                CMTimeRange(start: CMTime(value: CMTimeValue(Float(stuckPointMusicData?.startTime ?? 0) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: CMTimeValue((Float(lastSecondPoint)) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE))
+            
+            return clipAudioRange
+        }
+        return CMTimeRange(start: CMTime(value: 0, timescale: 1), duration: CMTime(value: 1, timescale: 1))
     }
 
     // 设置播放器
@@ -1029,12 +1037,7 @@ class PQStuckPointEditerController: PQBaseViewController {
     
     func settingPlayerView() {
         stuckPointCuttingView.resetDefaultsColor()
-        synchroMarskView.removeFromSuperview()
-        if synchroMarskView.superview == nil {
-            UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
-            synchroMarskView.show()
-        }
-
+        
         // 1,设置播放器的显示区域 和画布大小
         //  - 按第一个素材尺寸自适应
         playerView.pause()
@@ -1135,7 +1138,8 @@ class PQStuckPointEditerController: PQBaseViewController {
 
                 self.playerView.play(pauseFirstFrame: false, playeTimeRange: CMTimeRange(start: self.playeTimeRange.start, end: self.playeTimeRange.end))
                 self.stuckPointCuttingView.updateProgress(progress: 0)
-                
+                self.synchroMarskView.removeMarskView()
+
                 let end4: TimeInterval = Date().timeIntervalSince1970
                 BFLog(message: " playerView.play tiskskskskme  \(end4 - end3)")
 
@@ -1143,7 +1147,7 @@ class PQStuckPointEditerController: PQBaseViewController {
                 self.playerView.progress = { [weak self] currentTime, tatolTime, percent in
                     if percent == 1 {
                         self?.stuckPointCuttingView.resetDefaultsColor(clearData: false)
-
+                        sharedImageProcessingContext.framebufferCache.purgeAllUnassignedFramebuffers()
                         return
                     }
                     if(CMTimeGetSeconds(self?.playeTimeRange.duration ?? .zero) <= 0.0){
@@ -1156,7 +1160,6 @@ class PQStuckPointEditerController: PQBaseViewController {
 
                     self?.stuckPointCuttingView.updateProgress(progress: CGFloat(progress))
 
-                    self?.synchroMarskView.removeMarskView()
                 }
             }
         }
@@ -1170,6 +1173,7 @@ class PQStuckPointEditerController: PQBaseViewController {
             exportSession.cancelExport()
         }
         self.synchroMarskView.removeMarskView()
+        sharedImageProcessingContext.framebufferCache.purgeAllUnassignedFramebuffers()
         BFLog(1, message: "卡点视频预览界面release")
     }
 }
@@ -1456,6 +1460,10 @@ extension PQStuckPointEditerController {
     /// - Parameter seed: 档位速度
     /// - Returns: 最后使用的卡点
     func getUsedStuckPoint(seed: Int) -> Array<Float> {
+        if !(stuckPointMusicData!.rhythmSdata.count > 0 && stuckPointMusicData!.rhythmSdata[0].pointTimes.count > 1){
+            return []
+        }
+        
         // 推荐卡点数
         var stuckPoints: Array = Array<Float>.init()
 
@@ -1768,10 +1776,7 @@ extension PQStuckPointEditerController {
     /// - Returns: <#description#>
     func synchroMusicInfoData(resetSelectIndex:Bool = true) {
         if (stuckPointMusicData?.rhythmSdata.count ?? 0) <= 0 {
-            if synchroMarskView.superview == nil {
-                UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
-                synchroMarskView.show()
-            }
+            synchroMarskView.show()
             PQStuckPointViewModel.stuckPointMusicDetailData(musicId: stuckPointMusicData?.musicId ?? "", originType: stuckPointMusicData?.originType ?? 1) { [weak self] newMusicData, _ in
                 if newMusicData != nil, (newMusicData?.rhythmSdata.count ?? 0) > 0 {
                     self?.isStuckPointDataSuccess = true
@@ -1806,11 +1811,10 @@ extension PQStuckPointEditerController {
                 }
             }
         } else if stuckPointMusicData?.localPath == nil || (stuckPointMusicData?.localPath?.count ?? 0) > 0 {
-            if synchroMarskView.superview == nil {
-                UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
-                synchroMarskView.show()
-            }
+
+            synchroMarskView.show()
             isStuckPointDataSuccess = true
+            
             PQDownloadManager.downLoadFile(url: stuckPointMusicData?.musicPath ?? "") { [weak self] filePath, error in
                 if error == nil, filePath != nil {
                     self?.isSynchroMusicInfoSuccess = true

+ 225 - 148
BFFramework/Classes/Stuckpoint/Controller/PQStuckPointPublicController.swift

@@ -433,8 +433,24 @@ class PQStuckPointPublicController: PQBaseViewController {
         saveVideoTipsLabel.textAlignment = .center
         saveVideoTipsLabel.font = UIFont.boldSystemFont(ofSize: 17)
         saveVideoTipsLabel.text = "视频保存中..."
+        saveVideoTipsLabel.sizeToFit()
         return saveVideoTipsLabel
     }()
+    
+    // 保存重试
+    lazy var saveRetryBtn: UIButton = {
+        let finishedBtn = UIButton(type: .custom)
+        finishedBtn.setTitle("重试", for: .normal)
+        finishedBtn.setTitleColor(UIColor.white, for: .normal)
+        finishedBtn.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .medium)
+        finishedBtn.backgroundColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
+        finishedBtn.tag = 97
+        finishedBtn.isHidden = true
+        finishedBtn.addCorner(corner: 5)
+        finishedBtn.addTarget(self, action: #selector(btnClick(sender:)), for: .touchUpInside)
+        return finishedBtn
+
+    }()
 
     override func backBtnClick() {
         if isExportSuccess {
@@ -461,6 +477,11 @@ class PQStuckPointPublicController: PQBaseViewController {
             }
         }
     }
+    
+    var isTestSaveFailed = false
+    override func rightBtnClick(sender:UIButton){
+        isTestSaveFailed = true
+    }
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -468,6 +489,8 @@ class PQStuckPointPublicController: PQBaseViewController {
         addNotification(self, selector: #selector(uploadSuccess(notify:)), name: cUploadSuccessKey, object: nil)
         PQNotification.addObserver(self, selector: #selector(didBecomeActiveNotification), name: UIApplication.didBecomeActiveNotification, object: nil)
         leftButton(image: nil, tintColor: PQBFConfig.shared.styleTitleColor)
+        // Test
+        rightButtonItem(image: nil, title: "TestSave失败")
         navHeadImageView?.backgroundColor = UIColor.clear
         lineView?.removeFromSuperview()
         view.addSubview(bgTopView)
@@ -510,7 +533,18 @@ class PQStuckPointPublicController: PQBaseViewController {
         
         view.addSubview(saveVideoTipsBgView)
         saveVideoTipsBgView.addSubview(saveVideoTipsLabel)
-       
+        saveVideoTipsBgView.addSubview(saveRetryBtn)
+        saveVideoTipsLabel.snp.makeConstraints { make in
+            make.top.height.equalToSuperview()
+            make.centerX.equalToSuperview()
+        }
+        saveRetryBtn.snp.makeConstraints { make in
+            make.left.equalTo(saveVideoTipsLabel.snp.right).offset(10)
+            make.top.equalTo(6)
+            make.bottom.equalTo(-6)
+            make.width.equalTo(50)
+        }
+
  
         coverImageTitle.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(settingCoverImage)))
         coverImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(settingCoverImage)))
@@ -532,19 +566,19 @@ class PQStuckPointPublicController: PQBaseViewController {
             make.height.equalTo(22)
         }
         shareWechatBtn.snp.makeConstraints { make in
-            make.right.equalTo(view.snp_centerX).offset(-cDefaultMargin)
+            make.right.equalTo(view.snp.centerX).offset(-cDefaultMargin)
             make.width.equalTo(164)
             make.height.equalTo(52)
-            make.bottom.equalTo(finishedBtn.snp_top).offset(-32)
+            make.bottom.equalTo(finishedBtn.snp.top).offset(-32)
         }
         shareFriendBtn.snp.makeConstraints { make in
-            make.left.equalTo(view.snp_centerX).offset(cDefaultMargin)
+            make.left.equalTo(view.snp.centerX).offset(cDefaultMargin)
             make.width.bottom.height.equalTo(shareWechatBtn)
         }
 
         inputBackView.snp.makeConstraints { make in
             make.centerX.equalToSuperview()
-            make.bottom.equalTo(shareWechatBtn.snp_top).offset(-16)
+            make.bottom.equalTo(shareWechatBtn.snp.top).offset(-16)
             make.width.equalTo(343)
             make.height.equalTo(109)
         }
@@ -566,21 +600,21 @@ class PQStuckPointPublicController: PQBaseViewController {
         coverImageTitle.snp.makeConstraints { make in
             make.left.equalToSuperview()
             make.width.equalTo(50)
-            make.top.equalTo(coverImageView.snp_bottom).offset(isWidth ? 0 : -23)
+            make.top.equalTo(coverImageView.snp.bottom).offset(isWidth ? 0 : -23)
             make.height.equalTo(23)
         }
 
         remindLab.snp.makeConstraints { make in
             make.centerX.equalToSuperview()
-            make.bottom.equalTo(inputBackView.snp_top).offset(-16).priorityHigh()
+            make.bottom.equalTo(inputBackView.snp.top).offset(-16).priorityHigh()
             make.height.equalTo(44)
             make.top.greaterThanOrEqualTo(5)
-            make.bottom.lessThanOrEqualTo(inputBackView.snp_top).offset(-5)
+            make.bottom.lessThanOrEqualTo(inputBackView.snp.top).offset(-5)
         }
 
         titleLabel.snp.makeConstraints { make in
             make.height.equalTo(48)
-            make.left.equalTo(coverImageView.snp_right).offset(12)
+            make.left.equalTo(coverImageView.snp.right).offset(12)
             make.right.equalToSuperview().offset(-14)
             make.top.equalToSuperview().offset(10)
         }
@@ -588,7 +622,7 @@ class PQStuckPointPublicController: PQBaseViewController {
         pinView.snp.makeConstraints { make in
             make.height.width.equalTo(72)
             make.right.equalToSuperview()
-            make.bottom.equalTo(inputBackView.snp_bottom)
+            make.bottom.equalTo(inputBackView.snp.bottom)
         }
 
         publicTitleView.snp.makeConstraints { make in
@@ -651,7 +685,7 @@ class PQStuckPointPublicController: PQBaseViewController {
     }
 
     deinit {
-        BFLog(message: "发布界面析构 1")
+        BFLog(1, message: "发布界面析构release")
         view.endEditing(true)
         PQNotification.removeObserver(self)
         // 取消导出
@@ -867,7 +901,7 @@ extension PQStuckPointPublicController {
             }
         }
         exporter.completion = { [weak self] url in
-            BFLog(message: "无水印的视频导出完成: \(url) 生成视频时长为:\(CMTimeGetSeconds(AVAsset(url: url).duration))")
+            BFLog(message: "无水印的视频导出完成: \(String(describing: url)) 生成视频时长为:\(CMTimeGetSeconds(AVAsset(url: (url ?? URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!)).duration))")
 
             // 导出完成后取消导出
             if self?.exporter != nil {
@@ -916,12 +950,14 @@ extension PQStuckPointPublicController {
     /// - Returns: <#description#>
     func saveStuckPointVideo() {
         
-        if(saveMovieLocalURL == nil){
+        if(saveMovieLocalURL == nil || isTestSaveFailed){
             BFLog(message: "保存相册的视频导出地址无效!!!")
-            saveVideoTipsLabel.text = "视频已经保存失败"
-            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { [weak self] in
-                self?.saveVideoTipsBgView.isHidden = true
-            }
+            saveVideoTipsLabel.text = "视频保存失败"
+            saveRetryBtn.isHidden = false
+            saveVideoTipsBgView.isHidden = false
+//            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { [weak self] in
+//                self?.saveVideoTipsBgView.isHidden = true
+//            }
             return
         }
         let authStatus = PHPhotoLibrary.authorizationStatus()
@@ -933,16 +969,18 @@ extension PQStuckPointPublicController {
                 DispatchQueue.main.async { [weak self] in
                     if self?.view != nil {
                         if isFinished {
-                            self?.saveVideoTipsLabel.text = "视频已经保存到相册"
+                            self?.saveVideoTipsLabel.text = "视频已保存到相册"
+                            self?.saveRetryBtn.isHidden = true
                             DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { [weak self] in
                                 self?.saveVideoTipsBgView.isHidden = true
                             }
                          
                         } else {
-                            self?.saveVideoTipsLabel.text = "视频已经保存失败"
-                            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { [weak self] in
-                                self?.saveVideoTipsBgView.isHidden = true
-                            }
+                            self?.saveVideoTipsLabel.text = "视频保存失败"
+                            self?.saveRetryBtn.isHidden = false
+//                            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.8) { [weak self] in
+//                                self?.saveVideoTipsBgView.isHidden = true
+//                            }
 
                             
                         }
@@ -1083,7 +1121,7 @@ extension PQStuckPointPublicController {
         DispatchQueue.global().async {
             PQBaseViewModel.getStsToken { [weak self] response, _ in
                 if response == nil {
-                    self?.showUploadRemindView(isNetCollected: false, msg: "获取数据失败了哦~")
+                    self?.showUploadRemindView(isNetCollected: false, msg: "token获取数据失败了哦~")
                     return
                 }
                 // 更新进度
@@ -1117,10 +1155,10 @@ extension PQStuckPointPublicController {
                 if code == 6 { // 无网
                     let uploadRequest: OSSMultipartUploadRequest? = PQAliOssUtil.shared.allTasks[self?.uploadData?.videoBucketKey ?? ""]
                     if !(uploadRequest != nil && "\(uploadRequest?.callbackParam["code"] ?? "0")" == "1") {
-                        self?.showUploadRemindView()
+                        self?.showUploadRemindView(msg:"aliOss")
                     }
                 } else if code == 260 {
-                    self?.showUploadRemindView(isNetCollected: false)
+                    self?.showUploadRemindView(isNetCollected: false, msg:"aliOss")
                 } else if code != 1 {
                     // 上传失败-播放视频
                     self?.publicEnd(isError: true)
@@ -1382,7 +1420,11 @@ extension PQStuckPointPublicController {
             navigationController?.viewControllers = [(navigationController?.viewControllers.first)!]
             // 发送通知
             postNotification(name: cFinishedPublishedNotiKey)
-
+        case 97:
+            saveRetryBtn.isHidden = true
+            saveVideoTipsLabel.text = "视频保存中..."
+            isTestSaveFailed = false
+            self.saveStuckPointVideo()
         default:
             break
         }
@@ -1392,14 +1434,46 @@ extension PQStuckPointPublicController {
     /// - Parameters:
     ///   - isNetCollected: <#isNetCollected description#>
     ///   - msg: <#msg description#>
-    func showUploadRemindView(isNetCollected _: Bool = true, msg _: String? = nil) {
+    func showUploadRemindView(isNetCollected _: Bool = true, msg: String? = nil) {
         view.endEditing(true)
-//        PQUploadRemindView.showUploadRemindView(title: isNetCollected ? "上传中断" : "上传失败", summary: (isNetCollected ? "似乎已断开与互联网的连接" : (msg != nil ? msg : "视频文件已丢失"))!, confirmTitle: isNetCollected ? "重新连接网络" : "重新上传") { [weak self] _, _ in
-//            if isNetCollected {
-//                openAppSetting()
-//            } else {
-//                self?.navigationController?.popToViewController((self?.navigationController?.viewControllers[1])!, animated: true)
-//            }
+        
+        let emptyData = PQEmptyModel()
+        emptyData.isRefreshHidden = false
+        emptyData.title = "上传失败"
+        emptyData.titleColor = UIColor.hexColor(hexadecimal: "#353535")
+        emptyData.summary = "建议切换 WIFI/移动网络后再重试"
+        emptyData.summaryColor = UIColor.hexColor(hexadecimal: "#353535")
+        emptyData.refreshBgColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
+        emptyData.refreshTitle = NSMutableAttributedString(string: "立即重试", attributes: [.foregroundColor:UIColor.white])
+        emptyData.emptySoureImage = UIImage.moduleImage(named: "stuckPoint_video_empty", moduleName: "BFMaterialKit",isAssets: false)
+        emptyData.netDisRefreshBgColor = UIColor.hexColor(hexadecimal: "#FA6400")
+        emptyData.netDisTitle = "内容加载失败"
+        emptyData.netDisTitleColor = UIColor.hexColor(hexadecimal: "#333333")
+        emptyData.netemptyDisImage = UIImage.moduleImage(named: "empty_netDis_icon", moduleName: "BFMaterialKit",isAssets: false)
+        emptyData.netDisRefreshTitle = NSMutableAttributedString(string: "重新加载", attributes: [.font: UIFont.systemFont(ofSize: 16, weight: .medium), .foregroundColor: UIColor.white])
+        
+        let emptyRemindView = PQEmptyRemindView(frame: CGRect(x: 0, y: cDevice_iPhoneNavBarAndStatusBarHei, width: view.frame.width, height: view.frame.height - cDevice_iPhoneNavBarAndStatusBarHei))
+//        emptyRemindView.isHidden = true
+        emptyRemindView.emptyData = emptyData
+        emptyRemindView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
+        emptyRemindView.fullRefreshBloc = {[weak self] _, _ in
+            if emptyRemindView.refreshBtn.currentAttributedTitle?.string == "立即重试" {
+                emptyRemindView.isHidden = true
+                // 重试逻辑
+                if let message = msg{
+                    if message.contains("token") {
+                        self?.uploadVideo()
+                    }else if message.contains("aliOss"){
+                        self?.uploadVideo()
+                    }
+                }
+            }
+        }
+        emptyRemindView.refreshBtn.addCorner(corner: 4)
+        view.addSubview(emptyRemindView)
+    
+//        PQRemindView.showUploadRemindView(title: "上传失败", summary: (msg != nil ? msg! : "视频文件已丢失"), confirmTitle:  "立即重试") { [weak self] _, _ in
+//            self?.navigationController?.popToViewController((self?.navigationController?.viewControllers[1])!, animated: true)
 //        }
     }
 
@@ -1648,12 +1722,12 @@ extension PQStuckPointPublicController {
             watermarkMovieExporter.start(playeTimeRange: playeTimeRange)
             BFLog(message: "开始导出")
         }
-        watermarkMovieExporter.progressClosure = { [weak self] _, _, progress in
+        watermarkMovieExporter.progressClosure = { _, _, progress in
             BFLog(message: "带水印的合成进度 \(progress) ")
           
         }
         watermarkMovieExporter.completion = { [weak self] url in
-            BFLog(message: "有水印的视频导出完成: \(url) 生成视频时长为:\(CMTimeGetSeconds(AVAsset(url: url).duration))")
+            BFLog(message: "有水印的视频导出完成: \(String(describing: url)) 生成视频时长为:\(CMTimeGetSeconds(AVAsset(url: url ?? URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!).duration))")
 
             // 导出完成后取消导出
             if self?.watermarkMovieExporter != nil {
@@ -1705,125 +1779,128 @@ extension PQStuckPointPublicController {
         BFLog(message: "头像的网络地址\(BFLoginUserInfo.shared.avatarUrl)")
         let avatarFilePath = NSHomeDirectory().appending("/Documents/").appending("user_avatar.jpg")
         
-        ImageDownloader.default.downloadImage(with: URL(string: BFLoginUserInfo.shared.avatarUrl)!, options: nil) { result in
+        // warning:给默认头像吧
+        ImageDownloader.default.downloadImage(with: URL(string: BFLoginUserInfo.shared.avatarUrl)!, options: nil) {[weak self] result in
+            var image : UIImage?
             switch result {
             case let .success(imageResult):  
-                   let image = UIImage.nx_circleImage(imageResult.image)
-                   if(image == nil){
-                       BFLog(message: "image date is error!!")
-                       return
-                   }
-                   UIImage.saveImage(currentImage: image!, outFilePath: avatarFilePath)
+                image = UIImage.nx_circleImage(imageResult.image)
                    
-                   //1,背景视频素材
-                   let bgMovieInfo:PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel.init()
-                   bgMovieInfo.type = StickerType.VIDEO.rawValue
-                   bgMovieInfo.locationPath = moveResPath ?? ""
-                   bgMovieInfo.timelineIn = 0
-                   bgMovieInfo.timelineOut = CMTimeGetSeconds(movieAsset.duration)
-                   bgMovieInfo.model_in = bgMovieInfo.timelineIn
-                   bgMovieInfo.out = bgMovieInfo.timelineOut
-                   bgMovieInfo.canvasFillType = stickerContentMode.aspectFitStr.rawValue
-                   //2,用户头像素材
-                   BFLog(message: "头像的沙盒地址:\(avatarFilePath)")
-                   let avatarSticker:PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel.init()
-                   avatarSticker.locationPath = avatarFilePath.replacingOccurrences(of: documensDirectory, with: "")
-                   avatarSticker.timelineIn = bgMovieInfo.timelineIn
-                   avatarSticker.timelineOut = bgMovieInfo.timelineOut
-                   avatarSticker.canvasFillType = stickerContentMode.aspectFitStr.rawValue
- 
-                //头像绘制大小\位置
-                var avatarSize:Float = 0.0
-                var avatarTop:Float = 0.0
-                if((self.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) > (self.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)){
-                    //竖屏
-                    avatarSize = Float(self.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * 360.0 / 1080.0
-                    avatarTop = 430
-                }else{
-                    //横屏屏
-                    avatarSize = Float(self.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 300.0 / 1080.0
-                    avatarTop = Float(self.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 130.0 / 1080.0
-                }
-         
-                let avatarPostion:PQEditMaterialPositionModel = PQEditMaterialPositionModel.init()
-                avatarPostion.width = Int(avatarSize)
-                avatarPostion.height = Int(avatarSize)
-                avatarPostion.x = ((self.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) - Int(avatarSize)) / 2
-                avatarPostion.y = Int(avatarTop)
-                   avatarSticker.materialPosition = avatarPostion
-                   
-                   //3,用户名素材
-                   let userNameSticker:PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel.init()
-                   userNameSticker.timelineIn = bgMovieInfo.timelineIn
-                   userNameSticker.timelineOut = bgMovieInfo.timelineOut
-                   userNameSticker.type = StickerType.SUBTITLE.rawValue
-                   
-                
-                   //用户名绘制用到的参数
-                var userNameTop:Float = 0.0
-                var userNameFontSize:Float = 0.0
-                if((self.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) > (self.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)){
-                    //竖屏
-                    userNameTop = 870
-                    userNameFontSize = Float(self.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * 100.0 / 1080.0
-                }else{
-                    //横屏
-                    userNameTop = Float(self.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 480 / 1080.0
-                    userNameFontSize = Float(self.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 70.0 / 1080.0
-                }
-             
-                   let subtitleInfo:PQEditSubtitleInfoModel = PQEditSubtitleInfoModel.init()
-                   subtitleInfo.fontSize = Int(userNameFontSize)
-                   subtitleInfo.text = BFLoginUserInfo.shared.nickName
-                   userNameSticker.subtitleInfo = subtitleInfo
-                   
-                   let userNamePostion:PQEditMaterialPositionModel = PQEditMaterialPositionModel.init()
-                   userNamePostion.width = Int(userNameFontSize ) * 10
-                   userNamePostion.height = Int(userNameFontSize ) * 3
-                   userNamePostion.x = ((self.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) -  userNamePostion.width) / 2
-                   userNamePostion.y = Int(userNameTop)
-                   userNameSticker.materialPosition = userNamePostion
-
-                   //4,音频
-                   let soundResPath = currentBundlePath()!.path(forResource: "endMovieSound", ofType: "mp3")
-                   let soundAsset = AVURLAsset(url:  URL(fileURLWithPath: soundResPath ?? ""), options: nil)
-                   self.endMovieExporter = PQCompositionExporter(asset: soundAsset, videoComposition: nil, audioMix: nil, filters: nil, stickers: [bgMovieInfo,avatarSticker,userNameSticker], animationTool: nil, exportURL: outPutMP4URL)
-                   self.endMovieExporter.isEndMovie = true
-                   if self.endMovieExporter.prepare(videoSize: CGSize(width: self.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: self.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0), videoAverageBitRate: orgeBitRate) {
-              
-                       self.endMovieExporter.start(playeTimeRange: CMTimeRange.init(start: CMTime.zero, duration: CMTimeMakeWithSeconds(Float64(bgMovieInfo.out), preferredTimescale: BASE_FILTER_TIMESCALE)))
-                       BFLog(message: "开始导出")
-                   }
-                   self.endMovieExporter.progressClosure = { [weak self] _, _, progress in
-                       BFLog(message: "片尾合成进度 \(progress) ")
-                     
-                   }
-                self.endMovieExporter.completion = { [weak self] url in
-                       BFLog(message: "片尾的视频导出完成: \(url) 生成视频时长为:\(CMTimeGetSeconds(AVAsset(url: url).duration))")
-
-                       // 导出完成后取消导出
-                       if self?.endMovieExporter != nil {
-                           self?.endMovieExporter.cancel()
-                       }
-                       self?.endMovieLocalURL = url
-                       //拼接水印正片和片尾
-                       if(self?.watermarkMovieLocalURL != nil && self?.endMovieLocalURL != nil){
-                           let videoMerge:NXVideoMerge = NXVideoMerge.init()
-                           videoMerge.mergeAndExportVideos(withFileURLs: [self!.watermarkMovieLocalURL!,self!.endMovieLocalURL!], renderSize:CGSize(width: self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)) { isSuccess, outFileURL in
-                               if(isSuccess){
-                                   BFLog(message: "合并视频成功 outFilePath is \(outFileURL ?? "")")
-                                   self?.saveMovieLocalURL = outFileURL as? URL
-                                   //保存到相册 fp2-1-1 - 请求权限
-                                   self?.authorizationStatus()
-                               }
-                           }
-                       }
-                 
-    
-                   }
             case let .failure(error):
+                image = UIImage.moduleImage(named: "user_avatar_normal", moduleName: "BFFramework", isAssets:false)
                 BFLog(message: "下载头像图片失败:\(error.localizedDescription)")
             }
+            if(image == nil){
+                BFLog(message: "image date is error!!")
+                return
+            }
+            UIImage.saveImage(currentImage: image!, outFilePath: avatarFilePath)
+            
+            //1,背景视频素材
+            let bgMovieInfo:PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel.init()
+            bgMovieInfo.type = StickerType.VIDEO.rawValue
+            bgMovieInfo.locationPath = moveResPath ?? ""
+            bgMovieInfo.timelineIn = 0
+            bgMovieInfo.timelineOut = CMTimeGetSeconds(movieAsset.duration)
+            bgMovieInfo.model_in = bgMovieInfo.timelineIn
+            bgMovieInfo.out = bgMovieInfo.timelineOut
+            bgMovieInfo.canvasFillType = stickerContentMode.aspectFitStr.rawValue
+            //2,用户头像素材
+            BFLog(message: "头像的沙盒地址:\(avatarFilePath)")
+            let avatarSticker:PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel.init()
+            avatarSticker.locationPath = avatarFilePath.replacingOccurrences(of: documensDirectory, with: "")
+            avatarSticker.timelineIn = bgMovieInfo.timelineIn
+            avatarSticker.timelineOut = bgMovieInfo.timelineOut
+            avatarSticker.canvasFillType = stickerContentMode.aspectFitStr.rawValue
+
+         //头像绘制大小\位置
+         var avatarSize:Float = 0.0
+         var avatarTop:Float = 0.0
+            if((self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) > (self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)){
+             //竖屏
+                avatarSize = Float(self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * 360.0 / 1080.0
+             avatarTop = 430
+         }else{
+             //横屏屏
+            avatarSize = Float(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 300.0 / 1080.0
+            avatarTop = Float(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 130.0 / 1080.0
+         }
+
+         let avatarPostion:PQEditMaterialPositionModel = PQEditMaterialPositionModel.init()
+         avatarPostion.width = Int(avatarSize)
+         avatarPostion.height = Int(avatarSize)
+            avatarPostion.x = ((self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) - Int(avatarSize)) / 2
+         avatarPostion.y = Int(avatarTop)
+            avatarSticker.materialPosition = avatarPostion
+            
+            //3,用户名素材
+            let userNameSticker:PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel.init()
+            userNameSticker.timelineIn = bgMovieInfo.timelineIn
+            userNameSticker.timelineOut = bgMovieInfo.timelineOut
+            userNameSticker.type = StickerType.SUBTITLE.rawValue
+            
+         
+            //用户名绘制用到的参数
+         var userNameTop:Float = 0.0
+         var userNameFontSize:Float = 0.0
+            if((self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) > (self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0)){
+             //竖屏
+             userNameTop = 870
+                userNameFontSize = Float(self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) * 100.0 / 1080.0
+         }else{
+             //横屏
+            userNameTop = Float(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 480 / 1080.0
+            userNameFontSize = Float(self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0) * 70.0 / 1080.0
+         }
+      
+         let subtitleInfo:PQEditSubtitleInfoModel = PQEditSubtitleInfoModel.init()
+         subtitleInfo.fontSize = Int(userNameFontSize)
+         subtitleInfo.text = BFLoginUserInfo.shared.nickName
+         userNameSticker.subtitleInfo = subtitleInfo
+
+         let userNamePostion:PQEditMaterialPositionModel = PQEditMaterialPositionModel.init()
+         userNamePostion.width = Int(userNameFontSize ) * 10
+         userNamePostion.height = Int(userNameFontSize ) * 3
+            userNamePostion.x = ((self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0) -  userNamePostion.width) / 2
+         userNamePostion.y = Int(userNameTop)
+         userNameSticker.materialPosition = userNamePostion
+
+         //4,音频
+         let soundResPath = currentBundlePath()!.path(forResource: "endMovieSound", ofType: "mp3")
+         let soundAsset = AVURLAsset(url:  URL(fileURLWithPath: soundResPath ?? ""), options: nil)
+            self?.endMovieExporter = PQCompositionExporter(asset: soundAsset, videoComposition: nil, audioMix: nil, filters: nil, stickers: [bgMovieInfo,avatarSticker,userNameSticker], animationTool: nil, exportURL: outPutMP4URL)
+            self?.endMovieExporter.isEndMovie = true
+            if ((self?.endMovieExporter.prepare(videoSize: CGSize(width: self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0), videoAverageBitRate: orgeBitRate)) != nil) {
+
+            self?.endMovieExporter.start(playeTimeRange: CMTimeRange.init(start: CMTime.zero, duration: CMTimeMakeWithSeconds(Float64(bgMovieInfo.out), preferredTimescale: BASE_FILTER_TIMESCALE)))
+            BFLog(message: "开始导出")
+         }
+            self?.endMovieExporter.progressClosure = { _, _, progress in
+            BFLog(message: "片尾合成进度 \(progress) ")
+          
+         }
+         
+            self?.endMovieExporter.completion = { [weak self] url in
+             BFLog(message: "片尾的视频导出完成: \(String(describing: url)) 生成视频时长为:\(CMTimeGetSeconds(AVAsset(url: url ?? URL(string: "https://media.w3.org/2010/05/sintel/trailer.mp4")!).duration))")
+
+                // 导出完成后取消导出
+                if self?.endMovieExporter != nil {
+                    self?.endMovieExporter.cancel()
+                }
+                self?.endMovieLocalURL = url
+                //拼接水印正片和片尾
+                if(self?.watermarkMovieLocalURL != nil && self?.endMovieLocalURL != nil){
+                    let videoMerge:NXVideoMerge = NXVideoMerge.init()
+                    videoMerge.mergeAndExportVideos(withFileURLs: [self!.watermarkMovieLocalURL!,self!.endMovieLocalURL!], renderSize:CGSize(width: self?.editProjectModel?.sData?.videoMetaData?.videoWidth ?? 0, height: self?.editProjectModel?.sData?.videoMetaData?.videoHeight ?? 0)) { isSuccess, outFileURL in
+                        if(isSuccess){
+                            BFLog(message: "合并视频成功 outFilePath is \(outFileURL ?? "")")
+                            self?.saveMovieLocalURL = outFileURL as? URL
+                            //保存到相册 fp2-1-1 - 请求权限
+                            self?.authorizationStatus()
+                        }
+                    }
+                }
+            }
         }
 
     }

+ 6 - 6
BFFramework/Classes/Stuckpoint/View/PQCustomSpeedSettingView.swift

@@ -209,7 +209,7 @@ class PQCustomSpeedSettingView: UIView {
             make.height.equalTo(50)
             make.width.equalTo(254)
             make.right.equalToSuperview().offset(-30)
-            make.top.equalTo(cancleBtn.snp_top)
+            make.top.equalTo(cancleBtn.snp.top)
         }
 
         customSpeedFastView.snp.makeConstraints { make in
@@ -220,25 +220,25 @@ class PQCustomSpeedSettingView: UIView {
 
         customSpeedSlowView.snp.makeConstraints { make in
             make.height.width.equalTo(30)
-            make.left.equalTo(customSpeedFastView.snp_left)
-            make.top.equalTo(customSpeedFastView.snp_bottom).offset(31)
+            make.left.equalTo(customSpeedFastView.snp.left)
+            make.top.equalTo(customSpeedFastView.snp.bottom).offset(31)
         }
 
         fastSlider.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(65)
             make.right.equalToSuperview().offset(-36)
-            make.centerY.equalTo(customSpeedFastView.snp_centerY)
+            make.centerY.equalTo(customSpeedFastView.snp.centerY)
         }
         slowSlider.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(65)
             make.right.equalToSuperview().offset(-36)
-            make.centerY.equalTo(customSpeedSlowView.snp_centerY)
+            make.centerY.equalTo(customSpeedSlowView.snp.centerY)
         }
 
         jumpSpeedSlider.snp.makeConstraints { make in
             make.left.equalToSuperview().offset(30)
             make.right.equalToSuperview().offset(-36)
-            make.top.equalTo(titleLab.snp_bottom).offset(104)
+            make.top.equalTo(titleLab.snp.bottom).offset(104)
         }
     }
 

+ 2 - 2
BFFramework/Classes/Stuckpoint/View/PQEditPublicCoverImageView.swift

@@ -126,10 +126,10 @@ class PQEditPublicCoverImageView: UIView {
         }
 
         compliteBtn.snp.makeConstraints { make in
-            make.left.equalTo(selectPhotoBtn.snp_right).offset(14.0 * cAdaptatWidth)
+            make.left.equalTo(selectPhotoBtn.snp.right).offset(14.0 * cAdaptatWidth)
             make.width.equalTo(164 * cAdaptatWidth)
             make.height.equalTo(54 * cAdaptatWidth)
-            make.top.equalTo(selectPhotoBtn.snp_top)
+            make.top.equalTo(selectPhotoBtn.snp.top)
         }
     }
 

+ 2 - 2
BFFramework/Classes/Stuckpoint/View/PQEditPublicTitleView.swift

@@ -155,7 +155,7 @@ class PQEditPublicTitleView: UIView {
         }
 
         titleCollectionView.snp.makeConstraints { make in
-            make.top.equalTo(inputBgView.snp_bottom).offset(10)
+            make.top.equalTo(inputBgView.snp.bottom).offset(10)
             make.width.right.equalToSuperview()
             make.bottom.equalTo(0 - cAKSafeAreaHeight)
 //            make.width.equalTo(cScreenWidth)
@@ -300,7 +300,7 @@ class PQEditPublicTitleViewContentCell: UICollectionViewCell {
         iconView.snp.remakeConstraints { make in
             make.width.height.equalTo(24)
             make.right.equalToSuperview().offset(-16)
-            make.top.equalTo(contentView.snp_top).offset(14)
+            make.top.equalTo(contentView.snp.top).offset(14)
         }
     }
 }

+ 6 - 6
BFFramework/Classes/Stuckpoint/View/PQSelecteMusicView.swift

@@ -183,9 +183,9 @@ class PQSelecteMusicView: UIView {
  
     }
     func autolayout() {
-        categoryCollection.snp_removeConstraints()
-        selectMusicCollection.snp_removeConstraints()
-        musicSearchBtn.snp_removeConstraints()
+        categoryCollection.snp.removeConstraints()
+        selectMusicCollection.snp.removeConstraints()
+        musicSearchBtn.snp.removeConstraints()
 
         categoryCollection.snp.makeConstraints { make in
             make.height.equalTo(20)
@@ -700,14 +700,14 @@ class PQSelectMusicCell: UICollectionViewCell {
         musicNameLab.snp.remakeConstraints { make in
             make.width.equalTo(60)
             make.height.equalTo(30)
-            make.top.equalTo(audioImageView.snp_bottom).offset(6)
+            make.top.equalTo(audioImageView.snp.bottom).offset(6)
         }
  
         confirmBtn.snp.remakeConstraints { make in
             make.width.equalTo(54)
             make.height.equalTo(29)
-            make.top.equalTo(musicNameLab.snp_bottom).offset(6)
-            make.centerX.equalTo(audioImageView.snp_centerX)
+            make.top.equalTo(musicNameLab.snp.bottom).offset(6)
+            make.centerX.equalTo(audioImageView.snp.centerX)
           
         }
         audioImageView.addCorner(corner: 60 / 2)

+ 2 - 2
BFFramework/Classes/Stuckpoint/View/PQStuckPointCuttingView.swift

@@ -197,8 +197,8 @@ class PQStuckPointCuttingView: UIView {
         tatalTimeLabel.snp.remakeConstraints { make in
             make.width.equalTo(40)
             make.height.equalTo(15)
-            make.top.equalTo(videoCropView.snp_top).offset(6)
-            make.right.equalTo(videoCropView.snp_right).offset(-6)
+            make.top.equalTo(videoCropView.snp.top).offset(6)
+            make.right.equalTo(videoCropView.snp.right).offset(-6)
         }
         
       

+ 15 - 8
BFFramework/Classes/Stuckpoint/View/PQStuckPointLoadingView.swift

@@ -43,14 +43,7 @@ class PQStuckPointLoadingView: UIView {
         addLayout()
         
     }
-    func show() {
-        let data = try? Data(contentsOf: URL(fileURLWithPath: currentBundlePath()!.path(forResource: "stuckPoint_edit_loading", ofType: ".gif")!))
-        if data != nil {
-            PQPHAssetVideoParaseUtil.parasGIFImage(data: data!, isRenderingColor: UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)) { _, images, duration in
-                self.loadingView.displayGIF(data: nil, images: images, repeatCount: .max, duration: duration ?? 2)
-            }
-        }
-    }
+    
     required init?(coder _: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
@@ -75,6 +68,20 @@ class PQStuckPointLoadingView: UIView {
         }
         
     }
+    
+    
+    func show() {
+        if self.superview != nil {
+            return
+        }
+        UIApplication.shared.keyWindow?.addSubview(self)
+        let data = try? Data(contentsOf: URL(fileURLWithPath: currentBundlePath()!.path(forResource: "stuckPoint_edit_loading", ofType: ".gif")!))
+        if data != nil {
+            PQPHAssetVideoParaseUtil.parasGIFImage(data: data!, isRenderingColor: UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)) { _, images, duration in
+                self.loadingView.displayGIF(data: nil, images: images, repeatCount: .max, duration: duration ?? 2)
+            }
+        }
+    }
 
     /// 移除视图
     /// - Returns: <#description#>

+ 1 - 1
BFFramework/Classes/Stuckpoint/View/PQStuckPointMaterialHeadView.swift

@@ -54,7 +54,7 @@ class PQStuckPointMaterialHeadView: UIView {
             make.centerY.equalToSuperview()
         }
         titleLab.snp.makeConstraints { make in
-            make.left.equalTo(iconImageView.snp_right).offset(cDefaultMargin)
+            make.left.equalTo(iconImageView.snp.right).offset(cDefaultMargin)
             make.right.equalToSuperview().offset(-cDefaultMargin)
             make.top.equalTo(iconImageView)
         }

+ 2 - 2
BFFramework/Classes/Stuckpoint/View/PQStuckPointMusicContentCell.swift

@@ -198,7 +198,7 @@ class PQStuckPointMusicContentCell: UICollectionViewCell {
             }
 
             musicNameLab.snp.remakeConstraints { make in
-                make.left.equalTo(audioImageView.snp_right).offset(margin)
+                make.left.equalTo(audioImageView.snp.right).offset(margin)
                 make.width.equalTo(nameW)
                 make.height.equalTo(cDefaultMargin * 3)
                 make.centerY.equalToSuperview()
@@ -230,7 +230,7 @@ class PQStuckPointMusicContentCell: UICollectionViewCell {
                 make.centerY.equalToSuperview()
             }
             titleLab.snp.remakeConstraints { make in
-                make.left.equalTo(audioImageView.snp_right)
+                make.left.equalTo(audioImageView.snp.right)
                 make.right.equalToSuperview().offset(-margin)
                 make.centerY.equalToSuperview()
             }

+ 3 - 3
BFFramework/Classes/Stuckpoint/View/PQStuckPointSearchEmptyCell.swift

@@ -88,7 +88,7 @@ class PQStuckPointSearchEmptyCell: UICollectionViewCell {
             make.top.equalToSuperview().offset(imageH)
         }
         remindLab.snp.makeConstraints { make in
-            make.top.equalTo(emptyImageView.snp_bottom).offset(margin)
+            make.top.equalTo(emptyImageView.snp.bottom).offset(margin)
             make.centerX.equalToSuperview()
         }
         hotRemindLab.snp.makeConstraints { make in
@@ -100,13 +100,13 @@ class PQStuckPointSearchEmptyCell: UICollectionViewCell {
             make.left.equalToSuperview().offset(leftMargin)
             make.height.equalTo(1)
             make.centerY.equalTo(hotRemindLab)
-            make.right.equalTo(hotRemindLab.snp_left).offset(-leftMargin)
+            make.right.equalTo(hotRemindLab.snp.left).offset(-leftMargin)
         }
         rightLineView.snp.makeConstraints { make in
             make.right.equalToSuperview().offset(-leftMargin)
             make.height.equalTo(1)
             make.centerY.equalTo(hotRemindLab)
-            make.left.equalTo(hotRemindLab.snp_right).offset(leftMargin)
+            make.left.equalTo(hotRemindLab.snp.right).offset(leftMargin)
         }
     }
 

+ 1 - 1
BFFramework/Classes/Stuckpoint/ViewModel/PQStuckPointViewModel.swift

@@ -64,7 +64,7 @@ public class PQStuckPointViewModel: NSObject {
             currtentX = currtentX + itemSpace + width
             attributesArray.append(attributes)
         }
-        return (attributesArray, currtentY + lineSpace + itemHeight)
+        return (attributesArray, attributesArray.count > 0 ? currtentY + lineSpace + itemHeight : 0)
     }
 
     /// 卡点音乐某个分类下列表