Bladeren bron

merge master

wenweiwei 3 jaren geleden
bovenliggende
commit
28de0a5856

+ 3 - 3
BFRecordScreenKit/Classes/BFRecordExport.swift

@@ -192,7 +192,7 @@ public class BFRecordExport {
             for sticker in videoStickers {
                 filters.append(PQMovieFilter(movieSticker: sticker))
             }
-            let outputSize:CGSize = CGSize(width: 1080.0, height: 1080 * CGFloat(Int(UIScreen.main.bounds.size.height / UIScreen.main.bounds.size.width)))
+            let outputSize:CGSize = CGSize(width: 1080, height: Int(1080 * CGFloat(UIScreen.main.bounds.size.height / UIScreen.main.bounds.size.width)))
             BFLog(message: "输出视频大小:\(outputSize)")
             
             //add by ak 有字幕数据 & 显示字幕开关打开 添加字幕filter
@@ -234,8 +234,8 @@ public class BFRecordExport {
                     let outSeconds = CMTimeGetSeconds(AVAsset(url: url).duration)
 
                     let exportEndTime = Date().timeIntervalSince1970
-                    BFLog(1, message: "视频导出完成: \(String(describing: url)) 生成视频时长为:\(outSeconds) 总用时:\(exportEndTime - tempBeginExport)")
-                    
+//                    BFLog(1, message: "视频导出完成: \(String(describing: url)) 生成视频时长为:\(outSeconds) 总用时:\(exportEndTime - tempBeginExport)")
+                    print("生成视频时长为:\(outSeconds) 总用时:\(exportEndTime - tempBeginExport)")
                     
                     cShowHUB(superView: nil, msg: (outSeconds == 0) ? "合成失败请重试。" : "合成成功")
                     self?.exportCompletion?(nil, url)

+ 29 - 4
BFRecordScreenKit/Classes/RecordScreen/Controller/BFRecordScreenController.swift

@@ -713,11 +713,12 @@ public class BFRecordScreenController: BFBaseViewController {
         if isStopAtRecordRange != -1, isStopAtRecordRange < itemModels[currItemModelIndex].voiceStickers.count {
             let model = itemModels[currItemModelIndex].voiceStickers[isStopAtRecordRange]
             itemModels[currItemModelIndex].voiceStickers.remove(at: isStopAtRecordRange)
+            events.append(WithDrawModel(type: 3, timestamp: currentAssetProgress.seconds, deletedVoices: [(model, isStopAtRecordRange)]))
             drawOrUpdateRecordProgessLable()
             searchStopAtRecordRange()
-            events.append(WithDrawModel(type: 3, timestamp: currentAssetProgress.seconds, deletedVoices: [(model, isStopAtRecordRange)]))
 
             deleteTitles(voiceModel: model)
+
         }
     }
 
@@ -801,6 +802,7 @@ public class BFRecordScreenController: BFBaseViewController {
         pause()
     }
 
+    // 撤销
     @objc func withdrawAction() {
         pause()
         if let action = events.last {
@@ -822,7 +824,17 @@ public class BFRecordScreenController: BFBaseViewController {
                             itemModels[currItemModelIndex].voiceStickers.insert(tuple.0, at: tuple.1)
                         }
                     }
-
+                    
+                    //恢复字幕
+                    let titleTuples = action.deletedTittles
+                    if titleTuples != nil, titleTuples!.count > 0 {
+                       
+                        titleTuples?.forEach { titleTuple in
+                            itemModels[currItemModelIndex].titleStickers.insert(titleTuple.0, at: titleTuple.1)
+                        }
+                    }
+                    
+                    
                     drawOrUpdateRecordProgessLable()
                     jumpTime = model.startTime
                 }
@@ -837,6 +849,17 @@ public class BFRecordScreenController: BFBaseViewController {
                         itemModels[currItemModelIndex].voiceStickers.insert(tuple.0, at: tuple.1)
                     }
                 }
+                
+                //恢复字幕
+                let titleTuples = action.deletedTittles
+                if titleTuples != nil, titleTuples!.count > 0 {
+                   
+                    titleTuples?.forEach { titleTuple in
+                        itemModels[currItemModelIndex].titleStickers.insert(titleTuple.0, at: titleTuple.1)
+                    }
+                }
+                
+                
                 drawOrUpdateRecordProgessLable()
             } else {}
             events.removeLast()
