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