PQStuckPointEditerController.swift 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. //
  2. // PQStuckPointEditerController.swift
  3. // PQSpeed
  4. //
  5. // Created by ak on 2021/4/26.
  6. // Copyright © 2021 BytesFlow. All rights reserved.
  7. // 功能:卡点音乐编辑界面
  8. // 创建不同玩法的类型
  9. enum createStickersModel {
  10. case createStickersModelPoint // 卡点
  11. case createStickersModelSpeed // 快慢速
  12. }
  13. import BFCommonKit
  14. import Foundation
  15. import ObjectMapper
  16. import Photos
  17. import RealmSwift
  18. import UIKit
  19. class PQStuckPointEditerController: PQBaseViewController {
  20. // 是否导出视频成功
  21. var isExportVideosSuccess: Bool = false
  22. // 是否请求卡点数据成功
  23. var isStuckPointDataSuccess: Bool = false
  24. // 是否同步音乐成功
  25. var isSynchroMusicInfoSuccess: Bool = false
  26. /// 当前所有的filter
  27. var filters: Array = Array<ImageProcessingOperation>.init()
  28. // 选中的总时长
  29. var selectedTotalDuration: Float64 = 0
  30. // 选择的总数
  31. var selectedDataCount: Int = 0
  32. // 选择的图片总数
  33. var selectedImageDataCount: Int = 0
  34. // 选中的素材数据
  35. var selectedPhotoData: [PHAsset]? {
  36. didSet {
  37. if selectedPhotoData != nil, (selectedPhotoData?.count ?? 0) > 0 {
  38. selectedMetarialData = Array<PQEditVisionTrackMaterialsModel>.init()
  39. selectedPhotoData?.forEach { phAsset in
  40. let metarialData = PQEditVisionTrackMaterialsModel()
  41. metarialData.asset = phAsset
  42. metarialData.width = Float(phAsset.pixelWidth)
  43. metarialData.itemWidth = Float(phAsset.pixelWidth)
  44. metarialData.height = Float(phAsset.pixelHeight)
  45. metarialData.itemHeight = Float(phAsset.pixelHeight)
  46. if phAsset.mediaType == .image {
  47. metarialData.type = "image"
  48. } else if phAsset.mediaType == .video {
  49. metarialData.type = "video"
  50. metarialData.duration = Float64(phAsset.duration)
  51. }
  52. metarialData.canvasFillType = phAsset.canvasFillType ?? ""
  53. metarialData.locationPath = phAsset.localPath ?? ""
  54. metarialData.selectedIndex = phAsset.selectedIndex ?? 1
  55. metarialData.originalData = phAsset.originalData
  56. metarialData.coverImageUI = phAsset.image
  57. selectedMetarialData?.append(metarialData)
  58. }
  59. }
  60. }
  61. }
  62. var selectedMetarialData: [PQEditVisionTrackMaterialsModel]?
  63. // 选中的音乐数据
  64. var stuckPointMusicData: PQVoiceModel?
  65. // 保存所有段的所有贴纸,音频信息,用于播放器的渲染使用
  66. var projectModel: PQEditProjectModel = PQEditProjectModel()
  67. // 从草稿箱进入的项目数据
  68. var draftProjectModel: PQEditProjectModel?
  69. var mStickers: [PQEditVisionTrackMaterialsModel]?
  70. // 播放器的开始和结束时间,1,刚进界面使用推荐的开始结束时间,2,用户修改起结点时修改
  71. var playeTimeRange: CMTimeRange = CMTimeRange()
  72. // add by ak 是否是再创作模式
  73. var isReCreate: Bool = false
  74. // 下一步
  75. lazy var nextBtn: UIButton = {
  76. let nextBtn = UIButton(type: .custom)
  77. nextBtn.frame = CGRect(x: cScreenWidth - 16 - cDefaultMargin * 6, y: cDevice_iPhoneStatusBarHei + (cDevice_iPhoneNavBarHei - cDefaultMargin * 3) / 2, width: cDefaultMargin * 6, height: cDefaultMargin * 3)
  78. nextBtn.setTitle("合成", for: .normal)
  79. nextBtn.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .medium)
  80. nextBtn.addTarget(self, action: #selector(nextBtnClick(sender:)), for: .touchUpInside)
  81. nextBtn.backgroundColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
  82. nextBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#FFFFFF"), for: .normal)
  83. nextBtn.uxy_acceptEventInterval = 0.5
  84. nextBtn.addCorner(corner: 3)
  85. return nextBtn
  86. }()
  87. // 播放器显示 view
  88. lazy var playerView: PQGPUImagePlayerView = {
  89. let playerHeight = cScreenWidth
  90. let playerView = PQGPUImagePlayerView(frame: CGRect(x: 0, y: navHeadImageView?.frame.maxY ?? 0, width: playerHeight, height: playerHeight))
  91. playerView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
  92. playerView.isShowLine = false
  93. playerView.showGaussianBlur = true
  94. playerView.playerEmptyView.isHidden = true
  95. return playerView
  96. }()
  97. /// 节奏选择视图
  98. lazy var sustomSwitchView: PQCustomSwitchView = {
  99. let sustomSwitchView = PQCustomSwitchView(frame: CGRect(x: (view.frame.width - cDefaultMargin * 28) / 2, y: view.frame.height - cSafeAreaHeight - cDefaultMargin * 3 - cDefaultMargin * 3, width: cDefaultMargin * 28, height: 35), titles: ["快节奏", "适中", "慢节奏"], defaultIndex: stuckPointMusicData?.speed ?? 2)
  100. sustomSwitchView.switchChangeHandle = { [weak self] sender in
  101. // 改变速率
  102. self?.stuckPointMusicData?.speed = sender.tag
  103. self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.bgmInfo?.rhythmMusicSpeed = sender.tag
  104. // 播放前先暂停
  105. // self?.playerView.stop()
  106. // 开始播放
  107. self?.settingPlayerView()
  108. // 点击上报:选择节奏
  109. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_selectRhythm, pageSource: .sp_stuck_previewSyncedUp, extParams: ["rhythmNumber": sender.tag], remindmsg: "点击上报:选择节奏")
  110. }
  111. return sustomSwitchView
  112. }()
  113. /// 裁剪视图
  114. lazy var stuckPointCuttingView: PQStuckPointCuttingView = {
  115. let stuckPointCuttingView = PQStuckPointCuttingView(frame: CGRect(x: 0, y: sustomSwitchView.frame.minY - cDefaultMargin * 14 - cDefaultMargin * 2, width: view.frame.width, height: cDefaultMargin * 14), duration: CGFloat(Float(stuckPointMusicData?.duration ?? "0") ?? 0), startTime: CGFloat(stuckPointMusicData?.startTime ?? 0), endTime: CGFloat(stuckPointMusicData?.endTime ?? 0))
  116. /// 裁剪进度回调
  117. stuckPointCuttingView.videoRangeDidChanged = { [weak self] startTime, endTime in
  118. BFLog(message: "裁剪返回--startTime = \(startTime),endTime = \(endTime)")
  119. }
  120. /// 播放进度回调
  121. stuckPointCuttingView.videoProgressDidChanged = { [weak self] progress in
  122. BFLog(message: "进度更新返回--progress = \(progress) \(String(describing: self?.playerView.mPlayeTimeRange))")
  123. }
  124. /// 拖缀结束的回调 type - 1-拖动左边裁剪结束 2--拖动右边裁剪结束 3-进度条拖动结束 4-滑动结束
  125. stuckPointCuttingView.videoDidEndDragging = { [weak self] type, startTime, endTime, progress in
  126. BFLog(message: "拖拽结束返回--type = \(type),startTime = \(startTime),endTime = \(endTime),progress = \(progress)")
  127. self?.playerView.pause()
  128. // 修改最新值
  129. self?.stuckPointMusicData?.startTime = Float64(startTime)
  130. self?.stuckPointMusicData?.endTime = Float64(endTime)
  131. // 红的指针完成
  132. if type == 3 {
  133. if CMTimeGetSeconds(self?.playerView.mPlayeTimeRange?.end ?? .zero) == 0 {
  134. BFLog(message: "mPlayeTimeRange is error")
  135. return
  136. }
  137. let newBeginSconds = (Double(startTime) + (Double(endTime) - Double(startTime)) * Double(progress)) * 600
  138. BFLog(message: " newBeginSconds is \(newBeginSconds)")
  139. let seekTimeRange: CMTimeRange = CMTimeRange(start: CMTime(value: CMTimeValue(Int64(newBeginSconds)), timescale: 600), end:
  140. CMTime(value: CMTimeValue(Int64(endTime * 600)), timescale: 600))
  141. BFLog(message: "修改的开始 \(CMTimeGetSeconds(seekTimeRange.start)) 结束 \(CMTimeGetSeconds(seekTimeRange.end))")
  142. // 重新设置有效缓存
  143. self?.playerView.configCache(beginTime: CMTimeGetSeconds(seekTimeRange.start))
  144. self?.playerView.play(pauseFirstFrame: false, playeTimeRange: seekTimeRange)
  145. } else {
  146. // 更改素材开始时间及结束时间
  147. self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.out = Float64(endTime)
  148. self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.model_in = Float64(startTime)
  149. self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.timelineIn = Float64(startTime)
  150. self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.timelineOut = Float64(endTime)
  151. BFLog(message: "调整后总时长: \(endTime - startTime) startTime:\(startTime) endTime:\(endTime)")
  152. // 初始化音频的开始和结束时间
  153. self?.playeTimeRange = CMTimeRange(start: CMTimeMakeWithSeconds(Float64(startTime), preferredTimescale: BASE_FILTER_TIMESCALE), end: CMTimeMakeWithSeconds(Float64(endTime), preferredTimescale: BASE_FILTER_TIMESCALE))
  154. DispatchQueue.global().async { // 并行、异步
  155. let beginTime: TimeInterval = Date().timeIntervalSince1970
  156. self?.mStickers = self?.createStickers(sections: self?.projectModel.sData?.sections ?? List(), inputSize: CGSize(width: CGFloat(self?.projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(self?.projectModel.sData?.videoMetaData?.videoHeight ?? 0)))
  157. self?.playerView.mStickers = self?.mStickers
  158. DispatchQueue.main.async { // 串行、异步
  159. var endTime: TimeInterval = Date().timeIntervalSince1970
  160. BFLog(message: "endTime is endTimeendTime \(endTime = beginTime)")
  161. self?.playerView.play(pauseFirstFrame: false, playeTimeRange: self!.playeTimeRange)
  162. }
  163. }
  164. }
  165. // 埋点上报
  166. if type == 1 || type == 2 {
  167. // 点击上报:拖动拖拽条(左/右部分)
  168. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: type == 1 ? .ot_click_dragFront : .ot_click_dragBehind, pageSource: .sp_stuck_previewSyncedUp, extParams: ["targetTime": type == 1 ? startTime * 1000 : endTime * 1000], remindmsg: "点击上报:拖动拖拽条(左/右部分)")
  169. }
  170. }
  171. return stuckPointCuttingView
  172. }()
  173. /// 卡点时长显示视图
  174. lazy var timeRemindLab: UILabel = {
  175. let timeRemindLab = UILabel(frame: CGRect(x: 0, y: stuckPointCuttingView.frame.minY - cDefaultMargin * 4 - cDefaultMargin * 3, width: view.frame.width, height: cDefaultMargin * 4))
  176. timeRemindLab.backgroundColor = UIColor.hexColor(hexadecimal: "#262626")
  177. timeRemindLab.textAlignment = .center
  178. timeRemindLab.font = UIFont.systemFont(ofSize: 12)
  179. timeRemindLab.textColor = UIColor.hexColor(hexadecimal: "#999999")
  180. let total: Float64 = ((stuckPointMusicData?.endTime ?? Float64(stuckPointMusicData?.duration ?? "0") ?? 0) - (stuckPointMusicData?.startTime ?? 0))
  181. timeRemindLab.text = "现卡点时长\(total.formatDurationToHMS()) / 原视频总时长\(selectedTotalDuration.formatDurationToHMS())"
  182. return timeRemindLab
  183. }()
  184. /// 音乐标题
  185. lazy var musicNameView: UIView = {
  186. let musicNameView = UIView()
  187. musicNameView.addSubview(musicNameLab)
  188. let nameWidth: CGFloat = musicNameLab.frame.width + (25 + cDefaultMargin * 3)
  189. musicNameView.frame = CGRect(x: (view.frame.width - nameWidth) / 2, y: cDevice_iPhoneStatusBarHei + (cDevice_iPhoneNavBarHei - cDefaultMargin * 3) / 2, width: nameWidth, height: cDefaultMargin * 3)
  190. // musicNameView.backgroundColor = UIColor.hexColor(hexadecimal: "#333333")
  191. musicNameView.addCorner(corner: musicNameView.frame.height / 2)
  192. let musicImageView = UIImageView()
  193. musicImageView.tintColor = PQBFConfig.shared.styleTitleColor
  194. musicImageView.image = UIImage.moduleImage(named: "stuckPoint_reCreate_music", moduleName: "BFFramework", isAssets: false)?.withRenderingMode(.alwaysTemplate)
  195. musicImageView.frame = CGRect(x: musicNameView.frame.height / 2 - 5, y: (musicNameView.frame.height - 22) / 2, width: 22, height: 22)
  196. musicNameView.addSubview(musicImageView)
  197. musicNameLab.frame.origin.x = musicImageView.frame.maxX + 5
  198. let ges = UITapGestureRecognizer(target: self, action: #selector(musicNameClick))
  199. musicNameView.addGestureRecognizer(ges)
  200. return musicNameView
  201. }()
  202. /// 音乐歌曲名称
  203. lazy var musicNameLab: LMJHorizontalScrollText = {
  204. let nameWidth: CGFloat = sizeWithText(text: "\(stuckPointMusicData?.musicName ?? "")", font: UIFont.systemFont(ofSize: 13), size: CGSize(width: view.frame.width - ((cDefaultMargin * 6 + 16 * 2) * 2) - (25 + cDefaultMargin * 3), height: cDefaultMargin * 3)).width
  205. let musicNameLab = LMJHorizontalScrollText(frame: CGRect(x: 0, y: 0, width: nameWidth < cDefaultMargin * 4 ? cDefaultMargin * 4 : nameWidth, height: cDefaultMargin * 3))
  206. musicNameLab.textColor = PQBFConfig.shared.styleTitleColor
  207. musicNameLab.textFont = UIFont.systemFont(ofSize: 13)
  208. musicNameLab.speed = 0.03
  209. musicNameLab.moveDirection = LMJTextScrollMoveLeft
  210. musicNameLab.moveMode = LMJTextScrollContinuous
  211. if nameWidth < cDefaultMargin * 4 {
  212. musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
  213. } else {
  214. musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
  215. }
  216. return musicNameLab
  217. }()
  218. /// 同步进度显示
  219. lazy var synchroMarskView: PQStuckPointLoadingView = {
  220. var synchroMarskView = PQStuckPointLoadingView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
  221. synchroMarskView.cancelHandle = { [weak self] _ in
  222. self?.navigationController?.popViewController(animated: true)
  223. }
  224. return synchroMarskView
  225. }()
  226. override func viewWillAppear(_ animated: Bool) {
  227. super.viewDidAppear(animated)
  228. lineView?.isHidden = true
  229. UIApplication.shared.isIdleTimerDisabled = true
  230. musicNameLab.move()
  231. PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
  232. }
  233. @objc func enterBackground() {
  234. BFLog(message: "进入到后台")
  235. // 取消导出
  236. playerView.pause()
  237. }
  238. override func backBtnClick() {
  239. super.backBtnClick()
  240. playerView.pause()
  241. // 点击上报:返回按钮
  242. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_back, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-(点击上报:返回按钮)")
  243. }
  244. @objc func musicNameClick() {
  245. // let musicVc = navigationController?.viewControllers.first(where: { vc in
  246. // vc is PQStuckPointMusicController
  247. // })
  248. // if musicVc != nil {
  249. // navigationController?.popToViewController(musicVc!, animated: true)
  250. // }
  251. }
  252. override func viewWillDisappear(_ animated: Bool) {
  253. super.viewWillDisappear(animated)
  254. UIApplication.shared.isIdleTimerDisabled = false
  255. musicNameLab.stop()
  256. playerView.pause()
  257. }
  258. override func viewDidLoad() {
  259. super.viewDidLoad()
  260. leftButton(image: UIImage(named: "icon_detail_back"), tintColor: PQBFConfig.shared.styleTitleColor)
  261. navHeadImageView?.addSubview(nextBtn)
  262. navHeadImageView?.addSubview(musicNameView)
  263. // 添加子视图
  264. addSubViews()
  265. // 导出相册视频
  266. exportPhotoData()
  267. // 同步音乐数据
  268. synchroMusicInfoData()
  269. // 曝光上报:预览页面曝光上报
  270. PQEventTrackViewModel.baseReportUpload(businessType: .bt_windowView, objectType: .ot_view_previewSyncedUp, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-(曝光上报:预览页面曝光上报)")
  271. }
  272. /// 添加子视图
  273. /// - Returns: <#description#>
  274. func addSubViews() {
  275. if (stuckPointMusicData?.rhythmSdata.count ?? 0) <= 0 {
  276. return
  277. }
  278. view.addSubview(playerView)
  279. view.addSubview(sustomSwitchView)
  280. view.addSubview(stuckPointCuttingView)
  281. // view.addSubview(timeRemindLab)
  282. // 添加一个背景区分不同色值
  283. let backView: UIView = UIView()
  284. backView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
  285. view.insertSubview(backView, aboveSubview: navHeadImageView!)
  286. backView.frame = CGRect(x: 0, y: navHeadImageView?.frame.height ?? 0, width: cScreenWidth, height: (stuckPointCuttingView.frame.minY - cDefaultMargin * 3) - (navHeadImageView?.frame.height ?? 0))
  287. }
  288. @objc func nextBtnClick(sender _: UIButton) {
  289. BFLog(message: "去发布")
  290. playerView.pause()
  291. // 使用深 copy
  292. let json = projectModel.toJSONString(prettyPrint: false)
  293. if json == nil {
  294. BFLog(message: "数据转换有问题 跳转")
  295. return
  296. }
  297. let tempModel: PQEditProjectModel? = Mapper<PQEditProjectModel>().map(JSONString: json!)
  298. let materialVC: PQStuckPointMaterialController? = navigationController?.viewControllers.first(where: { (vc) -> Bool in
  299. vc is PQStuckPointMaterialController
  300. }) as? PQStuckPointMaterialController
  301. if materialVC != nil, materialVC?.isToPublicHandle != nil {
  302. materialVC?.isToPublicHandle!(isReCreate, selectedTotalDuration, selectedDataCount, selectedImageDataCount, mStickers, stuckPointMusicData, tempModel)
  303. } else {
  304. let videoExporter = PQStuckPointPublicController()
  305. videoExporter.isReCreate = isReCreate
  306. videoExporter.selectedTotalDuration = selectedTotalDuration
  307. videoExporter.selectedDataCount = selectedDataCount
  308. videoExporter.selectedImageDataCount = selectedImageDataCount
  309. videoExporter.mStickers = mStickers
  310. videoExporter.audioMixModel = stuckPointMusicData
  311. videoExporter.editProjectModel = tempModel
  312. navigationController?.pushViewController(videoExporter, animated: true)
  313. }
  314. // 点击上报:去合成
  315. PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_commit, pageSource: .sp_stuck_previewSyncedUp, extParams: ["musicName": stuckPointMusicData?.musicName ?? "", "musicId": stuckPointMusicData?.musicId ?? "", "rhythmNumber": stuckPointMusicData?.speed ?? 2, "duration": ((stuckPointMusicData?.endTime ?? 0) - (stuckPointMusicData?.startTime ?? 0)) * 1000], remindmsg: "点击上报:去合成")
  316. }
  317. // MARK: - 播放器相关操作
  318. /// seek 播放器
  319. /// - Parameter playeTimeRange: 开始和结束时间
  320. func seekPlayer(playeTimeRange: CMTimeRange) {
  321. playerView.setEnableSeek(isSeek: true)
  322. playerView.play(pauseFirstFrame: false, playeTimeRange: playeTimeRange)
  323. }
  324. /// 通过传入的 selectedPhotoData 、 stuckPointMusicData 创建 projectModel 模型 后面都使用 projectModel 参数
  325. func createPorjectData() {
  326. // 1,添加选择的视觉素材
  327. let section: PQEditSectionModel = PQEditSectionModel()
  328. selectedMetarialData?.forEach { model in
  329. let json = model.toJSONString(prettyPrint: false)
  330. if json == nil {
  331. BFLog(message: "数据转换有问题 跳转")
  332. return
  333. }
  334. let tempModel: PQEditVisionTrackMaterialsModel = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: json!)!
  335. section.sectionTimeline?.visionTrack?.visionTrackMaterials.append(tempModel)
  336. }
  337. projectModel.sData?.sections.append(section)
  338. // 2,添加背景音乐
  339. projectModel.sData?.addBGM(audioMix: stuckPointMusicData!)
  340. }
  341. // 设置播放器
  342. func settingPlayerView() {
  343. // 1,设置播放器的显示区域 和画布大小
  344. // - 按第一个素材尺寸自适应
  345. let playerShowHeight = (stuckPointCuttingView.frame.minY - cDefaultMargin * 3) - (navHeadImageView?.frame.maxY ?? 0)
  346. var showRect: CGRect = PQPlayerViewModel.getShowCanvasRect(editProjectModel: projectModel, showType: 1, playerViewHeight: playerShowHeight)
  347. if showRect.size.width == showRect.size.height {
  348. if cScreenWidth > playerShowHeight {
  349. showRect.origin.x = (cScreenWidth - playerShowHeight) / 2
  350. showRect.size.width = playerShowHeight
  351. showRect.size.height = playerShowHeight
  352. } else {
  353. showRect.origin.x = 0
  354. showRect.size.width = cScreenWidth
  355. showRect.size.height = cScreenWidth
  356. }
  357. }
  358. showRect.origin.y = (playerShowHeight - showRect.size.height) / 2.0 + (navHeadImageView?.frame.maxY ?? 0)
  359. if showRect.size.width != 0, showRect.size.height != 0 {
  360. playerView.resetCanvasFrame(frame: showRect)
  361. }
  362. var firstModel: PQEditVisionTrackMaterialsModel?
  363. for part in projectModel.sData!.sections {
  364. if part.sectionTimeline?.visionTrack?.getEnableVisionTrackMaterials().count ?? 0 > 0 {
  365. firstModel = part.sectionTimeline?.visionTrack?.getEnableVisionTrackMaterials().first
  366. break
  367. }
  368. }
  369. var videoSize: CGSize = CGSize(width: Int(firstModel?.width ?? 0), height: Int(firstModel?.height ?? 0))
  370. var minSlider = min(videoSize.width, videoSize.height)
  371. var maxSlider = max(videoSize.width, videoSize.height)
  372. let ration = 1080 / minSlider
  373. minSlider = minSlider * ration
  374. maxSlider = maxSlider * ration
  375. if videoSize.width > videoSize.height { // 宽屏
  376. videoSize = CGSize(width: maxSlider, height: minSlider)
  377. } else {
  378. videoSize = CGSize(width: minSlider, height: maxSlider)
  379. }
  380. let maxValue = max(videoSize.width, videoSize.height ?? 0)
  381. if maxValue > 1920 {
  382. let maxRation = 1920 / maxValue
  383. videoSize = CGSize(width: videoSize.width * CGFloat(maxRation), height: videoSize.height * CGFloat(maxRation))
  384. BFLog(message: "最长边已经超过 1920 要等比缩小 缩放后\(videoSize)")
  385. }
  386. if (Int(videoSize.width) % 2) != 0 {
  387. videoSize.width = videoSize.width - 1
  388. }
  389. if (Int(videoSize.height) % 2) != 0 {
  390. videoSize.height = videoSize.height - 1
  391. }
  392. projectModel.sData?.videoMetaData?.videoWidth = Int(videoSize.width)
  393. projectModel.sData?.videoMetaData?.videoHeight = Int(videoSize.height)
  394. // 2,创建滤镜
  395. let beginTime: TimeInterval = Date().timeIntervalSince1970
  396. mStickers = createStickers(sections: projectModel.sData?.sections ?? List(), inputSize: CGSize(width: CGFloat(projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(projectModel.sData?.videoMetaData?.videoHeight ?? 0)), model: .createStickersModelPoint)
  397. playerView.mStickers = mStickers
  398. let end: TimeInterval = Date().timeIntervalSince1970
  399. BFLog(message: "createStickers tiskskskskme \(end - beginTime)")
  400. // 3,设置音频
  401. let audioPath = stuckPointMusicData?.localPath ?? ""
  402. BFLog(message: "初始化音频播放器的音频地址为:\(audioPath)")
  403. playerView.stop()
  404. // 这里的测试这个音乐播放有问题
  405. // self.playerView.updateAsset(URL(fileURLWithPath: "63930549652d74e477141e3b79c8d29a9ef8af81625053214516.mp3", relativeTo:Bundle.main.resourceURL!), videoComposition: nil, audioMixModel: nil)
  406. playerView.updateAsset(URL(fileURLWithPath: documensDirectory + audioPath), videoComposition: nil, audioMixModel: nil)
  407. let end2: TimeInterval = Date().timeIntervalSince1970
  408. BFLog(message: "updateAsset tiskskskskme \(end2 - end)")
  409. // 4, 设置播放器的输出画布大小
  410. playerView.movie?.mShowVidoSize = CGSize(width: CGFloat(projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(projectModel.sData?.videoMetaData?.videoHeight ?? 0))
  411. // 5,开始播放
  412. playerView.isLoop = false
  413. playerView.showProgressLab = true
  414. // 初始化音频的开始和结束时间
  415. BFLog(message: "播放的器 开始\(String(describing: CMTimeGetSeconds(playeTimeRange.start))) 结束 \(String(describing: CMTimeGetSeconds(playeTimeRange.end)))")
  416. let end3: TimeInterval = Date().timeIntervalSince1970
  417. playerView.play(pauseFirstFrame: false, playeTimeRange: CMTimeRange(start: playeTimeRange.start, end: playeTimeRange.end))
  418. let end4: TimeInterval = Date().timeIntervalSince1970
  419. BFLog(message: " playerView.play tiskskskskme \(end4 - end3)")
  420. // 6,进度回调
  421. playerView.progress = { [weak self] currentTime, tatolTime, _ in
  422. // 更新进度
  423. let progress = (currentTime - CMTimeGetSeconds(self?.playeTimeRange.start ?? .zero)) / CMTimeGetSeconds(self?.playeTimeRange.duration ?? .zero)
  424. BFLog(message: "\(currentTime) \(tatolTime) 显示播放器进度为: \(progress)")
  425. self?.stuckPointCuttingView.videoCropView.updateProgress(progress: CGFloat(progress))
  426. if self?.synchroMarskView.superview != nil {
  427. self?.synchroMarskView.removeMarskView()
  428. }
  429. }
  430. }
  431. deinit {
  432. BFLog(message: "卡点视频预览界面销毁")
  433. musicNameLab.stop()
  434. playerView.pause()
  435. // 取消所有的导出
  436. PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
  437. exportSession.cancelExport()
  438. }
  439. }
  440. }
  441. // MARK: - 视频渲染相关逻辑方法
  442. extension PQStuckPointEditerController {
  443. /// 分割视频 这里只设置视频类型的 in 和 out 并不设置显示的开始和结束时间 mp4 ,png ,png ,mp4
  444. /// - Parameter section: 当前段
  445. /// - Parameter stuckPoints: 用户选择的,或推荐的卡点数
  446. /// - Returns: 返回分割后的所有 stickers 和卡点数是一致的
  447. func clipVideoMerage(section: PQEditSectionModel, stuckPoints: [Float]) -> [PQEditVisionTrackMaterialsModel] {
  448. var stickers: Array = Array<PQEditVisionTrackMaterialsModel>.init()
  449. // 第二种情况:有视频要进行分割
  450. /*
  451. 1, 确定每个视频素材需要切的段数p
  452. 2, 将所有视频时长相加,得到总视频素材时长L = l1 + l2 + ... + ln
  453. 3, 视频素材a1需要切分的个数clipNum = max (round (kongduan * a1 / L) , 1)
  454. */
  455. // 要补的空位数
  456. let kongduan: Int = Int(stuckPoints.count) - Int(section.sectionTimeline!.visionTrack?.getEnableVisionTrackMaterials().count ?? 0)
  457. // 所有视频总时长
  458. var videoTotalDuration: Float64 = 0.0
  459. for video in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video") {
  460. let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + video.locationPath), options: nil)
  461. videoTotalDuration = videoTotalDuration + Float64(CMTimeGetSeconds(asset.duration))
  462. }
  463. if videoTotalDuration == 0 {
  464. BFLog(message: "视频总时长出现错误!!!!这里应该有视频素材的")
  465. return stickers
  466. }
  467. for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
  468. if sticker.type == StickerType.VIDEO.rawValue {
  469. let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
  470. // 要分割的段落
  471. let clipNum = Int(max(round(Double(kongduan) * CMTimeGetSeconds(asset.duration) / videoTotalDuration), 1))
  472. sticker.duration = CMTimeGetSeconds(asset.duration)
  473. BFLog(message: "单个视频\(sticker.locationPath)时长::\(CMTimeGetSeconds(asset.duration)) ,clipNum is:\(clipNum)")
  474. for clipindex in 0 ... clipNum - 1 {
  475. // deep copy sticker model 防止只有一个对象
  476. let stickerjson = sticker.toJSONString(prettyPrint: false)
  477. let deepCopySticker = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: stickerjson!)
  478. // 设置循环模式和适配模式
  479. deepCopySticker?.generateDefaultValues()
  480. deepCopySticker?.model_in = clipindex == 0 ? 0 : CMTimeGetSeconds(asset.duration) / Double(clipNum) * Double(clipindex)
  481. deepCopySticker?.out = (deepCopySticker?.model_in ?? 0) + CMTimeGetSeconds(asset.duration) / Double(clipNum)
  482. if (deepCopySticker?.model_in ?? 0) >= CMTimeGetSeconds(asset.duration) || (deepCopySticker?.out ?? 0) >= CMTimeGetSeconds(asset.duration) {
  483. deepCopySticker?.model_in = CMTimeGetSeconds(asset.duration) - CMTimeGetSeconds(asset.duration) / Double(clipNum)
  484. deepCopySticker?.out = CMTimeGetSeconds(asset.duration)
  485. }
  486. BFLog(message: " crilp is in \(deepCopySticker?.model_in ?? 0) out \(deepCopySticker?.out ?? 0) 总时长\(CMTimeGetSeconds(asset.duration))")
  487. if deepCopySticker != nil {
  488. stickers.append(deepCopySticker!)
  489. }
  490. }
  491. } else if sticker.type == StickerType.IMAGE.rawValue {
  492. sticker.generateDefaultValues()
  493. stickers.append(sticker)
  494. }
  495. }
  496. return stickers
  497. }
  498. /// 创建sticker
  499. /// - Parameters:
  500. /// - sections: 项目所有段落数据信息
  501. /// - inputSize: 画布大小
  502. /// - Returns: filters 数据 播放器可直接使用
  503. func createStickers(sections: List<PQEditSectionModel>, inputSize _: CGSize = .zero, model: createStickersModel = .createStickersModelPoint) -> [PQEditVisionTrackMaterialsModel] {
  504. // 保存滤镜对象数据
  505. var stickers: Array = Array<PQEditVisionTrackMaterialsModel>.init()
  506. if model == .createStickersModelPoint {
  507. for section in sections {
  508. if section.sectionType == "normal" {
  509. // 推荐卡点数
  510. var stuckPoints: Array = Array<Float>.init()
  511. var stuckPointsTemp = Array<Float>.init()
  512. for (index, dunshu) in stuckPointMusicData!.rhythmSdata[0].pointTimes.enumerated() {
  513. BFLog(message: "原所有卡点数:\(index) \(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))")
  514. if Float64(dunshu) / Float64(BASE_FILTER_TIMESCALE) > CMTimeGetSeconds(playeTimeRange.start), Float64(dunshu) / Float64(BASE_FILTER_TIMESCALE) < CMTimeGetSeconds(playeTimeRange.end) {
  515. stuckPointsTemp.append(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))
  516. }
  517. }
  518. /* 快慢速模式下取卡点
  519. - 快节奏为选中区域的所有点位,即0,1,2,3,4 5 6 7 8 9 10 ……
  520. - 适中为每两个点位取一个,即0,3,6,9 12
  521. - 慢节奏为每三个点位取一个,即0 5 10 15
  522. 慢节奏要做特殊处理
  523. 5d or L/1.23
  524. (*当输入素材为L ∈(0-10.5]s 时,判断与5d之间的关系,若L/1.2≥5d,则取5d;若L/1.2<5d,则取L/1.2)
  525. */
  526. // 跳跃卡点模式下根据不同速度 取卡点 1,2,3
  527. /*
  528. - 快节奏为选中区域的所有点位,即0,1,2,3,4……
  529. - 适中为每两个点位取一个,即0,2,4,6……
  530. - 慢节奏为每三个点位取一个,即0,3,6,9……
  531. */
  532. BFLog(message: "stuckPointMusicData?.speed is \(String(describing: stuckPointMusicData?.speed))")
  533. for (index, point) in stuckPointsTemp.enumerated() {
  534. if stuckPointMusicData?.speed == 1 {
  535. stuckPoints.append(Float(point))
  536. } else if stuckPointMusicData?.speed == 2 {
  537. if index % 2 == 0 {
  538. stuckPoints.append(point)
  539. }
  540. } else if stuckPointMusicData?.speed == 3 {
  541. if index % 3 == 0 {
  542. stuckPoints.append(point)
  543. }
  544. }
  545. }
  546. for point in stuckPoints {
  547. BFLog(message: "没有 start end 计算后的卡点数\(point)")
  548. }
  549. if stuckPoints.first != nil {
  550. stuckPoints.removeFirst()
  551. }
  552. if stuckPoints.last != nil {
  553. stuckPoints.removeLast()
  554. }
  555. // 开始时间是服务器返回, 结果时间根据策略计算的
  556. stuckPoints.insert(Float(CMTimeGetSeconds(playeTimeRange.start)), at: 0)
  557. stuckPoints.insert(Float(CMTimeGetSeconds(playeTimeRange.end)), at: stuckPoints.count)
  558. for point in stuckPoints {
  559. BFLog(message: "有 start end 计算后的卡点数\(point)")
  560. }
  561. BFLog(message: "stuckPoints count is \(stuckPoints.count)")
  562. // 当用户上传视觉素材个数大于等于音乐选择区域节拍分割个数时,无需进行视频分割,只显示卡点数-1 个素材
  563. if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count >= stuckPointMusicData!.rhythmSdata[0].pointTimes.count {
  564. for (index, sticker) in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().enumerated() {
  565. if index == stuckPointMusicData!.rhythmSdata[0].pointTimes.count {
  566. BFLog(message: "到达卡点数量")
  567. break
  568. }
  569. BFLog(message: "创建 filter start :\(sticker.timelineIn) end :\(sticker.timelineOut) type is \(sticker.type) \(sticker.locationPath)")
  570. sticker.timelineIn = Float64("\(stuckPoints[index])") ?? 0.0
  571. sticker.timelineOut = Float64("\(stuckPoints[index + 1])") ?? 0.0
  572. BFLog(message: "卡点 间隔 \(sticker.timelineIn - sticker.timelineOut)")
  573. sticker.generateDefaultValues()
  574. stickers.append(sticker)
  575. }
  576. } else {
  577. // 卡点数 > 选择素材数
  578. // 第一种情况:全是图片,图片回环播放
  579. if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video").count == 0, section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "image").count > 0 {
  580. for (index, point) in stuckPoints.enumerated() {
  581. let sticker: PQEditVisionTrackMaterialsModel = section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials()[index % section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count]
  582. BFLog(message: "stickerlocationPath sticker : \(sticker.locationPath)")
  583. let stickerjson = sticker.toJSONString(prettyPrint: false)
  584. let deepCopySticker = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: stickerjson!)
  585. if deepCopySticker!.type == StickerType.IMAGE.rawValue {
  586. if index + 1 < stuckPoints.count {
  587. deepCopySticker!.timelineIn = Float64("\(stuckPoints[index])") ?? 0.0
  588. deepCopySticker!.timelineOut = Float64("\(stuckPoints[index + 1])") ?? 0.0
  589. if deepCopySticker != nil {
  590. deepCopySticker?.generateDefaultValues()
  591. stickers.append(deepCopySticker!)
  592. }
  593. }
  594. }
  595. }
  596. } else {
  597. // 第二种情况:有视频要进行分割
  598. let clipFilters = clipVideoMerage(section: section, stuckPoints: stuckPoints)
  599. for (index, point) in stuckPoints.enumerated() {
  600. BFLog(message: "aaaaaindexindeindexxindexindexindex \(index) \(point)")
  601. if index + 1 < stuckPoints.count, index < clipFilters.count {
  602. BFLog(message: "bbbbbindexindeindexxindexindexindex \(index) \(point)")
  603. let sticker: PQEditVisionTrackMaterialsModel = clipFilters[index]
  604. sticker.timelineIn = Float64("\(stuckPoints[index])") ?? 0.0
  605. // TODO: 不是最好方案
  606. sticker.timelineOut = Float64("\(stuckPoints[index + 1])") ?? 0.0
  607. // 卡点的时间 > in out 值 这里就会出现鬼畜效果
  608. let timelineInterval = sticker.timelineOut - sticker.timelineIn
  609. let inOutInterval = sticker.out - sticker.model_in
  610. if timelineInterval > inOutInterval {
  611. BFLog(message: "实际要显示卡点时长\(timelineInterval) 素材裁剪时长:\(inOutInterval)")
  612. sticker.out = sticker.model_in + timelineInterval
  613. // 下面只是 LOG 方便查问题
  614. let stickerInOut = sticker.out - sticker.model_in
  615. let stickerTimelineInOut = sticker.timelineOut - sticker.timelineIn
  616. if stickerInOut != stickerTimelineInOut {
  617. BFLog(message: "sticker.timelineIn \(sticker.timelineIn) stickerTimelineInOut is\(stickerTimelineInOut) stickerInOut is\(stickerInOut) 相差\(stickerTimelineInOut - stickerInOut)")
  618. }
  619. }
  620. // out > 素材的总时长in out 进行前移操作
  621. let offsetAssetDuration = sticker.out - sticker.duration
  622. if offsetAssetDuration > 0 {
  623. sticker.model_in = sticker.model_in - offsetAssetDuration
  624. sticker.out = sticker.out - offsetAssetDuration
  625. }
  626. BFLog(message: "index is \(index)分割后 创建 filter timelineIn :\(sticker.timelineIn) timelineOut :\(sticker.timelineOut) in :\(sticker.model_in) out:\(sticker.out) type is \(sticker.type) 显示总时长为:\(sticker.timelineOut - sticker.timelineIn) 裁剪总时长\(sticker.out - sticker.model_in)")
  627. stickers.append(sticker)
  628. }
  629. }
  630. }
  631. }
  632. }
  633. }
  634. } else {
  635. for section in sections {
  636. if section.sectionType == "normal" {
  637. // 推荐卡点数
  638. var stuckPoints: Array = Array<Float>.init()
  639. var stuckPointsTemp = Array<Float>.init()
  640. for (index, dunshu) in stuckPointMusicData!.rhythmSdata[0].pointTimes.enumerated() {
  641. BFLog(message: "原所有卡点数:\(index) \(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))")
  642. if Float64(dunshu) / Float64(BASE_FILTER_TIMESCALE) > CMTimeGetSeconds(playeTimeRange.start), Float64(dunshu) / Float64(BASE_FILTER_TIMESCALE) < CMTimeGetSeconds(playeTimeRange.end) {
  643. stuckPointsTemp.append(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))
  644. }
  645. }
  646. // 根据不同速度 取卡点 1,2,3
  647. /*
  648. - 快节奏为选中区域的所有点位,即0,1,2,3,4……
  649. - 适中为每两个点位取一个,即0,2,4,6……
  650. - 慢节奏为每三个点位取一个,即0,3,6,9……
  651. */
  652. BFLog(message: "stuckPointMusicData?.speed is \(String(describing: stuckPointMusicData?.speed))")
  653. for (index, point) in stuckPointsTemp.enumerated() {
  654. if stuckPointMusicData?.speed == 1 {
  655. stuckPoints.append(Float(point))
  656. } else if stuckPointMusicData?.speed == 2 {
  657. if index % 2 == 0 {
  658. stuckPoints.append(point)
  659. }
  660. } else if stuckPointMusicData?.speed == 3 {
  661. if index % 3 == 0 {
  662. stuckPoints.append(point)
  663. }
  664. }
  665. }
  666. for point in stuckPoints {
  667. BFLog(message: "没有 start end 计算后的卡点数\(point)")
  668. }
  669. if stuckPoints.first != nil {
  670. stuckPoints.removeFirst()
  671. }
  672. if stuckPoints.last != nil {
  673. stuckPoints.removeLast()
  674. }
  675. // 开始时间是服务器返回, 结果时间根据策略计算的
  676. stuckPoints.insert(Float(CMTimeGetSeconds(playeTimeRange.start)), at: 0)
  677. stuckPoints.insert(Float(CMTimeGetSeconds(playeTimeRange.end)), at: stuckPoints.count)
  678. for point in stuckPoints {
  679. BFLog(message: "有 start end 计算后的卡点数\(point)")
  680. }
  681. BFLog(message: "stuckPoints count is \(stuckPoints.count)")
  682. // 当用户上传视觉素材个数大于等于音乐选择区域节拍分割个数时,无需进行视频分割,只显示卡点数-1 个素材
  683. if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count >= stuckPointMusicData!.rhythmSdata[0].pointTimes.count {
  684. for (index, sticker) in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().enumerated() {
  685. if index == stuckPointMusicData!.rhythmSdata[0].pointTimes.count {
  686. BFLog(message: "到达卡点数量")
  687. break
  688. }
  689. BFLog(message: "创建 filter start :\(sticker.timelineIn) end :\(sticker.timelineOut) type is \(sticker.type) \(sticker.locationPath)")
  690. sticker.timelineIn = Float64("\(stuckPoints[index])") ?? 0.0
  691. sticker.timelineOut = Float64("\(stuckPoints[index + 1])") ?? 0.0
  692. BFLog(message: "卡点 间隔 \(sticker.timelineIn - sticker.timelineOut)")
  693. sticker.generateDefaultValues()
  694. stickers.append(sticker)
  695. }
  696. } else {
  697. // 卡点数 > 选择素材数
  698. // 第一种情况:全是图片,图片回环播放
  699. if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video").count == 0, section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "image").count > 0 {
  700. for (index, point) in stuckPoints.enumerated() {
  701. let sticker: PQEditVisionTrackMaterialsModel = section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials()[index % section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count]
  702. BFLog(message: "stickerlocationPath sticker : \(sticker.locationPath)")
  703. let stickerjson = sticker.toJSONString(prettyPrint: false)
  704. let deepCopySticker = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: stickerjson!)
  705. if deepCopySticker!.type == StickerType.IMAGE.rawValue {
  706. if index + 1 < stuckPoints.count {
  707. deepCopySticker!.timelineIn = Float64("\(stuckPoints[index])") ?? 0.0
  708. deepCopySticker!.timelineOut = Float64("\(stuckPoints[index + 1])") ?? 0.0
  709. if deepCopySticker != nil {
  710. deepCopySticker?.generateDefaultValues()
  711. stickers.append(deepCopySticker!)
  712. }
  713. }
  714. }
  715. }
  716. } else {
  717. // 第二种情况:有视频要进行分割
  718. let clipFilters = clipVideoMerage(section: section, stuckPoints: stuckPoints)
  719. for (index, point) in stuckPoints.enumerated() {
  720. BFLog(message: "aaaaaindexindeindexxindexindexindex \(index) \(point)")
  721. if index + 1 < stuckPoints.count, index < clipFilters.count {
  722. BFLog(message: "bbbbbindexindeindexxindexindexindex \(index) \(point)")
  723. let sticker: PQEditVisionTrackMaterialsModel = clipFilters[index]
  724. let spit: Int = 2
  725. sticker.timelineIn = 57.5 + Double(index * spit)
  726. sticker.timelineOut = sticker.timelineIn + Double(spit)
  727. sticker.speedRate = index % 2 == 0 ? 0.1 : 2
  728. sticker.model_in = Double(index * spit)
  729. sticker.out = sticker.model_in + Double(spit)
  730. // let spit: Int = 2
  731. // sticker.timelineIn = 57.5
  732. // sticker.timelineOut = sticker.timelineIn + 24
  733. // sticker.speedRate = index % 2 == 0 ? 0.1 : 2
  734. //// sticker.speedRate = 0.1
  735. // sticker.model_in = 0
  736. // sticker.out = sticker.model_in + 24
  737. if stickers.count < 12 {
  738. BFLog(message: "index is \(index)分割后 创建 filter timelineIn :\(sticker.timelineIn) timelineOut :\(sticker.timelineOut) in :\(sticker.model_in) out:\(sticker.out) type is \(sticker.type) 显示总时长为:\(sticker.timelineOut - sticker.timelineIn) 裁剪总时长\(sticker.out - sticker.model_in)")
  739. stickers.append(sticker)
  740. }
  741. }
  742. }
  743. }
  744. }
  745. }
  746. }
  747. }
  748. return stickers
  749. }
  750. }
  751. // MARK: - 同步/下载素材相关
  752. /// 同步/下载素材相关
  753. extension PQStuckPointEditerController {
  754. /// 同步音乐相关数据
  755. /// - Returns: <#description#>
  756. func synchroMusicInfoData() {
  757. if (stuckPointMusicData?.rhythmSdata.count ?? 0) <= 0 {
  758. if synchroMarskView.superview == nil {
  759. UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
  760. }
  761. PQStuckPointViewModel.stuckPointMusicDetailData(musicId: stuckPointMusicData?.musicId ?? "", originType: stuckPointMusicData?.originType ?? 1) { [weak self] newMusicData, _ in
  762. if newMusicData != nil, (newMusicData?.rhythmSdata.count ?? 0) > 0 {
  763. self?.isStuckPointDataSuccess = true
  764. self?.stuckPointMusicData?.rhythmSdata = newMusicData?.rhythmSdata ?? []
  765. self?.stuckPointMusicData?.startTime = newMusicData?.startTime ?? 0
  766. self?.stuckPointMusicData?.endTime = newMusicData?.endTime ?? 0
  767. if newMusicData?.speed != nil {
  768. self?.stuckPointMusicData?.speed = newMusicData?.speed ?? 2
  769. }
  770. if (self?.stuckPointMusicData?.rhythmSdata.count ?? 0) > 0 && (((self?.selectedDataCount ?? 0) - (self?.selectedImageDataCount ?? 0)) > 0 || (self?.selectedImageDataCount ?? 0) > 0 || (self?.selectedTotalDuration ?? 0) > 0) {
  771. self?.stuckPointMusicData?.endTime = (self?.stuckPointMusicData?.startTime ?? 0) + (self?.stuckPointMusicData?.stuckPointCuttingTime(videoCount: (self?.selectedDataCount ?? 0) - (self?.selectedImageDataCount ?? 0), imageCount: self?.selectedImageDataCount ?? 0, totalDuration: self?.selectedTotalDuration ?? 0) ?? 0)
  772. }
  773. self?.stuckPointCuttingView.updateEndTime(startTime: CGFloat(self?.stuckPointMusicData?.startTime ?? 0), endTime: CGFloat(self?.stuckPointMusicData?.endTime ?? 0))
  774. if self?.stuckPointMusicData?.localPath == nil || (self?.stuckPointMusicData?.localPath?.count ?? 0) > 0 {
  775. PQDownloadManager.downLoadFile(url: self?.stuckPointMusicData?.musicPath ?? "") { [weak self] filePath, error in
  776. if error == nil, filePath != nil {
  777. self?.isSynchroMusicInfoSuccess = true
  778. self?.stuckPointMusicData?.localPath = filePath?.replacingOccurrences(of: documensDirectory, with: "")
  779. // 处理所有数据完成
  780. self?.dealWithDataSuccess()
  781. } else {
  782. if self?.synchroMarskView.superview != nil {
  783. self?.synchroMarskView.removeMarskView()
  784. }
  785. // PQUploadRemindView.showUploadRemindView(title: nil, attributedTitle: NSAttributedString(string: "加载音乐失败,请重新选择音乐"), summary: "", confirmTitle: nil) { [weak self] _, _ in
  786. // self?.navigationController?.popViewController(animated: true)
  787. // }
  788. }
  789. }
  790. } else {
  791. self?.isSynchroMusicInfoSuccess = true
  792. // 处理所有数据完成
  793. self?.dealWithDataSuccess()
  794. }
  795. // 添加子视图
  796. self?.addSubViews()
  797. } else {
  798. if self?.synchroMarskView.superview != nil {
  799. self?.synchroMarskView.removeMarskView()
  800. }
  801. // PQUploadRemindView.showUploadRemindView(title: nil, attributedTitle: NSAttributedString(string: "加载音乐失败,请重新选择音乐"), summary: "", confirmTitle: nil) { [weak self] _, _ in
  802. // self?.navigationController?.popViewController(animated: true)
  803. // }
  804. }
  805. }
  806. } else if stuckPointMusicData?.localPath == nil || (stuckPointMusicData?.localPath?.count ?? 0) > 0 {
  807. if synchroMarskView.superview == nil {
  808. UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
  809. }
  810. isStuckPointDataSuccess = true
  811. PQDownloadManager.downLoadFile(url: stuckPointMusicData?.musicPath ?? "") { [weak self] filePath, error in
  812. if error == nil, filePath != nil {
  813. self?.isSynchroMusicInfoSuccess = true
  814. self?.stuckPointMusicData?.localPath = filePath?.replacingOccurrences(of: documensDirectory, with: "")
  815. // 处理所有数据完成
  816. self?.dealWithDataSuccess()
  817. } else {
  818. if self?.synchroMarskView.superview != nil {
  819. self?.synchroMarskView.removeMarskView()
  820. }
  821. // PQUploadRemindView.showUploadRemindView(title: nil, attributedTitle: NSAttributedString(string: "加载音乐失败,请重新选择音乐"), summary: "", confirmTitle: nil) { [weak self] _, _ in
  822. // self?.navigationController?.popViewController(animated: true)
  823. // }
  824. }
  825. }
  826. } else {
  827. isStuckPointDataSuccess = true
  828. // 处理所有数据完成
  829. dealWithDataSuccess()
  830. }
  831. }
  832. /// 导出相册数据
  833. /// - Returns: <#description#>
  834. func exportPhotoData() {
  835. // 取消所有的导出
  836. PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
  837. exportSession.cancelExport()
  838. }
  839. var isHaveVideo: Bool = false
  840. if selectedMetarialData != nil, (selectedMetarialData?.count ?? 0) > 0 {
  841. if synchroMarskView.superview == nil {
  842. UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
  843. }
  844. let dispatchGroup = DispatchGroup()
  845. for photo in selectedMetarialData! {
  846. if photo.asset != nil, photo.asset?.mediaType == .video {
  847. if !isHaveVideo {
  848. isHaveVideo = true
  849. }
  850. dispatchGroup.enter()
  851. PQPHAssetVideoParaseUtil.parasToAVAsset(phAsset: photo.asset!) { avAsset, _, _, _ in
  852. if avAsset is AVURLAsset {
  853. // 创建目录
  854. let fileName = (avAsset as! AVURLAsset).url.absoluteString
  855. BFLog(message: "video fileName is\(fileName)")
  856. let tempPhoto = self.selectedMetarialData?.first(where: { material in
  857. material.asset == photo.asset
  858. })
  859. if fileName.count > 0 {
  860. createDirectory(path: photoLibraryDirectory)
  861. let outFilePath = photoLibraryDirectory + fileName.md5 + ".mp4"
  862. // 文件存在先删除老文件
  863. if FileManager.default.fileExists(atPath: outFilePath) {
  864. do {
  865. try FileManager.default.removeItem(at: NSURL.fileURL(withPath: outFilePath))
  866. } catch {
  867. BFLog(message: "导出相册视频-error == \(error)")
  868. }
  869. }
  870. do {
  871. try FileManager.default.copyItem(atPath: fileName.replacingOccurrences(of: "file:///", with: ""), toPath: outFilePath)
  872. print("Success to copy file.")
  873. } catch {
  874. print("Failed to copy file.")
  875. }
  876. tempPhoto?.locationPath = outFilePath.replacingOccurrences(of: documensDirectory, with: "")
  877. BFLog(message: "导出视频相册地址为 \(String(describing: tempPhoto?.locationPath))")
  878. dispatchGroup.leave()
  879. }
  880. }
  881. }
  882. }
  883. }
  884. dispatchGroup.notify(queue: DispatchQueue.main) { [weak self] in
  885. self?.isExportVideosSuccess = true
  886. BFLog(message: "所有相册视频导出成功")
  887. // 处理所有数据完成
  888. if isHaveVideo {
  889. self?.dealWithDataSuccess()
  890. }
  891. }
  892. // 只有图片
  893. if !isHaveVideo {
  894. isExportVideosSuccess = true
  895. BFLog(message: "所有相册视频导出成功")
  896. dealWithDataSuccess()
  897. }
  898. }
  899. }
  900. /// 处理所有数据完成
  901. /// - Returns: <#description#>
  902. func dealWithDataSuccess() {
  903. BFLog(message: "三组参数:\(isSynchroMusicInfoSuccess) \(isStuckPointDataSuccess) \(isExportVideosSuccess)")
  904. if !isSynchroMusicInfoSuccess || !isStuckPointDataSuccess || !isExportVideosSuccess {
  905. return
  906. }
  907. playeTimeRange = CMTimeRange(start: CMTimeMakeWithSeconds(stuckPointMusicData?.startTime ?? 0, preferredTimescale: BASE_FILTER_TIMESCALE), end: CMTimeMakeWithSeconds(stuckPointMusicData?.endTime ?? 0, preferredTimescale: BASE_FILTER_TIMESCALE))
  908. createPorjectData()
  909. settingPlayerView()
  910. }
  911. }