Forráskód Böngészése

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

jsonwang 3 éve
szülő
commit
6889d08951

+ 2 - 2
BFRecordScreenKit/Classes/BFRSComm.swift

@@ -27,9 +27,9 @@ public var ThemeStyleColor = UIColor.hexColor(hexadecimal: "#389AFF")
 
 
 // 获取视频资源的旋转方向
-public func degressFromVideoFile(url:URL) -> UInt {
+public func degressFromVideoFile(asset: AVAsset) -> UInt {
     var degress :UInt = 0
-    let tracks = AVAsset(url: url).tracks(withMediaType: .video)
+    let tracks = asset.tracks(withMediaType: .video)
     if let track = tracks.first {
         let t = track.preferredTransform
         

+ 5 - 1
BFRecordScreenKit/Classes/BFRecordExport.swift

@@ -319,7 +319,11 @@ public class BFRecordExport {
         guard let totalDuration = data?.reduce(0.0, { partialResult, itemModell in
             var modelDuraion = 0.0
             if itemModell.mediaType == .IMAGE {
-                modelDuraion = itemModell.materialDuraion
+                if itemModell.voiceStickers.count == 0 && synthesisAll {
+                    modelDuraion += 2
+                }else {
+                    modelDuraion = itemModell.materialDuraion                    
+                }
             }else if itemModell.mediaType == .VIDEO{
                 modelDuraion = itemModell.dealedDurationRanges.reduce(0.0) { partialResult, srange in
 //                    partialResult + (!synthesisAll && srange.isRecord) ?

+ 17 - 13
BFRecordScreenKit/Classes/BFRecordItemModel.swift

@@ -20,9 +20,9 @@ public class BFRecordItemModel: NSObject {
 //    var baseMaterial : AVURLAsset?
     var localPath: String?
     var materialDuraion: Double = 0.0
-    var fetchCoverImg: ((UIImage) -> Void)?
-    var fetchAVUrlAsset: ((AVURLAsset) -> Void)?
-    var fetchPlayItem: ((AVPlayerItem) -> Void)?
+    var fetchCoverImgCallBack: ((UIImage) -> Void)?
+    var fetchAVUrlAssetCallBack: ((AVURLAsset) -> Void)?
+    var fetchPlayItemCallBack: ((BFRecordItemModel?) -> Void)?
     var dealedDurationRanges = [SplitRecordRange]() // 录音切割的时间区间,合成导出时计算
     public var voiceStickers = [PQVoiceModel]() //
     public var videoStickers = [PQEditVisionTrackMaterialsModel]() // 合成导出时计算
@@ -45,17 +45,18 @@ public class BFRecordItemModel: NSObject {
     func initOriginData(phasset: PHAsset) {
         width = phasset.pixelWidth
         height = phasset.pixelHeight
+        
+        fetchCoverImage(phasset)
+        fetchAVUrlAsset(phasset)
+        
         if phasset.mediaType == .image {
             mediaType = .IMAGE
             localPath = "image"
         } else if phasset.mediaType == .video {
             mediaType = .VIDEO
             fetchPlayItem(phasset)
-
         }
 
-        fetchCoverImage(phasset)
-        fetchAVUrlAsset(phasset)
     }
 
     func fetchCoverImage(_ phasset: PHAsset) {
@@ -70,7 +71,7 @@ public class BFRecordItemModel: NSObject {
             // 设置首帧/封面
             if image != nil {
                 self?.coverImg = image
-                self?.fetchCoverImg?(image!)
+                self?.fetchCoverImgCallBack?(image!)
             }
         }
     }
@@ -87,7 +88,7 @@ public class BFRecordItemModel: NSObject {
                 return
             }
             self?.playItem = item
-            self?.fetchPlayItem?(item)
+            self?.fetchPlayItemCallBack?(self)
         })
     }
 
@@ -97,11 +98,14 @@ public class BFRecordItemModel: NSObject {
         options.deliveryMode = .automatic
 
         PHCachingImageManager().requestAVAsset(forVideo: phasset, options: options, resultHandler: { [weak self] (asset: AVAsset?, _: AVAudioMix?, _) in
-            if let videoAsset = asset as? AVURLAsset {
-                self?.materialDuraion = videoAsset.duration.seconds
-                self?.localPath = (videoAsset.url.absoluteString.removingPercentEncoding)?.replacingOccurrences(of: "file://", with: "")
-                self?.videoAsset = videoAsset
-                self?.fetchAVUrlAsset?(videoAsset)
+            guard let sself = self else {
+                return
+            }
+            if let videoAsset = (asset as? AVURLAsset) {
+                sself.materialDuraion = videoAsset.duration.seconds
+                sself.localPath = (videoAsset.url.absoluteString.removingPercentEncoding)?.replacingOccurrences(of: "file://", with: "")
+                sself.videoAsset = videoAsset
+                sself.fetchAVUrlAssetCallBack?(videoAsset)
             }
         })
     }

+ 4 - 4
BFRecordScreenKit/Classes/BFVideoThumbProgressStrategy.swift

@@ -19,12 +19,12 @@ class BFVideoThumbProgressStrategy: BFVideoThumbProgressStrategyProtocol {
 
         if assetDuration > 0, assetDuration <= 10 {
             count = 5
-        } else if assetDuration >= 10, assetDuration < 60 {
+        } else if assetDuration >= 10, assetDuration <= 60 {
             count = 10
-        } else if assetDuration >= 60, assetDuration < 300 {
+        } else if assetDuration >= 60, assetDuration <= 300 {
             count = 20
-        } else if assetDuration >= 300 {
-            count = 30
+        } else if assetDuration > 300 {
+            count = 50
         }
 
         return count

+ 62 - 47
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenController.swift

@@ -46,7 +46,10 @@ public class BFRecordScreenController: BFBaseViewController {
         didSet {
             withDrawBtn.isHidden = isRecording
             changeVoiceBtn.isHidden = isRecording
-            recordBtn.setTitle(isRecording ? "松手 完成" : "按住 说话", for: .normal)
+            subtitleBtn.isHidden = isRecording
+            soundSettingBtn.isHidden = isRecording
+            
+            recordBtn.setTitle(isRecording ? "松手 暂停" : "按住 录音", for: .normal)
             recordBtn.backgroundColor = UIColor.hexColor(hexadecimal: "#389AFF", alpha: isRecording ? 0.6 : 1)
             playBtn.isSelected = isRecording
 //            if !isRecording {
@@ -85,8 +88,17 @@ public class BFRecordScreenController: BFBaseViewController {
     // MARK: 行为参数
     
     var movieIsProcessing = false
-    var events = [WithDrawModel]() // 行为记录,方便撤销
-    var isDragingProgressSlder: Bool = false // 是否在拖动进度条
+    
+    // 行为记录,方便撤销
+    var events = [WithDrawModel]() {
+        didSet {
+            withDrawBtn.isEnabled = (events.count != 0)
+        }
+    }
+    
+    // 是否在拖动进度条
+    var isDragingProgressSlder: Bool = false
+    
     var isStopAtRecordRange = -1
     
     // 保存识别出来的字幕信息,用于回放,和合成使用
@@ -152,7 +164,7 @@ public class BFRecordScreenController: BFBaseViewController {
     lazy var recordBtn: UIButton = {
         let btn = UIButton(type: .custom)
         btn.backgroundColor = ThemeStyleColor
-        btn.setTitle("按住 说话", for: .normal)
+        btn.setTitle("按住 录音", for: .normal)
         btn.addCorner(corner: 6)
         btn.adjustsImageWhenHighlighted = false
         btn.addTarget(self, action: #selector(startRecord), for: .touchDown)
@@ -178,7 +190,8 @@ public class BFRecordScreenController: BFBaseViewController {
         btn.setImage(imageInRecordScreenKit(by: "withdraw_h"), for: .highlighted)
         btn.setTitle("回退", for: .normal)
         btn.setTitleColor(.white, for: .normal)
-        btn.setTitleColor(.gray, for: .highlighted)
+        btn.setTitleColor(.lightGray, for: .disabled)
+        btn.isEnabled = false
         btn.titleLabel?.font = UIFont.systemFont(ofSize: 12)
         btn.contentVerticalAlignment = UIControl.ContentVerticalAlignment.center
         btn.addTarget(self, action: #selector(withdrawAction), for: .touchUpInside)
@@ -472,6 +485,8 @@ public class BFRecordScreenController: BFBaseViewController {
                         }
                         sself.itemModels[sself.currItemModelIndex].materialDuraion = Double(String(format: "%.3f", duration)) ?? 0
                         self?.isEndPlay = true
+                        // 录制结束显示播放按钮
+                        (sself.collectionView.cellForItem(at: IndexPath(item: sself.currItemModelIndex, section: 0)) as? BFImageCoverViewCell)?.playBtn.isSelected = sself.itemModels[sself.currItemModelIndex].voiceStickers.count <= 0
                     }
                     DispatchQueue.main.async { [weak self] in
                         // 录音完,重绘撤销按钮,更新录音按钮,
@@ -881,7 +896,6 @@ public class BFRecordScreenController: BFBaseViewController {
         if !avatarView.isHidden {
             avatarView.beginRecord()
         }
-        
         if itemModels[currItemModelIndex].mediaType == .VIDEO {
             if !movieIsProcessing {
                 movie?.startProcessing()
@@ -889,10 +903,9 @@ public class BFRecordScreenController: BFBaseViewController {
             }
             assetPlayer?.volume = 0
             assetPlayer?.play()
-            
-            // 暂停状态
-            (collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFImageCoverViewCell)?.playBtn.isSelected = true
         }
+        // 录制中不显示播放按钮
+        (collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFImageCoverViewCell)?.playBtn.isSelected = true
     }
     
     @objc func endRecord() {
@@ -1290,7 +1303,7 @@ public class BFRecordScreenController: BFBaseViewController {
         assetPlayer?.seek(to: currentAssetProgress, toleranceBefore: CMTime(value: 1, timescale: 1_000_000), toleranceAfter: CMTime(value: 1, timescale: 1_000_000), completionHandler: { _ in
         })
         // 暂停状态
-        (collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFImageCoverViewCell)?.playBtn.isSelected = false
+        (collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFImageCoverViewCell)?.playBtn.isSelected = (itemModels[currItemModelIndex].mediaType == .IMAGE && itemModels[currItemModelIndex].voiceStickers.count <= 0)
     }
     
     func fetchVideo() {
@@ -1303,9 +1316,9 @@ public class BFRecordScreenController: BFBaseViewController {
                 itemModels.append(itemModel)
                 if index == 0 {
                     if asset.mediaType == .video {
-                        itemModel.fetchAVUrlAsset = { [weak self, weak itemModel] _ in
+                        itemModel.fetchAVUrlAssetCallBack = { [weak self, weak itemModel] _ in
                             //                            self?.export(avsss:uralss)
-                            DispatchQueue.main.async { [weak self] in
+                            DispatchQueue.main.async { [weak self, weak itemModel] in
                                 self?.progressThumV.recordItem = itemModel
                                 self?.progressThumV.isHidden = false
                             }
@@ -1377,16 +1390,18 @@ public class BFRecordScreenController: BFBaseViewController {
         
         NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: assetPlayer?.currentItem, queue: .main) { [weak self] notify in
             BFLog(1, message: "AVPlayerItemDidPlayToEndTime = \(notify)")
-            if (self?.currItemModelIndex ?? 0) < ((self?.itemModels.count ?? 0) - 1) {
-                self?.collectionView.setContentOffset(CGPoint(x: CGFloat((self?.currItemModelIndex ?? 0) + 1) * (self?.collectionView.frame.width ?? 0), y: 0), animated: true)
-            } else {
-                self?.isNormalPlaying = false
-                //            self?.assetPlayer?.seek(to: CMTime.zero)
-                //            self?.progressThumV.progress = 0
-                self?.isEndPlay = true
-                //            self?.currentPlayRecordIndex = -1
-                self?.recordBtn.isHidden = true
-            }
+//            if (self?.currItemModelIndex ?? 0) < ((self?.itemModels.count ?? 0) - 1) {
+//                self?.collectionView.setContentOffset(CGPoint(x: CGFloat((self?.currItemModelIndex ?? 0) + 1) * (self?.collectionView.frame.width ?? 0), y: 0), animated: true)
+//            } else {
+//            }
+            self?.isNormalPlaying = false
+            //            self?.assetPlayer?.seek(to: CMTime.zero)
+            //            self?.progressThumV.progress = 0
+            self?.isEndPlay = true
+            //            self?.currentPlayRecordIndex = -1
+            self?.recordBtn.isHidden = true
+            
+            
             if self?.isRecording ?? false {
                 self?.endRecord()
                 cShowHUB(superView: nil, msg: "此视频已录制到头了哦!")
@@ -1488,13 +1503,15 @@ public class BFRecordScreenController: BFBaseViewController {
     }
     
     // 修正视频旋转方向,因为自己录制的竖屏视频会预览为横屏
-    func reloadMaterial(recordItem: BFRecordItemModel) {
-        if let path = recordItem.localPath, let cell: BFImageCoverViewCell = collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFImageCoverViewCell {
+    func reloadMaterial() {
+        let recordItem = itemModels[currItemModelIndex]
+        if  let vasset = recordItem.videoAsset, let cell: BFImageCoverViewCell = collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFImageCoverViewCell {
             setVideoPlay(item: recordItem.playItem, imageView: cell.playView)
             setAudioPlay(item: recordItem.playItem)
             playBtn = cell.playBtn
+//            recordItem.videoAsset
             
-            let degress = degressFromVideoFile(url: URL(fileURLWithPath: path))
+            let degress = degressFromVideoFile(asset: vasset)
             switch degress {
             case 90:
                 cell.playView.setInputRotation(GPUImageRotationMode(rawValue: 2), at: 0)
@@ -1544,19 +1561,22 @@ extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDa
         } else {
             cell = BFImageCoverViewCell.gpuImageViewCell(collectionView: collectionView, indexPath: indexPath)
         }
-        recordItem.fetchCoverImg = { [weak self, weak cell] _ in
+        recordItem.fetchCoverImgCallBack = { [weak self, weak cell, weak recordItem] _ in
+            guard let sself = self, let item = recordItem else {
+                return
+            }
             cell?.addData()
-            if recordItem.mediaType == .IMAGE {
-                self?.progressThumV.recordItem = recordItem
-                self?.progressThumV.isHidden = false
+            if item.mediaType == .IMAGE {
+                sself.progressThumV.recordItem = item
+                sself.progressThumV.isHidden = false
             }
         }
-        recordItem.fetchPlayItem = { [weak self, weak recordItem] _ in
-            guard let item = recordItem else {
+        recordItem.fetchPlayItemCallBack = { [weak self] item in
+            guard let sself = self else {
                 return
             }
-            if indexPath.item == self?.currItemModelIndex {
-                self?.reloadMaterial(recordItem: item)
+            if indexPath.item == sself.currItemModelIndex {
+                sself.reloadMaterial()
             }
         }
         cell.btnClickHandle = { [weak self] sender, _ in
@@ -1573,11 +1593,14 @@ extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDa
         if page != currItemModelIndex {
             // 暂停
             pause()
-            // 暂停状态
-            let lastCell: BFImageCoverViewCell? = collectionView.cellForItem(at: IndexPath(item: currItemModelIndex, section: 0)) as? BFImageCoverViewCell
-            lastCell?.playBtn.isSelected = false
+            events.append(WithDrawModel(type: 0, timestamp: currentAssetProgress.seconds))
+            // 重设撤销栈
+            itemModels[currItemModelIndex].events = events
+            currItemModelIndex = page
             
-            let recordItem = itemModels[page]
+            events = itemModels[currItemModelIndex].events
+            
+            let recordItem = itemModels[currItemModelIndex]
             // 重绘录音区域
             progressThumV.recordItem = recordItem
             DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {[weak self] in
@@ -1586,26 +1609,18 @@ extension BFRecordScreenController: UICollectionViewDelegate, UICollectionViewDa
             // 更新缩略图
             progressThumV.isHidden = false
             progreddL.text = "00:00"
-            
-            
-            events.append(WithDrawModel(type: 0, timestamp: currentAssetProgress.seconds))
             // 重置指针
             currentAssetProgress = CMTime(seconds: 0, preferredTimescale: 1000)
             searchStopAtRecordRange()
             // 重置播放器
             assetPlayer?.seek(to: CMTime.zero)
             recordPlayer?.seek(to: CMTime.zero)
-            // 重设撤销栈
-            itemModels[currItemModelIndex].events = events
-            events = itemModels[page].events
-            currItemModelIndex = page
+            
             searchStopAtRecordRange()
             changeWithDrawBtnLayout(false)
-            
-            // 更新当前page
             pauseTime = 0
             if recordItem.mediaType == .VIDEO {
-                reloadMaterial(recordItem: recordItem)
+                reloadMaterial()
                 assetPlayer?.seek(to: .zero, toleranceBefore: CMTime(value: 1, timescale: 1_000_000), toleranceAfter: CMTime(value: 1, timescale: 1_000_000))
             }
             if changeItemHandle != nil {

+ 13 - 1
BFRecordScreenKit/Classes/RecordScreen/View/BFImageCoverViewCell.swift

@@ -6,9 +6,9 @@
 //  Copyright © 2021 BytesFlow. All rights reserved.
 //
 
+import BFUIKit
 import GPUImage
 import UIKit
-import BFUIKit
 
 open class BFImageCoverViewCell: UICollectionViewCell {
     var btnClickHandle: ((_ sender: UIButton, _ recordItem: BFRecordItemModel?) -> Void)?
@@ -76,6 +76,8 @@ open class BFImageCoverViewCell: UICollectionViewCell {
         picture.addTarget(filter)
         filter.addTarget(playView)
         picture.processImage()
+        // 暂停状态--如果是图片素材同时没有录音文件时不显示播放按钮
+        playBtn.isSelected = (recordItem?.mediaType == .IMAGE && (recordItem?.voiceStickers.count ?? 0) <= 0)
     }
 
     public func addLayout() {
@@ -90,4 +92,14 @@ open class BFImageCoverViewCell: UICollectionViewCell {
             btnClickHandle!(sender, recordItem)
         }
     }
+    
+    func clearImageFilter() {
+        filter.removeTarget(playView)
+        picture.removeTarget(filter)
+        GPUImageContext.sharedFramebufferCache().purgeAllUnassignedFramebuffers()
+    }
+    
+    deinit {
+        clearImageFilter()
+    }
 }