|
@@ -104,13 +104,17 @@ class WSSEditVideoViewController: MTViewController {
|
|
fileprivate var templateModel: WSSCommonTemplateModel?
|
|
fileprivate var templateModel: WSSCommonTemplateModel?
|
|
|
|
|
|
fileprivate var exportSession: AVAssetExportSession?
|
|
fileprivate var exportSession: AVAssetExportSession?
|
|
-
|
|
|
|
fileprivate var updateDisplayLink: CADisplayLink?
|
|
fileprivate var updateDisplayLink: CADisplayLink?
|
|
|
|
|
|
fileprivate var mixAudioExportSession: AVAssetExportSession?
|
|
fileprivate var mixAudioExportSession: AVAssetExportSession?
|
|
-
|
|
|
|
fileprivate var mixAudioUpdateDisplayLink: CADisplayLink?
|
|
fileprivate var mixAudioUpdateDisplayLink: CADisplayLink?
|
|
|
|
|
|
|
|
+ fileprivate var waterMarkSession: AVAssetExportSession?
|
|
|
|
+ fileprivate var waterMarkSessionDisplayLink: CADisplayLink?
|
|
|
|
+
|
|
|
|
+ /// 用于waterMark主动Cancel 没有条件判断
|
|
|
|
+ fileprivate var isUserCancel = false
|
|
|
|
+
|
|
fileprivate var isTwoExportSessionMode = false
|
|
fileprivate var isTwoExportSessionMode = false
|
|
|
|
|
|
fileprivate var pasterInputs: [OJAPasterFrameInput] = []
|
|
fileprivate var pasterInputs: [OJAPasterFrameInput] = []
|
|
@@ -219,6 +223,7 @@ extension WSSEditVideoViewController {
|
|
|
|
|
|
// 视频导出取消
|
|
// 视频导出取消
|
|
NotificationCenter.default.reactive.notifications(forName: Notification.Name.kSVProgressHUDCacnel).take(duringLifetimeOf: self).observeValues { [weak self] _ in
|
|
NotificationCenter.default.reactive.notifications(forName: Notification.Name.kSVProgressHUDCacnel).take(duringLifetimeOf: self).observeValues { [weak self] _ in
|
|
|
|
+ self?.isUserCancel = true
|
|
|
|
|
|
if let exportEfftect = self?.effectExport {
|
|
if let exportEfftect = self?.effectExport {
|
|
exportEfftect.endProcessing()
|
|
exportEfftect.endProcessing()
|
|
@@ -242,6 +247,11 @@ extension WSSEditVideoViewController {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if let waterMartExport = self?.waterMarkSession {
|
|
|
|
+ waterMartExport.cancelExport()
|
|
|
|
+ self?.waterMarkSession = nil
|
|
|
|
+ }
|
|
|
|
+
|
|
hideHud()
|
|
hideHud()
|
|
self?.videoPlayer?.play()
|
|
self?.videoPlayer?.play()
|
|
}
|
|
}
|
|
@@ -630,6 +640,7 @@ private extension WSSEditVideoViewController {
|
|
movieAsset = AVURLAsset(url: url)
|
|
movieAsset = AVURLAsset(url: url)
|
|
}
|
|
}
|
|
isTwoExportSessionMode = false
|
|
isTwoExportSessionMode = false
|
|
|
|
+ isUserCancel = false
|
|
|
|
|
|
/// 当用户添加新贴纸或者设置滤镜,需要进行重新渲染
|
|
/// 当用户添加新贴纸或者设置滤镜,需要进行重新渲染
|
|
if pasterOverlayView.pasterArray.count > 0 || lookupFilter != nil {
|
|
if pasterOverlayView.pasterArray.count > 0 || lookupFilter != nil {
|
|
@@ -694,26 +705,19 @@ private extension WSSEditVideoViewController {
|
|
volume = controlMusicView.currentMusicVolume()
|
|
volume = controlMusicView.currentMusicVolume()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ let combineVideoAsset = AVURLAsset(url: outputUrl)
|
|
|
|
+ let presetName = WSSCreativeTemplateConstant.getAdjustNormalPreset(videoAsset: combineVideoAsset)
|
|
if let musicFile = self?.cutMusicFileUrl, let originalVideo = self?.movieAsset, volume.1 > 0.0 {
|
|
if let musicFile = self?.cutMusicFileUrl, let originalVideo = self?.movieAsset, volume.1 > 0.0 {
|
|
// 有选择音乐,并且音乐音量大于0.0,视频混入音乐
|
|
// 有选择音乐,并且音乐音量大于0.0,视频混入音乐
|
|
- let videoAsset = AVURLAsset(url: outputUrl)
|
|
|
|
let musicAsset = AVURLAsset(url: musicFile)
|
|
let musicAsset = AVURLAsset(url: musicFile)
|
|
- let presetName = WSSCreativeTemplateConstant.getAdjustNormalPreset(videoAsset: videoAsset)
|
|
|
|
if let mixAudioExportSession = try? WSSMediaOperationTool.mixAudio(withAudioAsset: musicAsset,
|
|
if let mixAudioExportSession = try? WSSMediaOperationTool.mixAudio(withAudioAsset: musicAsset,
|
|
- forVideo: videoAsset,
|
|
|
|
|
|
+ forVideo: combineVideoAsset,
|
|
originalAudioAsset: originalVideo,
|
|
originalAudioAsset: originalVideo,
|
|
audioVolume: volume.1,
|
|
audioVolume: volume.1,
|
|
videoVolume: volume.0,
|
|
videoVolume: volume.0,
|
|
presetName: presetName,
|
|
presetName: presetName,
|
|
finish: { [weak self] result in
|
|
finish: { [weak self] result in
|
|
- DispatchQueue.main.async {
|
|
|
|
- self?.stopMixAudioDisplayLink()
|
|
|
|
- if result != nil, let path = (result as? AVURLAsset)?.url {
|
|
|
|
- self?.combineVideoSuccess(path.absoluteString.replacingOccurrences(of: "file://", with: ""), needAddWaterMark: false)
|
|
|
|
- } else {
|
|
|
|
- hideHud()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ self?.mixAudioFinishHandle(resultAsset: result)
|
|
}) {
|
|
}) {
|
|
DispatchQueue.main.async {
|
|
DispatchQueue.main.async {
|
|
self?.setupMixAudioDisplaLink()
|
|
self?.setupMixAudioDisplaLink()
|
|
@@ -724,24 +728,20 @@ private extension WSSEditVideoViewController {
|
|
} else {
|
|
} else {
|
|
// 没有选音乐或者配乐音量为0,保留视频原声
|
|
// 没有选音乐或者配乐音量为0,保留视频原声
|
|
if let originalVideo = self?.movieAsset {
|
|
if let originalVideo = self?.movieAsset {
|
|
- let combineVideo = AVURLAsset(url: outputUrl)
|
|
|
|
- let presetName = WSSCreativeTemplateConstant.getAdjustNormalPreset(videoAsset: combineVideo)
|
|
|
|
- _ = try? WSSMediaOperationTool.mixAudio(withAudioAsset: originalVideo,
|
|
|
|
- forVideo: combineVideo,
|
|
|
|
- originalAudioAsset: nil,
|
|
|
|
- audioVolume: volume.0,
|
|
|
|
- videoVolume: 0.0,
|
|
|
|
- presetName: presetName,
|
|
|
|
- finish: { [weak self] mixResult in
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- self?.stopMixAudioDisplayLink()
|
|
|
|
- if mixResult != nil, let path = (mixResult as? AVURLAsset)?.url {
|
|
|
|
- self?.combineVideoSuccess(path.absoluteString.replacingOccurrences(of: "file://", with: ""), needAddWaterMark: false)
|
|
|
|
- } else {
|
|
|
|
- hideHud()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
|
|
+ if let mixAudioExportSession = try? WSSMediaOperationTool.mixAudio(withAudioAsset: originalVideo,
|
|
|
|
+ forVideo: combineVideoAsset,
|
|
|
|
+ originalAudioAsset: nil,
|
|
|
|
+ audioVolume: volume.0,
|
|
|
|
+ videoVolume: 0.0,
|
|
|
|
+ presetName: presetName,
|
|
|
|
+ finish: { [weak self] mixResult in
|
|
|
|
+ self?.mixAudioFinishHandle(resultAsset: mixResult, needAddWaterMark: false)
|
|
|
|
+ }) {
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ self?.setupMixAudioDisplaLink()
|
|
|
|
+ self?.mixAudioExportSession = mixAudioExportSession
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -782,27 +782,39 @@ private extension WSSEditVideoViewController {
|
|
audioVolume: volume.1,
|
|
audioVolume: volume.1,
|
|
videoVolume: volume.0,
|
|
videoVolume: volume.0,
|
|
presetName: presetName,
|
|
presetName: presetName,
|
|
- finish: { result in
|
|
|
|
- if result != nil, let path = (result as? AVURLAsset)?.url {
|
|
|
|
- self.combineVideoSuccess(path.absoluteString.replacingOccurrences(of: "file://", with: ""))
|
|
|
|
- }
|
|
|
|
|
|
+ finish: { [weak self] result in
|
|
|
|
+ self?.mixAudioFinishHandle(resultAsset: result)
|
|
}) {
|
|
}) {
|
|
setupMixAudioDisplaLink()
|
|
setupMixAudioDisplaLink()
|
|
self.mixAudioExportSession = mixAudioExportSession
|
|
self.mixAudioExportSession = mixAudioExportSession
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- // 没有选音乐或者配乐音量为0,保留视频原声
|
|
|
|
|
|
+ // 无选择音乐,但是用户可调控源视频音量大小
|
|
let presetName = WSSCreativeTemplateConstant.getAdjustNormalPreset(videoAsset: movieAsset)
|
|
let presetName = WSSCreativeTemplateConstant.getAdjustNormalPreset(videoAsset: movieAsset)
|
|
- exportSession = WSSMediaOperationTool.exportVideo(withVideoAsset: movieAsset,
|
|
|
|
- exportPath: URL(fileURLWithPath: kFinalOutputURLString),
|
|
|
|
- presetName: presetName,
|
|
|
|
- fileType: AVFileType.mp4,
|
|
|
|
- videoComposition: nil,
|
|
|
|
- audioMix: nil) { [weak self] isSuccess in
|
|
|
|
- if isSuccess {
|
|
|
|
- self?.combineVideoSuccess(kFinalOutputURLString)
|
|
|
|
- }
|
|
|
|
|
|
+ if let mixAudioExportSession = try? WSSMediaOperationTool.mixAudio(withAudioAsset: movieAsset,
|
|
|
|
+ forVideo: movieAsset,
|
|
|
|
+ originalAudioAsset: nil,
|
|
|
|
+ audioVolume: volume.0,
|
|
|
|
+ videoVolume: 0.0,
|
|
|
|
+ presetName: presetName,
|
|
|
|
+ finish: { [weak self] result in
|
|
|
|
+ self?.mixAudioFinishHandle(resultAsset: result)
|
|
|
|
+ }) {
|
|
|
|
+ setupMixAudioDisplaLink()
|
|
|
|
+ self.mixAudioExportSession = mixAudioExportSession
|
|
}
|
|
}
|
|
|
|
+// // 没有选音乐或者配乐音量为0,保留视频原声
|
|
|
|
+// let presetName = WSSCreativeTemplateConstant.getAdjustNormalPreset(videoAsset: movieAsset)
|
|
|
|
+// exportSession = WSSMediaOperationTool.exportVideo(withVideoAsset: movieAsset,
|
|
|
|
+// exportPath: URL(fileURLWithPath: kFinalOutputURLString),
|
|
|
|
+// presetName: presetName,
|
|
|
|
+// fileType: AVFileType.mp4,
|
|
|
|
+// videoComposition: nil,
|
|
|
|
+// audioMix: nil) { [weak self] isSuccess in
|
|
|
|
+// if isSuccess {
|
|
|
|
+// self?.combineVideoSuccess(kFinalOutputURLString)
|
|
|
|
+// }
|
|
|
|
+// }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -815,6 +827,17 @@ private extension WSSEditVideoViewController {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ func mixAudioFinishHandle(resultAsset: AVAsset?, needAddWaterMark: Bool = true) {
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ self.stopMixAudioDisplayLink()
|
|
|
|
+ if resultAsset != nil, let path = (resultAsset as? AVURLAsset)?.url {
|
|
|
|
+ self.combineVideoSuccess(path.absoluteString.replacingOccurrences(of: "file://", with: ""), needAddWaterMark: needAddWaterMark)
|
|
|
|
+ } else {
|
|
|
|
+ hideHud()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
func combineVideoSuccess(_ outputPath: String, needAddWaterMark: Bool = true) {
|
|
func combineVideoSuccess(_ outputPath: String, needAddWaterMark: Bool = true) {
|
|
if needAddWaterMark && WSSProfileItemModel.isWatermarkOn() {
|
|
if needAddWaterMark && WSSProfileItemModel.isWatermarkOn() {
|
|
// 加水印
|
|
// 加水印
|
|
@@ -829,16 +852,25 @@ private extension WSSEditVideoViewController {
|
|
let scale = 2
|
|
let scale = 2
|
|
let watermarkSize = CGSize(width: 35 * scale, height: 35 * scale)
|
|
let watermarkSize = CGSize(width: 35 * scale, height: 35 * scale)
|
|
|
|
|
|
- try? WSSMediaOperationTool.insertWatermark(withVideoAsset: videoAsset, watermark: watermark, watermarkSize: watermarkSize, finish: { [weak self] tmpUrl in
|
|
|
|
|
|
+ let waterMarkSession = try? WSSMediaOperationTool.insertWatermark(withVideoAsset: videoAsset, watermark: watermark, watermarkSize: watermarkSize, finish: { [weak self] tmpUrl in
|
|
DispatchQueue.main.async {
|
|
DispatchQueue.main.async {
|
|
|
|
+ guard let userCancel = self?.isUserCancel,
|
|
|
|
+ !userCancel
|
|
|
|
+ else {
|
|
|
|
+ hideHud()
|
|
|
|
+ self?.stopWaterMarkDisplay()
|
|
|
|
+ return
|
|
|
|
+ }
|
|
if let finishUrl = tmpUrl {
|
|
if let finishUrl = tmpUrl {
|
|
|
|
+ self?.stopWaterMarkDisplay()
|
|
self?.saveFinalVideoToAlbum(withOutputPath: finishUrl.path)
|
|
self?.saveFinalVideoToAlbum(withOutputPath: finishUrl.path)
|
|
} else {
|
|
} else {
|
|
showHud(withOnlyText: "保存失败,稍后重试")
|
|
showHud(withOnlyText: "保存失败,稍后重试")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
})
|
|
})
|
|
|
|
+ self.waterMarkSession = waterMarkSession
|
|
|
|
+ setupWaterMarkDisplay()
|
|
} else {
|
|
} else {
|
|
saveFinalVideoToAlbum(withOutputPath: outputPath)
|
|
saveFinalVideoToAlbum(withOutputPath: outputPath)
|
|
}
|
|
}
|
|
@@ -865,7 +897,6 @@ private extension WSSEditVideoViewController {
|
|
|
|
|
|
DispatchQueue.main.async { [weak self] in
|
|
DispatchQueue.main.async { [weak self] in
|
|
if isSuccess {
|
|
if isSuccess {
|
|
- showProgressCancelHub(progress: 1.0, status: "正在合成...")
|
|
|
|
hideHud()
|
|
hideHud()
|
|
OJAJumpManager.jumpToEditFinishViewController(filePath: outputPath, videoLocalId: videoID, template: self?.templateModel, nav: nav)
|
|
OJAJumpManager.jumpToEditFinishViewController(filePath: outputPath, videoLocalId: videoID, template: self?.templateModel, nav: nav)
|
|
}
|
|
}
|
|
@@ -881,6 +912,7 @@ private extension WSSEditVideoViewController {
|
|
func cleanDisplayLink() {
|
|
func cleanDisplayLink() {
|
|
stopUpdateDisplayLink()
|
|
stopUpdateDisplayLink()
|
|
stopMixAudioDisplayLink()
|
|
stopMixAudioDisplayLink()
|
|
|
|
+ stopWaterMarkDisplay()
|
|
hideHud()
|
|
hideHud()
|
|
}
|
|
}
|
|
|
|
|
|
@@ -892,6 +924,8 @@ private extension WSSEditVideoViewController {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // MARK: mixAudioExportSession
|
|
|
|
+
|
|
func stopMixAudioDisplayLink() {
|
|
func stopMixAudioDisplayLink() {
|
|
if let mixAudioDisplayLink = self.mixAudioUpdateDisplayLink {
|
|
if let mixAudioDisplayLink = self.mixAudioUpdateDisplayLink {
|
|
mixAudioDisplayLink.isPaused = true
|
|
mixAudioDisplayLink.isPaused = true
|
|
@@ -928,12 +962,19 @@ private extension WSSEditVideoViewController {
|
|
if let exportSession = self.mixAudioExportSession {
|
|
if let exportSession = self.mixAudioExportSession {
|
|
let progress = exportSession.progress
|
|
let progress = exportSession.progress
|
|
if isTwoExportSessionMode {
|
|
if isTwoExportSessionMode {
|
|
- showProgressCancelHub(progress: 0.7 + 0.3 * progress, status: "正在合成...")
|
|
|
|
|
|
+ var realProgress = simd_clamp(0.7 + 0.3 * progress, 0.7, 1.0)
|
|
|
|
+ if WSSProfileItemModel.isWatermarkOn() {
|
|
|
|
+ realProgress = simd_clamp(0.7 + 0.2 * progress, 0.7, 0.9)
|
|
|
|
+ }
|
|
|
|
+ showProgressCancelHub(progress: realProgress, status: "正在合成...")
|
|
} else {
|
|
} else {
|
|
- showProgressCancelHub(progress: progress, status: "正在合成...")
|
|
|
|
|
|
+ var realProgress = progress
|
|
|
|
+ if WSSProfileItemModel.isWatermarkOn() {
|
|
|
|
+ realProgress = simd_clamp(0.5 * realProgress, 0.0, 0.5)
|
|
|
|
+ }
|
|
|
|
+ showProgressCancelHub(progress: realProgress, status: "正在合成...")
|
|
}
|
|
}
|
|
if progress >= 1.0 {
|
|
if progress >= 1.0 {
|
|
-// hideHud()
|
|
|
|
displayLink.invalidate()
|
|
displayLink.invalidate()
|
|
mixAudioUpdateDisplayLink = nil
|
|
mixAudioUpdateDisplayLink = nil
|
|
}
|
|
}
|
|
@@ -943,6 +984,42 @@ private extension WSSEditVideoViewController {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // MARK: waterMarkExportSession
|
|
|
|
+
|
|
|
|
+ func setupWaterMarkDisplay() {
|
|
|
|
+ if waterMarkSessionDisplayLink != nil {
|
|
|
|
+ waterMarkSessionDisplayLink?.isPaused = true
|
|
|
|
+ waterMarkSessionDisplayLink?.invalidate()
|
|
|
|
+ waterMarkSessionDisplayLink = nil
|
|
|
|
+ }
|
|
|
|
+ let displayLink = CADisplayLink(target: self, selector: #selector(updateWaterMarkProgress))
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ displayLink.add(to: .current, forMode: .common)
|
|
|
|
+ self.waterMarkSessionDisplayLink = displayLink
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func stopWaterMarkDisplay() {
|
|
|
|
+ if let displayLink = waterMarkSessionDisplayLink {
|
|
|
|
+ displayLink.isPaused = true
|
|
|
|
+ displayLink.invalidate()
|
|
|
|
+ waterMarkSessionDisplayLink = nil
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @objc func updateWaterMarkProgress() {
|
|
|
|
+ if let exportSession = waterMarkSession {
|
|
|
|
+ let progress = exportSession.progress
|
|
|
|
+ var realProgress = simd_clamp(0.5 + 0.5 * progress, 0.5, 1.0)
|
|
|
|
+ if isTwoExportSessionMode {
|
|
|
|
+ realProgress = simd_clamp(0.9 + 0.1 * progress, 0.9, 1.0)
|
|
|
|
+ }
|
|
|
|
+ showProgressCancelHub(progress: realProgress, status: "正在合成...")
|
|
|
|
+ } else {
|
|
|
|
+ stopWaterMarkDisplay()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
// MARK: data
|
|
// MARK: data
|
|
|
|
|
|
func loadTopMuisc() {
|
|
func loadTopMuisc() {
|