@@ -1119,9 +1142,10 @@ public class BFRecordScreenController: BFBaseViewController {
                 itemModel.index = index
                 itemModel.initOriginData(phasset: asset)
                 itemModels.append(itemModel)
-                if index == currItemModelIndex {
+                if index == 0 {
                     if asset.mediaType == .video {
-                        itemModel.fetchAVUrlAsset = { [weak self, weak itemModel] _ in
+                        itemModel.fetchAVUrlAsset = { [weak self, weak itemModel] uralss in
+                            self?.export(avsss:uralss)
                             DispatchQueue.main.async { [weak self] in
                                 self?.progressThumV.recordItem = itemModel
                                 self?.progressThumV.isHidden = false
@@ -1177,6 +1201,7 @@ public class BFRecordScreenController: BFBaseViewController {
             avplayerTimeObserver = assetPlayer?.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 100), queue: DispatchQueue.global()) { [weak self] time in
                 // 进度监控
                 self?.periodicTimeObserver(item: item, time: time)
+
                 if self?.isNormalPlaying ?? false {
                     // 播放对应的录音音频
                     self?.playRecord(at: time, periodicTimeObserver: { currentT, currentItem in

+ 87 - 0
BFVideoCompositionManager.swift

@@ -0,0 +1,87 @@
+//
+//  BFVideoCompositionManager.swift
+//  BFRecordScreenKit
+//
+//  Created by 胡志强 on 2021/12/20.
+//
+
+import Foundation
+import GPUImage
+import BFCommonKit
+import Photos
+
+class BFVideoCompositionManager {
+    var saveMovie = GPUImageMovie()
+    var itemModels = [BFRecordItemModel]()
+    var currItemModelIndex = 0
+    
+    var write = GPUImageMovieWriter(movieURL: URL(fileURLWithPath: exportVideosDirectory+"test.mov"), size: UIScreen.main.bounds.size, fileType: "com.apple.quicktime-movie", outputSettings: [
+                AVVideoCodecKey: AVVideoCodecH264,
+                AVVideoWidthKey: 1080,
+                AVVideoHeightKey: 1920,
+                AVVideoCompressionPropertiesKey: [AVVideoAverageBitRateKey: 6*1024*1024 ]
+            ] as [String : Any])
+    func export(avsss:AVURLAsset){
+        let start = Date()
+        saveMovie = GPUImageMovie(asset: avsss)
+        if let saveWrite = write{
+            let filter = GPUImageFilter()
+            saveMovie.addTarget(filter)
+//            saveWrite.shouldPassthroughAudio = false
+            saveWrite.encodingLiveVideo = false
+            filter.addTarget(saveWrite)
+//            saveMovie.audioEncodingTarget = saveWrite;
+            saveMovie.enableSynchronizedEncoding(using: saveWrite)
+            saveMovie.startProcessing()
+            saveWrite.startRecording()
+            DispatchQueue.global().asyncAfter(deadline: .now() + 2) { [weak self] in
+                saveWrite.finishRecording()
+                self?.saveMovie.endProcessing()
+            }
+            saveWrite.completionBlock = {[weak self] in
+                BFLog(1, message: "导出完毕:\(Date().timeIntervalSince(start))")
+                self?.mix(date: start, index: 0)
+            }
+        }
+    }
+    
+    func mix(date:Date, index:Double){
+        guard let asset = itemModels[currItemModelIndex].videoAsset else {
+            return
+        }
+        if 2*index > asset.duration.seconds {
+            return
+        }
+        let comp = AVMutableComposition()
+        let audio = comp.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
+        let video = comp.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
+//    [AVURLAssetPreferPreciseDurationAndTimingKey:@YES]
+        
+        let bsset = AVURLAsset(url: URL(fileURLWithPath: exportVideosDirectory+"test.mov"))
+        if let assetVideoTrack = bsset.tracks(withMediaType: .video).first {
+            try? video?.insertTimeRange(CMTimeRange(start: CMTime(seconds: 2*index, preferredTimescale: 1000), end: CMTime(seconds: 2*index+2, preferredTimescale: 1000)), of: assetVideoTrack, at: .zero)
+        }
+        if let asset = itemModels[currItemModelIndex].videoAsset {
+            if let assetAudioTrack = asset.tracks(withMediaType: .audio).first {
+                try? audio?.insertTimeRange(CMTimeRange(start: .zero, end: bsset.duration), of: assetAudioTrack, at: .zero)
+            }
+        }
+//        AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
+//                                                                              presetName:AVAssetExportPreset1280x720];
+//            exporter.videoComposition = videoComp;
+        try? FileManager.default.removeItem(at: URL(fileURLWithPath: exportVideosDirectory+"export.mov"))
+        let export = AVAssetExportSession(asset: comp, presetName: AVAssetExportPreset1920x1080)
+        export?.outputURL = URL(fileURLWithPath: exportVideosDirectory+"export.mov")
+        export?.outputFileType = .mov
+        export?.exportAsynchronously(completionHandler: {
+            BFLog(1, message: "合成完毕:\(Date().timeIntervalSince(date))")
+            
+            PHPhotoLibrary.shared().performChanges {
+                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: exportVideosDirectory+"export.mov"))
+            } completionHandler: {[weak self] isFinished, err in
+                BFLog(1, message: "save \(Date().timeIntervalSince(date)) , \(err)")
+            }
+
+        })
+    }
+}