|
@@ -7,10 +7,10 @@
|
|
|
|
|
|
import Foundation
|
|
import Foundation
|
|
import AVFoundation
|
|
import AVFoundation
|
|
-import BFFramework
|
|
|
|
-import BFVideoEditKit
|
|
|
|
import Photos
|
|
import Photos
|
|
import GPUImage
|
|
import GPUImage
|
|
|
|
+import BFCommonKit
|
|
|
|
+import BFMediaKit
|
|
|
|
|
|
public class BFRecordExport {
|
|
public class BFRecordExport {
|
|
public var progress : ((Float)->Void)?
|
|
public var progress : ((Float)->Void)?
|
|
@@ -46,29 +46,49 @@ public class BFRecordExport {
|
|
if let itemModels = data {
|
|
if let itemModels = data {
|
|
|
|
|
|
var totalDur = 0.0
|
|
var totalDur = 0.0
|
|
- for (index, itemModel) in itemModels.enumerated() {
|
|
|
|
|
|
+
|
|
|
|
+ // 切割视频素材
|
|
|
|
+ for (_, itemModel) in itemModels.enumerated() {
|
|
itemModel.videoStickers.removeAll()
|
|
itemModel.videoStickers.removeAll()
|
|
- let asset = itemModel.baseMaterial
|
|
|
|
- if let dur = asset?.duration.seconds {
|
|
|
|
- if synthesisAll {
|
|
|
|
- let bgMovieInfo = splitBaseMaterial(timelineIn: totalDur, model_in: 0, duration: dur)
|
|
|
|
- bgMovieInfo.volumeGain = 0
|
|
|
|
- itemModel.videoStickers.append(bgMovieInfo)
|
|
|
|
- totalDur += dur
|
|
|
|
- } else {
|
|
|
|
- var subDur = 0.0
|
|
|
|
- let drangs = itemModel.dealedDurationRanges.filter { ranges in
|
|
|
|
- ranges.isRecord == true
|
|
|
|
- }
|
|
|
|
- for srange in drangs {
|
|
|
|
- let range = srange.range
|
|
|
|
- let sticker = splitBaseMaterial(timelineIn: (totalDur + subDur), model_in: range.start.seconds, duration: range.duration.seconds)
|
|
|
|
- sticker.volumeGain = 0
|
|
|
|
- itemModel.videoStickers.append(sticker)
|
|
|
|
- subDur += range.duration.seconds
|
|
|
|
|
|
+
|
|
|
|
+ if synthesisAll {
|
|
|
|
+ // 保留全部
|
|
|
|
+ // let bgMovieInfo = splitBaseMaterial(timelineIn: totalDur, model_in: 0, duration: dur)
|
|
|
|
+ // bgMovieInfo.volumeGain = 0
|
|
|
|
+ // itemModel.videoStickers.append(bgMovieInfo)
|
|
|
|
+ // totalDur += dur
|
|
|
|
+ var subDur = 0.0
|
|
|
|
+ let drangs = itemModel.dealedDurationRanges
|
|
|
|
+ for srange in drangs {
|
|
|
|
+ let range = srange.range
|
|
|
|
+ let sticker = splitBaseMaterial(timelineIn: (totalDur + subDur), model_in: range.start.seconds, duration: range.duration.seconds)
|
|
|
|
+ sticker.volumeGain = srange.isRecord ? 0 : 100
|
|
|
|
+ itemModel.videoStickers.append(sticker)
|
|
|
|
+ subDur += range.duration.seconds
|
|
|
|
+ }
|
|
|
|
+ totalDur += subDur
|
|
|
|
+ // assert(totalDur == dur)
|
|
|
|
+ } else {
|
|
|
|
+ var subDur = 0.0
|
|
|
|
+ var drangs = itemModel.dealedDurationRanges.filter { srange in
|
|
|
|
+ srange.isRecord == true
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 是否按录音顺序排列
|
|
|
|
+ let needSort = false
|
|
|
|
+ if needSort {
|
|
|
|
+ drangs.sort { range1, range2 in
|
|
|
|
+ range1.index < range2.index
|
|
}
|
|
}
|
|
- totalDur += subDur
|
|
|
|
}
|
|
}
|
|
|
|
+ for srange in drangs {
|
|
|
|
+ let range = srange.range
|
|
|
|
+ let sticker = splitBaseMaterial(timelineIn: (totalDur + subDur), model_in: range.start.seconds, duration: range.duration.seconds)
|
|
|
|
+ sticker.volumeGain = 0
|
|
|
|
+ itemModel.videoStickers.append(sticker)
|
|
|
|
+ subDur += range.duration.seconds
|
|
|
|
+ }
|
|
|
|
+ totalDur += subDur
|
|
}
|
|
}
|
|
}
|
|
}
|
|
beginExport(synthesisAll:synthesisAll)
|
|
beginExport(synthesisAll:synthesisAll)
|
|
@@ -79,6 +99,16 @@ public class BFRecordExport {
|
|
self.exporter?.cancel()
|
|
self.exporter?.cancel()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ public func clearFileCache(){
|
|
|
|
+ data?.forEach({ itemModel in
|
|
|
|
+ itemModel.voiceStickers.forEach { model in
|
|
|
|
+ if let localPath = model.wavFilePath{
|
|
|
|
+ try? FileManager.default.removeItem(atPath: localPath)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
|
|
enum DispatchError: Error {
|
|
enum DispatchError: Error {
|
|
case timeout
|
|
case timeout
|
|
@@ -96,19 +126,19 @@ public class BFRecordExport {
|
|
|
|
|
|
func splitBaseMaterial(timelineIn:Double, model_in:Double, duration:Double) -> PQEditVisionTrackMaterialsModel{
|
|
func splitBaseMaterial(timelineIn:Double, model_in:Double, duration:Double) -> PQEditVisionTrackMaterialsModel{
|
|
let bgMovieInfo: PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel()
|
|
let bgMovieInfo: PQEditVisionTrackMaterialsModel = PQEditVisionTrackMaterialsModel()
|
|
- if let asset = data?.first?.baseMaterial {
|
|
|
|
- bgMovieInfo.type = StickerType.VIDEO.rawValue
|
|
|
|
- bgMovieInfo.locationPath = ((asset.url.absoluteString).removingPercentEncoding ?? "").replacingOccurrences(of: "file://", with: "")
|
|
|
|
- bgMovieInfo.timelineIn = timelineIn
|
|
|
|
- bgMovieInfo.timelineOut = timelineIn + duration
|
|
|
|
- bgMovieInfo.model_in = model_in
|
|
|
|
- bgMovieInfo.out = model_in + duration
|
|
|
|
- bgMovieInfo.canvasFillType = stickerContentMode.aspectFitStr.rawValue
|
|
|
|
- bgMovieInfo.volumeGain = 1
|
|
|
|
- bgMovieInfo.aptDuration = bgMovieInfo.timelineOut
|
|
|
|
- bgMovieInfo.duration = bgMovieInfo.timelineOut
|
|
|
|
- BFLog(1, message: "hhh- timIn:\(timelineIn), modIn:\(model_in), dur:\(duration)")
|
|
|
|
|
|
+ bgMovieInfo.type = StickerType.VIDEO.rawValue
|
|
|
|
+ bgMovieInfo.timelineIn = timelineIn
|
|
|
|
+ bgMovieInfo.timelineOut = timelineIn + duration
|
|
|
|
+ bgMovieInfo.model_in = model_in
|
|
|
|
+ bgMovieInfo.out = model_in + duration
|
|
|
|
+ bgMovieInfo.canvasFillType = stickerContentMode.aspectFitStr.rawValue
|
|
|
|
+ bgMovieInfo.volumeGain = 1
|
|
|
|
+ bgMovieInfo.aptDuration = bgMovieInfo.timelineOut
|
|
|
|
+ bgMovieInfo.duration = bgMovieInfo.timelineOut
|
|
|
|
+ if let localPath = data?.first?.localPath {
|
|
|
|
+ bgMovieInfo.locationPath = localPath
|
|
}
|
|
}
|
|
|
|
+ BFLog(1, message: "hhh- timIn:\(timelineIn), modIn:\(model_in), dur:\(duration)")
|
|
|
|
|
|
return bgMovieInfo
|
|
return bgMovieInfo
|
|
}
|
|
}
|
|
@@ -125,19 +155,49 @@ public class BFRecordExport {
|
|
let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
|
|
let outPutMP4URL = URL(fileURLWithPath: outPutMP4Path)
|
|
BFLog(1, message: "导出视频地址 \(outPutMP4URL)")
|
|
BFLog(1, message: "导出视频地址 \(outPutMP4URL)")
|
|
|
|
|
|
- guard let itemModel = data?.first else {
|
|
|
|
|
|
+ guard let itemData = data else {
|
|
|
|
+ let error = NSError(domain: "err", code: -1, userInfo: ["msg":"voiceStickers count += nil"])
|
|
|
|
+ self.exportCompletion?(error as Error, nil)
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+
|
|
// 处理导出
|
|
// 处理导出
|
|
- let voiceList = itemModel.voiceStickers
|
|
|
|
- let videoStickers = itemModel.videoStickers
|
|
|
|
- if voiceList.count > 0 || videoStickers.count > 1 {
|
|
|
|
|
|
+ var voiceList = [PQVoiceModel]()
|
|
|
|
+ var videoStickers = [PQEditVisionTrackMaterialsModel]()
|
|
|
|
+ var titleStickers = [PQEditSubTitleModel]()
|
|
|
|
+ for itemModel in itemData {
|
|
|
|
+ voiceList.append(contentsOf: itemModel.voiceStickers)
|
|
|
|
+ videoStickers.append(contentsOf: itemModel.videoStickers)
|
|
|
|
+ titleStickers.append(contentsOf: itemModel.titleStickers)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ guard let voiceCount = data?.reduce(0, { partialResult, itemModell in
|
|
|
|
+ itemModell.voiceStickers.count + partialResult
|
|
|
|
+ }) else {
|
|
|
|
+ BFLog(1, message: "voiceStickers count += nil")
|
|
|
|
+ let error = NSError(domain: "err", code: -1, userInfo: ["msg":"voiceStickers count += nil"])
|
|
|
|
+ self.exportCompletion?(error as Error, nil)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ guard let totalDuration = data?.reduce(0, { partialResult, itemModell in
|
|
|
|
+ (itemModell.materialDuraion ) + partialResult
|
|
|
|
+ }) else {
|
|
|
|
+ let error = NSError(domain: "err", code: -1, userInfo: ["msg":"时长计算出错"])
|
|
|
|
+ self.exportCompletion?(error as Error, nil)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 有录音操作或者多个视频,就会进入合成步骤,否则就是一个没有处理的素材,直接导出就行了
|
|
|
|
+ if voiceCount > 0 || videoStickers.count > 1 {
|
|
|
|
|
|
let (audioMix, composition) = mergeAudio(videoStickers: videoStickers, audios: voiceList,synthesisAll:synthesisAll)
|
|
let (audioMix, composition) = mergeAudio(videoStickers: videoStickers, audios: voiceList,synthesisAll:synthesisAll)
|
|
//
|
|
//
|
|
let filters = videoStickers.map { sticker in
|
|
let filters = videoStickers.map { sticker in
|
|
PQMovieFilter(movieSticker: sticker)
|
|
PQMovieFilter(movieSticker: sticker)
|
|
}
|
|
}
|
|
|
|
+//<<<<<<< HEAD
|
|
//add by ak 如果有字幕创建字幕 filter
|
|
//add by ak 如果有字幕创建字幕 filter
|
|
// var filters:[PQBaseFilter] = Array.init()
|
|
// var filters:[PQBaseFilter] = Array.init()
|
|
// for sticker in videoStickers {
|
|
// for sticker in videoStickers {
|
|
@@ -163,10 +223,12 @@ public class BFRecordExport {
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
exporter = PQCompositionExporter(asset: composition, videoComposition: nil, audioMix: audioMix, filters: filters, animationTool: nil, exportURL: outPutMP4URL)
|
|
exporter = PQCompositionExporter(asset: composition, videoComposition: nil, audioMix: audioMix, filters: filters, animationTool: nil, exportURL: outPutMP4URL)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
|
|
- let asset = data?.first?.baseMaterial
|
|
|
|
|
|
+// let asset = data?.first?.baseMaterial // 可能为空
|
|
|
|
|
|
- let size = getVideoSize(asset: asset!)
|
|
|
|
|
|
+ let size = UIScreen.main.bounds.size //getVideoSize(asset: asset!)
|
|
var orgeBitRate = Int(size.width * size.height * 3)
|
|
var orgeBitRate = Int(size.width * size.height * 3)
|
|
|
|
|
|
for stick in videoStickers {
|
|
for stick in videoStickers {
|
|
@@ -181,8 +243,9 @@ public class BFRecordExport {
|
|
}
|
|
}
|
|
|
|
|
|
BFLog(message: "导出设置的码率为:\(orgeBitRate)")
|
|
BFLog(message: "导出设置的码率为:\(orgeBitRate)")
|
|
|
|
+
|
|
if exporter!.prepare(videoSize: size, videoAverageBitRate: orgeBitRate) {
|
|
if exporter!.prepare(videoSize: size, videoAverageBitRate: orgeBitRate) {
|
|
- exporter!.start(playeTimeRange: CMTimeRange(start: CMTime.zero, end: synthesisAll ? asset?.duration as! CMTime : composition.duration))
|
|
|
|
|
|
+ exporter!.start(playeTimeRange: CMTimeRange(start: CMTime.zero, end: synthesisAll ? CMTime(seconds: totalDuration, preferredTimescale: 100) : composition.duration))
|
|
}
|
|
}
|
|
exporter?.progressClosure = { [weak self] _, _, progress in
|
|
exporter?.progressClosure = { [weak self] _, _, progress in
|
|
// BFLog(message: "正片合成进度 \(progress * 100)%")
|
|
// BFLog(message: "正片合成进度 \(progress * 100)%")
|
|
@@ -200,7 +263,7 @@ public class BFRecordExport {
|
|
cShowHUB(superView: nil, msg: ( outSeconds == 0) ? "合成失败请重试。" : "合成成功")
|
|
cShowHUB(superView: nil, msg: ( outSeconds == 0) ? "合成失败请重试。" : "合成成功")
|
|
self?.exportCompletion?(nil, url)
|
|
self?.exportCompletion?(nil, url)
|
|
|
|
|
|
- }else{
|
|
|
|
|
|
+ } else {
|
|
let error = NSError(domain: "err", code: -1, userInfo: nil)
|
|
let error = NSError(domain: "err", code: -1, userInfo: nil)
|
|
self?.exportCompletion?(error as Error, nil)
|
|
self?.exportCompletion?(error as Error, nil)
|
|
cShowHUB(superView: nil, msg: "导出失败")
|
|
cShowHUB(superView: nil, msg: "导出失败")
|
|
@@ -211,8 +274,8 @@ public class BFRecordExport {
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
// 没有处理,直接copy原文件
|
|
// 没有处理,直接copy原文件
|
|
- if let ass = data?.first?.baseMaterial{
|
|
|
|
- self.exportCompletion?(nil, ass.url)
|
|
|
|
|
|
+ if let localPath = data?.first?.localPath{
|
|
|
|
+ self.exportCompletion?(nil, URL(fileURLWithPath: localPath))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -254,7 +317,7 @@ extension BFRecordExport {
|
|
}
|
|
}
|
|
sticker.volumeGain = 50
|
|
sticker.volumeGain = 50
|
|
totalDuration = max(totalDuration, sticker.duration)
|
|
totalDuration = max(totalDuration, sticker.duration)
|
|
- tempParameters += PQVideoEditViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
|
|
|
|
|
|
+ tempParameters += PQPlayerViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
|
|
}
|
|
}
|
|
if let voices = audios {
|
|
if let voices = audios {
|
|
if synthesisAll {
|
|
if synthesisAll {
|
|
@@ -286,7 +349,7 @@ extension BFRecordExport {
|
|
sticker.duration = duration
|
|
sticker.duration = duration
|
|
sticker.locationPath = model.wavFilePath
|
|
sticker.locationPath = model.wavFilePath
|
|
sticker.volumeGain = 100 //Float64(model.volume)
|
|
sticker.volumeGain = 100 //Float64(model.volume)
|
|
- tempParameters += PQVideoEditViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
|
|
|
|
|
|
+ tempParameters += PQPlayerViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
|
|
totalDur += duration
|
|
totalDur += duration
|
|
}
|
|
}
|
|
|
|
|
|
@@ -309,7 +372,7 @@ extension BFRecordExport {
|
|
sticker.duration = sticker.aptDuration
|
|
sticker.duration = sticker.aptDuration
|
|
sticker.locationPath = model.wavFilePath
|
|
sticker.locationPath = model.wavFilePath
|
|
sticker.volumeGain = 100 //Float64(model.volume)
|
|
sticker.volumeGain = 100 //Float64(model.volume)
|
|
- tempParameters += PQVideoEditViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
|
|
|
|
|
|
+ tempParameters += PQPlayerViewModel.dealWithMaterialTrack(stickerModel: sticker, composition: composition)
|
|
}
|
|
}
|
|
|
|
|
|
return tempParameters
|
|
return tempParameters
|