1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771 |
- //
- // PQStuckPointEditerController.swift
- // PQSpeed
- //
- // Created by ak on 2021/4/26.
- // Copyright © 2021 BytesFlow. All rights reserved.
- // 功能:卡点音乐编辑界面
- // 创建不同玩法的类型 (1:跳跃卡点,2:快慢速,3:仅配乐)
- public enum createStickersModel: Int{
- case createStickersModelPoint = 1 // 跳跃卡点
- case createStickersModelSpeed = 2 // 快慢速
- case createStickersModelOnlyMusic = 3// 仅配乐
- }
- import Foundation
- import ObjectMapper
- import RealmSwift
- import UIKit
- class PQStuckPointEditerController: PQBaseViewController {
- // 是否导出视频成功
- var isExportVideosSuccess: Bool = false
- // 是否请求卡点数据成功
- var isStuckPointDataSuccess: Bool = false
- // 是否同步音乐成功
- var isSynchroMusicInfoSuccess: Bool = false
- /// 当前所有的filter
- var filters: Array = Array<ImageProcessingOperation>.init()
- // 选中所有素材的的总时长 已经不包括图片的时长
- var selectedTotalDuration: Float64 = 0
- // 选择的总数
- var selectedDataCount: Int = 0
- // 选择的图片总数
- var selectedImageDataCount: Int = 0
- // 选中的素材数据
- var selectedPhotoData: [PQEditVisionTrackMaterialsModel]?
- // 选中的音乐数据
- var stuckPointMusicData: PQVoiceModel?
- // 保存所有段的所有贴纸,音频信息,用于播放器的渲染使用
- var projectModel: PQEditProjectModel = PQEditProjectModel()
- // 从草稿箱进入的项目数据
- var draftProjectModel: PQEditProjectModel?
- var mStickers: [PQEditVisionTrackMaterialsModel]?
- // 播放器的开始和结束时间,1,刚进界面使用推荐的开始结束时间,2,用户修改起结点时修改
- var playeTimeRange: CMTimeRange = CMTimeRange()
- // add by ak 是否是再创作模式
- var isReCreate: Bool = false
- // 最后一个选择的模式 BTN 用于还原选中状态
- var lastEditModelBtn: UIButton?
-
- // add by ak 最大、最小速度 有固定值和自定义,当快慢速下两个值都有效,当跳跃卡点只有maxSpeed有效
- var maxSpeed: Float = 1
- var minSpeed: Float = 1
-
- //快慢速最后一次选择的速度位置
- var lastSpeedSelectIndex:Int = 0
- //跳跃卡点最后一次选择的速度位置
- var lastJumpSpeedSelectIndex:Int = 0
- //循环次数设置最后一次选择的位置
- var lastCyclesSelectIndex:Int = 0
-
- //当前选择的玩法模式
- var currentCreateStickersModel:createStickersModel = .createStickersModelSpeed
-
- //最终使用的卡点数据
- var finallyStuckPoints: Array = Array<Float>.init()
- //最终使用的音频时长
- var finallyUserAudioTime:Float = 0.0
- // 下一步
- lazy var nextBtn: UIButton = {
- let nextBtn = UIButton(type: .custom)
- nextBtn.frame = CGRect(x: cScreenWidth - 16 - cDefaultMargin * 6, y: cDevice_iPhoneStatusBarHei + (cDevice_iPhoneNavBarHei - cDefaultMargin * 3) / 2, width: cDefaultMargin * 6, height: cDefaultMargin * 3)
- nextBtn.setTitle("合成", for: .normal)
- nextBtn.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .medium)
- nextBtn.addTarget(self, action: #selector(nextBtnClick(sender:)), for: .touchUpInside)
- nextBtn.backgroundColor = UIColor.hexColor(hexadecimal: PQBFConfig.shared.styleColor.rawValue)
- nextBtn.setTitleColor(UIColor.hexColor(hexadecimal: "#FFFFFF"), for: .normal)
- nextBtn.uxy_acceptEventInterval = 0.5
- nextBtn.addCorner(corner: 3)
- return nextBtn
- }()
- // 播放器显示 view
- lazy var playerView: PQGPUImagePlayerView = {
- let playerHeight = cScreenWidth
- let playerView = PQGPUImagePlayerView(frame: CGRect(x: 0, y: navHeadImageView?.frame.maxY ?? 0, width: playerHeight, height: playerHeight))
- playerView.backgroundColor = PQBFConfig.shared.styleBackGroundColor
- playerView.isShowLine = false
- playerView.showGaussianBlur = true
- playerView.renderViewOnClickHandle = {[weak self] in
- self?.musicEditBGView.pausePlayer()
- }
- playerView.playerEmptyView.isHidden = true
- return playerView
- }()
- /// 节奏选择视图
- lazy var sustomSwitchView: PQCustomSwitchView = {
- let sustomSwitchView = PQCustomSwitchView(frame: CGRect(x: 16, y: 0, width: 180, height: 30), titles: ["快节奏", "适中", "慢节奏"], defaultIndex: stuckPointMusicData?.speed ?? 2)
- sustomSwitchView.switchChangeHandle = { [weak self] sender in
- // 改变速率
- if(self?.currentCreateStickersModel == .createStickersModelSpeed){
- self?.stuckPointMusicData?.speed = sender.tag + 1
- }else{
- self?.stuckPointMusicData?.speed = sender.tag
- }
-
- self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.bgmInfo?.rhythmMusicSpeed = sender.tag
- // 播放前先暂停
- // self?.playerView.stop()
- // 开始播放
- self?.settingPlayerView()
- // 点击上报:选择节奏
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_selectRhythm, pageSource: .sp_stuck_previewSyncedUp, extParams: ["rhythmNumber": sender.tag], remindmsg: "点击上报:选择节奏")
- }
- return sustomSwitchView
- }()
- /// 裁剪视图
- lazy var stuckPointCuttingView: PQStuckPointCuttingView = {
-
- let stuckPointCuttingView = PQStuckPointCuttingView(frame: CGRect(x: 0, y: optionlineView.frame.minY - 85 - 28, width: view.frame.width, height: 80), duration: CGFloat(Float(stuckPointMusicData?.duration ?? "0") ?? 0), startTime: CGFloat(stuckPointMusicData?.startTime ?? 0), endTime: CGFloat(stuckPointMusicData?.endTime ?? 0))
- /// 裁剪进度回调
- stuckPointCuttingView.videoRangeDidChanged = { [weak self] startTime, endTime in
- BFLog(message: "裁剪返回--startTime = \(startTime),endTime = \(endTime)")
- }
- /// 播放进度回调
- stuckPointCuttingView.videoProgressDidChanged = { [weak self] progress in
- BFLog(message: "进度更新返回--progress = \(progress) \(String(describing: self?.playerView.mPlayeTimeRange))")
- }
- /// 拖缀结束的回调 type - 1-拖动左边裁剪结束 2--拖动右边裁剪结束 3-进度条拖动结束 4-滑动结束
- stuckPointCuttingView.videoDidEndDragging = { [weak self] type, startTime, endTime, progress in
- BFLog(message: "拖拽结束返回--type = \(type),startTime = \(startTime),endTime = \(endTime),progress = \(progress)")
- self?.playerView.pause()
- // 修改最新值
- self?.stuckPointMusicData?.startTime = Float64(startTime)
- self?.stuckPointMusicData?.endTime = Float64(endTime)
- // 红的指针完成
- if type == 3 {
- if CMTimeGetSeconds(self?.playerView.mPlayeTimeRange?.end ?? .zero) == 0 {
- BFLog(message: "mPlayeTimeRange is error")
- return
- }
- let newBeginSconds = (Double(startTime) + (Double(endTime) - Double(startTime)) * Double(progress)) * 600
- BFLog(message: " newBeginSconds is \(newBeginSconds)")
- let seekTimeRange: CMTimeRange = CMTimeRange(start: CMTime(value: CMTimeValue(Int64(newBeginSconds)), timescale: 600), end:
- CMTime(value: CMTimeValue(Int64(endTime * 600)), timescale: 600))
- BFLog(message: "修改的开始 \(CMTimeGetSeconds(seekTimeRange.start)) 结束 \(CMTimeGetSeconds(seekTimeRange.end))")
- // 重新设置有效缓存
- self?.playerView.configCache(beginTime: CMTimeGetSeconds(seekTimeRange.start))
- self?.playerView.play(pauseFirstFrame: false, playeTimeRange: seekTimeRange)
- } else {
- // 更改素材开始时间及结束时间
- self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.out = Float64(endTime)
- self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.model_in = Float64(startTime)
- self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.timelineIn = Float64(startTime)
- self?.projectModel.sData?.getBGMSession()?.sectionTimeline?.audioTrack?.audioTrackMaterials.first?.timelineOut = Float64(endTime)
- BFLog(message: "调整后总时长: \(endTime - startTime) startTime:\(startTime) endTime:\(endTime)")
- // 初始化音频的开始和结束时间
- self?.playeTimeRange = CMTimeRange(start: CMTimeMakeWithSeconds(Float64(startTime), preferredTimescale: BASE_FILTER_TIMESCALE), end: CMTimeMakeWithSeconds(Float64(endTime), preferredTimescale: BASE_FILTER_TIMESCALE))
- // DispatchQueue.global().async { // 并行、异步
- let beginTime: TimeInterval = Date().timeIntervalSince1970
- 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)))
- self?.playerView.mStickers = self?.mStickers
- DispatchQueue.main.async { // 串行、异步
- var endTime: TimeInterval = Date().timeIntervalSince1970
- BFLog(message: "endTime is endTimeendTime \(endTime = beginTime)")
- self?.playerView.play(pauseFirstFrame: false, playeTimeRange: self!.playeTimeRange)
- }
- // }
- }
-
- }
- return stuckPointCuttingView
- }()
- /// 卡点模式标题
- lazy var pointEditRemindLab: UILabel = {
- let pointEditRemindLab = UILabel()
- pointEditRemindLab.backgroundColor = .clear
- pointEditRemindLab.textAlignment = .left
- pointEditRemindLab.font = UIFont.boldSystemFont(ofSize: 14)
- pointEditRemindLab.textColor = .black
- pointEditRemindLab.text = "卡点模式"
- return pointEditRemindLab
- }()
- /// 卡点模式标题
- lazy var speedTitleLab: UILabel = {
- let speedTitleLab = UILabel()
- speedTitleLab.backgroundColor = .clear
- speedTitleLab.textAlignment = .left
- speedTitleLab.font = UIFont.boldSystemFont(ofSize: 14)
- speedTitleLab.textColor = .black
- speedTitleLab.text = "节奏变化"
- return speedTitleLab
- }()
- /// 卡点模式下方操作区背景
- lazy var pointEditBGView: UIView = {
- let pointEditBGView = UIView()
- pointEditBGView.backgroundColor = .clear
- return pointEditBGView
- }()
-
- /// 下方音乐编辑操作区背景
- lazy var musicEditBGView: PQSelecteMusicView = {
- let musicEditBGView = PQSelecteMusicView.init()
- musicEditBGView.backgroundColor = .clear
- musicEditBGView.isUserInteractionEnabled = true
- musicEditBGView.isHidden = true
- musicEditBGView.musicSearchBtn.addTarget(self, action: #selector(musicSearchBtnClick(sender:)), for: .touchUpInside)
-
- musicEditBGView.didSelectItemHandle = { [weak self] isplaying in
- self?.playerView.pause()
- }
- musicEditBGView.btnClickHandle = { [weak self] _, bgmData in
- // 使用音乐
- self?.userstuckPointMusic(musicData: bgmData as? PQVoiceModel)
- }
-
- return musicEditBGView
- }()
-
-
- // 卡点编辑 btn
- lazy var pointEditerBtn: UIButton = {
- let pointEdterBtn = UIButton(type: .custom)
- pointEdterBtn.setImage(UIImage().BF_Image(named: "pointEditerBtn_n"), for: .normal)
- pointEdterBtn.setImage(UIImage().BF_Image(named: "pointEditerBtn_h"), for: .selected)
- pointEdterBtn.addTarget(self, action: #selector(pointEditerBtnClick(sender:)), for: .touchUpInside)
- pointEdterBtn.isSelected = true
- pointEdterBtn.adjustsImageWhenHighlighted = false
- return pointEdterBtn
- }()
- // 音乐编辑 btn
- lazy var musicEditerBtn: UIButton = {
- let musicEditerBtn = UIButton(type: .custom)
- musicEditerBtn.setImage(UIImage().BF_Image(named: "musicEditerBtn_n"), for: .normal)
- musicEditerBtn.setImage(UIImage().BF_Image(named: "musicEditerBtn_h"), for: .selected)
- musicEditerBtn.addTarget(self, action: #selector(musicEditerBtnClick(sender:)), for: .touchUpInside)
- musicEditerBtn.adjustsImageWhenHighlighted = false
- return musicEditerBtn
- }()
- // 快慢速卡点模式 btn
- lazy var speedStuckBtn: UIButton = {
- let speedStuckBtn = UIButton(type: .custom)
- speedStuckBtn.addTarget(self, action: #selector(editModelClick(sender:)), for: .touchUpInside)
- speedStuckBtn.setBackgroundImage(UIImage().BF_Image(named: "speedstuck_n"), for: .normal)
- speedStuckBtn.setBackgroundImage(UIImage().BF_Image(named: "speedstuck_h"), for: .selected)
- speedStuckBtn.tag = 1
- speedStuckBtn.adjustsImageWhenHighlighted = false
- return speedStuckBtn
- }()
- //
- lazy var speedStuckBtnGif: UIImageView = {
- let speedStuckBtnGif = UIImageView.init()
- speedStuckBtnGif.kf.setImage(with: URL(fileURLWithPath: Bundle.init().BF_mainbundle().path(forResource: "speedstuck_h", ofType: ".gif")!))
- return speedStuckBtnGif
- }()
- // 跳转卡点模式 btn
- lazy var jumpPointBtn: UIButton = {
- let jumpPointBtn = UIButton(type: .custom)
- jumpPointBtn.setImage(UIImage().BF_Image(named: "jumpPoint_n"), for: .normal)
- jumpPointBtn.setImage(UIImage().BF_Image(named: "jumpPoint_h"), for: .selected)
- jumpPointBtn.tag = 2
- jumpPointBtn.addTarget(self, action: #selector(editModelClick(sender:)), for: .touchUpInside)
- jumpPointBtn.adjustsImageWhenHighlighted = false
- return jumpPointBtn
- }()
- lazy var jumpPointBtnGif: UIImageView = {
- let jumpPointBtnGif = UIImageView.init()
- jumpPointBtnGif.kf.setImage(with: URL(fileURLWithPath: Bundle.init().BF_mainbundle().path(forResource: "jumpPoint_n", ofType: ".gif")!))
- return jumpPointBtnGif
- }()
- // 仅配乐模式 btn
- lazy var onlyMusicBtn: UIButton = {
- let onlyMusicBtn = UIButton(type: .custom)
- onlyMusicBtn.setImage(UIImage().BF_Image(named: "onlyMusic_n"), for: .normal)
- onlyMusicBtn.setImage(UIImage().BF_Image(named: "onlyMusic_h"), for: .selected)
- onlyMusicBtn.tag = 3
- onlyMusicBtn.addTarget(self, action: #selector(editModelClick(sender:)), for: .touchUpInside)
- onlyMusicBtn.adjustsImageWhenHighlighted = false
- return onlyMusicBtn
- }()
- // 编辑模式的指示线
- lazy var editModelLineView: UIView = {
- let editModelLineView = UIView()
- editModelLineView.backgroundColor = UIColor.hexColor(hexadecimal: "#EFEFEF")
- return editModelLineView
- }()
- // 操作面板上的分割线
- lazy var optionlineView: UIView = {
- let optionlineView = UIView()
- optionlineView.backgroundColor = UIColor.hexColor(hexadecimal: "#EFEFEF")
- return optionlineView
- }()
- // 固定速度 UI
- lazy var speedSettingView: PQSpeedSettingView = {
- let speedSettingView = PQSpeedSettingView()
- speedSettingView.backgroundColor = .clear
- speedSettingView.viewType = 1
- speedSettingView.selectSpeedCallBack = { [weak self] maxSpeed, minSpeed,selectIndex in
- BFLog(message: "固定maxSpeed is\(maxSpeed) minSpeed \(minSpeed)")
- //更新最后一次选择的位置恢复时使用
- if(speedSettingView.viewType == 1){
- self?.lastSpeedSelectIndex = selectIndex
- }else if(speedSettingView.viewType == 2){
- self?.lastJumpSpeedSelectIndex = selectIndex
- }else{
- self?.lastCyclesSelectIndex = selectIndex
- }
- self?.maxSpeed = maxSpeed
- self?.minSpeed = minSpeed
- if maxSpeed == -1.0 && minSpeed == -1.0 {
- self?.customSpeedSettingView.isHidden = true
- self?.customSpeedSettingView.isJumpSpeedModel = speedSettingView.viewType == 2
- }
- self?.settingPlayerView()
- }
- return speedSettingView
- }()
- // 自定义速度
- lazy var customSpeedSettingView: PQCustomSpeedSettingView = {
- let customSpeedSettingView = PQCustomSpeedSettingView(frame: CGRect(x: 0, y: cScreenHeigth - 354, width: cScreenWidth, height: 354))
- customSpeedSettingView.isHidden = true
- customSpeedSettingView.isJumpSpeedModel = false
- customSpeedSettingView.selectSpeedCallBack = { [weak self] maxSpeed, minSpeed ,isJumpSpeedModel in
- self?.maxSpeed = maxSpeed
- self?.minSpeed = minSpeed
- BFLog(message: "自定义速度maxSpeed is\(maxSpeed) minSpeed \(minSpeed) \(isJumpSpeedModel)")
- self?.settingPlayerView()
- }
- return customSpeedSettingView
- }()
- /// 音乐标题
- lazy var musicNameView: UIView = {
- let musicNameView = UIView()
- musicNameView.addSubview(musicNameLab)
- let nameWidth: CGFloat = musicNameLab.frame.width + (25 + cDefaultMargin * 3)
- musicNameView.frame = CGRect(x: (view.frame.width - nameWidth) / 2, y: cDevice_iPhoneStatusBarHei + (cDevice_iPhoneNavBarHei - cDefaultMargin * 3) / 2, width: nameWidth, height: cDefaultMargin * 3)
- // musicNameView.backgroundColor = UIColor.hexColor(hexadecimal: "#333333")
- musicNameView.addCorner(corner: musicNameView.frame.height / 2)
- let musicImageView = UIImageView()
- musicImageView.tintColor = PQBFConfig.shared.styleTitleColor
- musicImageView.image = UIImage().BF_Image(named: "stuckPoint_reCreate_music").withRenderingMode(.alwaysTemplate)
- musicImageView.frame = CGRect(x: musicNameView.frame.height / 2 - 5, y: (musicNameView.frame.height - 22) / 2, width: 22, height: 22)
- musicNameView.addSubview(musicImageView)
- musicNameLab.frame.origin.x = musicImageView.frame.maxX + 5
- return musicNameView
- }()
- /// 音乐歌曲名称
- lazy var musicNameLab: LMJHorizontalScrollText = {
- 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
- let musicNameLab = LMJHorizontalScrollText(frame: CGRect(x: 0, y: 0, width: nameWidth < cDefaultMargin * 4 ? cDefaultMargin * 4 : nameWidth, height: cDefaultMargin * 3))
- musicNameLab.textColor = PQBFConfig.shared.styleTitleColor
- musicNameLab.textFont = UIFont.systemFont(ofSize: 13)
- musicNameLab.speed = 0.03
- musicNameLab.moveDirection = LMJTextScrollMoveLeft
- musicNameLab.moveMode = LMJTextScrollContinuous
- if nameWidth < cDefaultMargin * 4 {
- musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
- } else {
- musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
- }
- return musicNameLab
- }()
- /// 同步进度显示
- lazy var synchroMarskView: PQStuckPointLoadingView = {
- var synchroMarskView = PQStuckPointLoadingView(frame: CGRect(x: 0, y: 0, width: cScreenWidth, height: cScreenHeigth))
- synchroMarskView.cancelHandle = { [weak self] _ in
- self?.navigationController?.popViewController(animated: true)
- }
- return synchroMarskView
- }()
- override func viewWillAppear(_ animated: Bool) {
- super.viewDidAppear(animated)
- lineView?.isHidden = true
- UIApplication.shared.isIdleTimerDisabled = true
- musicNameLab.move()
- PQNotification.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
- }
- @objc func enterBackground() {
- BFLog(message: "进入到后台")
- // 取消导出
- playerView.pause()
- }
- override func backBtnClick() {
- super.backBtnClick()
- playerView.pause()
- // 点击上报:返回按钮
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_buttonClick, objectType: .ot_click_back, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-(点击上报:返回按钮)")
- }
-
- //使用选择音乐 调用情况:1,操作面板直接选择 ,2 搜索界面点击使用
- func userstuckPointMusic(musicData: PQVoiceModel?) {
-
- //1,音乐面板点击了使用
- stuckPointMusicData = musicData
- //2,同步最新音乐数据
- synchroMusicInfoData()
-
- //3,更新音乐标题UI
- 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
-
- if nameWidth < cDefaultMargin * 4 {
- musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
- } else {
- musicNameLab.text = " \(stuckPointMusicData?.musicName ?? "") "
- }
- }
- //点击搜索音乐
- @objc func musicSearchBtnClick(sender: UIButton) {
- let searchVC = PQEditMusicSearchController.init()
-
- searchVC.btnClickHandle = { [weak self] _, bgmData in
- // 使用音乐
- BFLog(message: "搜索音乐点击了使用")
- // self?.musicEditBGView.insertSearchMusic(model: bgmData as! PQVoiceModel)
- self?.userstuckPointMusic(musicData: bgmData as? PQVoiceModel)
- }
-
- let navigationController: UINavigationController = UINavigationController(rootViewController: searchVC)
- navigationController.modalPresentationStyle = .fullScreen
- present(navigationController, animated: true, completion: nil)
- }
-
- // 卡点编辑
- @objc func pointEditerBtnClick(sender: UIButton) {
- if sender.isSelected { return }
- sender.isSelected = !sender.isSelected
- musicEditerBtn.isSelected = false
- pointEditBGView.isHidden = false
- musicEditBGView.isHidden = true
- }
- // 音乐编辑
- @objc func musicEditerBtnClick(sender: UIButton) {
- if sender.isSelected { return }
- sender.isSelected = !sender.isSelected
- pointEditerBtn.isSelected = false
- pointEditBGView.isHidden = true
- musicEditBGView.isHidden = false
- }
- // 三种模式修改
- @objc func editModelClick(sender: UIButton) {
-
- //1,根据条件判断哪些功能不可用直接出提示
- // if(selectedDataCount == selectedImageDataCount && (sender == speedStuckBtn || sender == onlyMusicBtn )){
- // BFLog(message: "全是图片 \(selectedDataCount) \(selectedImageDataCount)")
- // cShowHUB(superView: self.view, msg: "1111")
- // return
- // }
- // if sender.isSelected {
- // BFLog(message: "已经是选中状态")
- // return
- // }
- lastEditModelBtn?.isSelected = false
- sender.isSelected = !sender.isSelected
- lastEditModelBtn = sender
- BFLog(message: "sender tag is \(sender.tag)")
- // 1 ui 调整
- if sender.tag == 1 || sender.tag == 2 {
- speedSettingView.viewType = sender.tag
- customSpeedSettingView.isJumpSpeedModel = speedSettingView.viewType == 2
- speedSettingView.snp.remakeConstraints { make in
- make.left.equalToSuperview().offset(16)
- make.right.equalToSuperview()
- make.top.equalTo(editModelLineView.snp_bottom).offset(8)
- make.height.equalTo(sender.tag == 1 ? 44 : 30)
- }
- speedSettingView.isHidden = false
- speedTitleLab.isHidden = false
- sustomSwitchView.isHidden = false
- editModelLineView.isHidden = false
- } else {
-
- speedTitleLab.isHidden = true
- speedSettingView.isHidden = true
- editModelLineView.isHidden = true
- sustomSwitchView.isHidden = true
-
- }
- //素材全是图片的时候三个模式都显示循环设置 UI
- if(selectedDataCount == selectedImageDataCount){
- speedSettingView.viewType = 3
- customSpeedSettingView.isJumpSpeedModel = false
- speedSettingView.snp.remakeConstraints { make in
- make.left.equalToSuperview().offset(16)
- make.right.equalToSuperview()
- make.top.equalTo(editModelLineView.snp_bottom).offset(8)
- make.height.equalTo(30)
- }
-
- speedSettingView.isHidden = !(selectedDataCount == selectedImageDataCount)
- editModelLineView.isHidden = !(selectedDataCount == selectedImageDataCount)
-
- speedSettingView.setSelectItem(index: lastCyclesSelectIndex)
-
- }else{
-
-
- if sender.tag == 1 { // 快慢速
- speedSettingView.setSelectItem(index: lastSpeedSelectIndex)
- } else if sender.tag == 2 { // 跳跃卡点
- speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex)
- } else if sender.tag == 3 { // 仅音乐
- customSpeedSettingView.isJumpSpeedModel = false
- speedSettingView.viewType = 2
- }
-
-
- }
- // 2 设置 btn 不同显示状态
- var speedStuckBtnGifName = ""
- var jumpPointBtnGifName = ""
- if sender.tag == 1 { // 快慢速
- speedStuckBtnGifName = "speedstuck_h"
- jumpPointBtnGifName = "jumpPoint_n"
- currentCreateStickersModel = .createStickersModelSpeed
-
- } else if sender.tag == 2 { // 跳跃卡点
- speedStuckBtnGifName = "speedstuck_n"
- jumpPointBtnGifName = "jumpPoint_h"
- currentCreateStickersModel = .createStickersModelPoint
- } else if sender.tag == 3 { // 仅音乐
- speedStuckBtnGifName = "speedstuck_n"
- jumpPointBtnGifName = "jumpPoint_n"
- currentCreateStickersModel = .createStickersModelOnlyMusic
-
- }
-
- settingPlayerView()
- speedStuckBtnGif.kf.setImage(with: URL(fileURLWithPath: Bundle.init().BF_mainbundle().path(forResource:speedStuckBtnGifName , ofType: ".gif")!))
- jumpPointBtnGif.kf.setImage(with: URL(fileURLWithPath: Bundle.init().BF_mainbundle().path(forResource: jumpPointBtnGifName , ofType: ".gif")!))
- }
- override func viewWillDisappear(_ animated: Bool) {
- super.viewWillDisappear(animated)
- UIApplication.shared.isIdleTimerDisabled = false
- musicNameLab.stop()
- playerView.pause()
-
- musicEditBGView.pausePlayer()
- }
- override func viewDidLoad() {
- super.viewDidLoad()
- leftButton(image: "icon_detail_back", tintColor: PQBFConfig.shared.styleTitleColor)
- navHeadImageView?.addSubview(nextBtn)
- navHeadImageView?.addSubview(musicNameView)
- // 添加子视图
- addSubViews()
- // 导出相册视频
- exportPhotoData()
- // 同步音乐数据
- synchroMusicInfoData()
- // 曝光上报:预览页面曝光上报
- PQEventTrackViewModel.baseReportUpload(businessType: .bt_windowView, objectType: .ot_view_previewSyncedUp, pageSource: .sp_stuck_previewSyncedUp, extParams: nil, remindmsg: "卡点视频数据上报-(曝光上报:预览页面曝光上报)")
- }
- /// 添加子视图
- /// - Returns: <#description#>
- func addSubViews() {
- if (stuckPointMusicData?.rhythmSdata.count ?? 0) <= 0 {
- return
- }
- view.addSubview(playerView)
- view.addSubview(pointEditBGView)
- view.addSubview(musicEditBGView)
- view.addSubview(optionlineView)
- view.addSubview(pointEditerBtn)
- view.addSubview(musicEditerBtn)
- view.addSubview(customSpeedSettingView)
- //卡点
- pointEditBGView.addSubview(pointEditRemindLab)
- pointEditBGView.addSubview(speedTitleLab)
- pointEditBGView.addSubview(speedStuckBtn)
- speedStuckBtn.addSubview(speedStuckBtnGif)
- pointEditBGView.addSubview(jumpPointBtn)
- jumpPointBtn.addSubview(jumpPointBtnGif)
- pointEditBGView.addSubview(onlyMusicBtn)
- pointEditBGView.addSubview(editModelLineView)
- pointEditBGView.addSubview(speedSettingView)
- pointEditBGView.addSubview(sustomSwitchView)
-
- //音乐
- musicEditBGView.addSubview(stuckPointCuttingView)
- pointEditerBtn.snp.makeConstraints { make in
- make.left.equalToSuperview().offset(100)
- make.bottom.equalToSuperview().offset(-(14 + cAKSafeAreaHeight))
- make.height.equalTo(24)
- make.width.equalTo(24)
- }
- musicEditerBtn.snp.makeConstraints { make in
- make.right.equalToSuperview().offset(-100)
- make.top.equalTo(pointEditerBtn.snp_top)
- make.height.equalTo(24)
- make.width.equalTo(24)
- }
- pointEditBGView.snp.makeConstraints { make in
- make.left.right.equalTo(view)
- make.bottom.equalTo(pointEditerBtn.snp_top).offset(-7)
- make.height.equalTo(290)
- }
- musicEditBGView.snp.makeConstraints { make in
- make.left.right.equalTo(view)
- make.bottom.equalTo(pointEditerBtn.snp_top).offset(-7)
- make.height.equalTo(290)
- }
- stuckPointCuttingView.snp.makeConstraints { make in
- make.left.right.equalTo(view)
- make.bottom.equalTo(pointEditerBtn.snp_top).offset(-7)
- make.height.equalTo(290)
- }
- optionlineView.snp.makeConstraints { make in
- make.left.right.equalToSuperview()
- make.bottom.equalTo(pointEditBGView.snp_bottom).offset(-1)
- make.height.equalTo(1)
- }
-
- stuckPointCuttingView.snp.makeConstraints { make in
- make.left.right.equalToSuperview()
- make.bottom.equalTo(musicEditBGView.snp_bottom).offset(-1)
- make.height.equalTo(85)
- }
- pointEditRemindLab.snp.makeConstraints { make in
- make.left.equalToSuperview().offset(16)
- make.top.equalToSuperview().offset(16)
- make.height.equalTo(20)
- make.width.equalTo(80)
- }
- speedStuckBtn.snp.makeConstraints { make in
- make.left.equalToSuperview().offset(16)
- make.top.equalTo(pointEditRemindLab.snp_bottom).offset(8)
- make.height.equalTo(80)
- make.width.equalTo(80)
- }
- speedStuckBtnGif.snp.makeConstraints { make in
- make.height.width.equalTo(46)
- make.top.equalToSuperview().offset(8)
- make.centerX.equalToSuperview()
- }
- jumpPointBtn.snp.makeConstraints { make in
- make.left.equalTo(speedStuckBtn.snp_right).offset(10)
- make.top.equalTo(speedStuckBtn.snp_top)
- make.height.equalTo(80)
- make.width.equalTo(80)
- }
- jumpPointBtnGif.snp.makeConstraints { make in
- make.height.width.equalTo(46)
- make.top.equalToSuperview().offset(8)
- make.centerX.equalToSuperview()
- }
- onlyMusicBtn.snp.makeConstraints { make in
- make.left.equalTo(jumpPointBtn.snp_right).offset(10)
- make.top.equalTo(speedStuckBtn.snp_top)
- make.height.equalTo(80)
- make.width.equalTo(64)
- }
- editModelLineView.snp.makeConstraints { make in
- make.left.equalToSuperview().offset(16)
- make.right.equalToSuperview()
- make.bottom.equalTo(onlyMusicBtn.snp_bottom).offset(17)
- make.height.equalTo(1)
- }
- speedSettingView.snp.makeConstraints { make in
- make.left.equalToSuperview().offset(16)
- make.right.equalToSuperview()
- make.top.equalTo(editModelLineView.snp_bottom).offset(8)
- make.height.equalTo(44)
- }
- speedTitleLab.snp.makeConstraints { make in
- make.left.equalToSuperview().offset(16)
- make.top.equalToSuperview().offset(213)
- make.height.equalTo(20)
- make.width.equalTo(80)
- }
- sustomSwitchView.snp.makeConstraints { make in
- make.left.equalToSuperview().offset(16)
- make.top.equalTo(speedTitleLab.snp_bottom).offset(8)
- make.height.equalTo(30)
- make.width.equalTo(180)
- }
- }
- @objc func nextBtnClick(sender _: UIButton) {
- BFLog(message: "去发布")
- playerView.pause()
- let videoExporter = PQStuckPointPublicController()
- videoExporter.rhythmMode = currentCreateStickersModel
- videoExporter.isReCreate = isReCreate
- videoExporter.selectedTotalDuration = selectedTotalDuration
- videoExporter.selectedDataCount = selectedDataCount
- videoExporter.selectedImageDataCount = selectedImageDataCount
- videoExporter.finallyUserAudioTime = finallyUserAudioTime
- videoExporter.clipAudioRange = getClipAudioRange()
- videoExporter.playeTimeRange = playeTimeRange
- // 使用深 copy
- let json = projectModel.toJSONString(prettyPrint: false)
- if json == nil {
- BFLog(message: "数据转换有问题 跳转")
- return
- }
- let tempModel: PQEditProjectModel? = Mapper<PQEditProjectModel>().map(JSONString: json!)
- videoExporter.mStickers = mStickers
- videoExporter.audioMixModel = stuckPointMusicData
- videoExporter.editProjectModel = tempModel
- navigationController?.pushViewController(videoExporter, animated: true)
- // 点击上报:去合成
- 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: "点击上报:去合成")
- }
- // MARK: - 播放器相关操作
- /// seek 播放器
- /// - Parameter playeTimeRange: 开始和结束时间
- func seekPlayer(playeTimeRange: CMTimeRange) {
- playerView.setEnableSeek(isSeek: true)
- playerView.play(pauseFirstFrame: false, playeTimeRange: playeTimeRange)
- }
- /// 通过传入的 selectedPhotoData 、 stuckPointMusicData 创建 projectModel 模型 后面都使用 projectModel 参数
- func createPorjectData() {
- // 1,添加选择的视觉素材
- let section: PQEditSectionModel = PQEditSectionModel()
- selectedPhotoData?.forEach { model in
- let json = model.toJSONString(prettyPrint: false)
- if json == nil {
- BFLog(message: "数据转换有问题 跳转")
- return
- }
- let tempModel: PQEditVisionTrackMaterialsModel = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: json!)!
- section.sectionTimeline?.visionTrack?.visionTrackMaterials.append(tempModel)
- }
- projectModel.sData?.sections.append(section)
- // 2,添加背景音乐
- projectModel.sData?.addBGM(audioMix: stuckPointMusicData!)
- }
-
- //计算拼接音乐的开始和结束点
- func getClipAudioRange() -> CMTimeRange {
- //设置音乐的拼接范围,开始:推荐的卡点 结束:点到的倒数第二位
- let lastSecondPoint = Float((stuckPointMusicData!.rhythmSdata[0].pointTimes[stuckPointMusicData!.rhythmSdata[0].pointTimes.count - 2] )) / Float(BASE_FILTER_TIMESCALE)
- let clipAudioRange =
- CMTimeRange(start: CMTime(value: CMTimeValue( Float(stuckPointMusicData?.startTime ?? 0) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: CMTimeValue((Float(lastSecondPoint)) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE))
-
- return clipAudioRange
-
- }
- // 设置播放器
- func settingPlayerView() {
- // 1,设置播放器的显示区域 和画布大小
- // - 按第一个素材尺寸自适应
- let playerShowHeight = pointEditBGView.frame.minY - (navHeadImageView?.frame.maxY ?? 0)
- var showRect: CGRect = PQPlayerViewModel.getShowCanvasRect(editProjectModel: projectModel, showType: 1, playerViewHeight: playerShowHeight)
- if showRect.size.width == showRect.size.height {
- if cScreenWidth > playerShowHeight {
- showRect.origin.x = (cScreenWidth - playerShowHeight) / 2
- showRect.size.width = playerShowHeight
- showRect.size.height = playerShowHeight
- } else {
- showRect.origin.x = 0
- showRect.size.width = cScreenWidth
- showRect.size.height = cScreenWidth
- }
- }
- showRect.origin.y = (playerShowHeight - showRect.size.height) / 2.0 + (navHeadImageView?.frame.maxY ?? 0)
- if showRect.size.width != 0, showRect.size.height != 0 {
- playerView.resetCanvasFrame(frame: showRect)
- }
- var firstModel: PQEditVisionTrackMaterialsModel?
- for part in projectModel.sData!.sections {
- if part.sectionTimeline?.visionTrack?.getEnableVisionTrackMaterials().count ?? 0 > 0 {
- firstModel = part.sectionTimeline?.visionTrack?.getEnableVisionTrackMaterials().first
- break
- }
- }
- var videoSize: CGSize = CGSize(width: Int(firstModel?.width ?? 0), height: Int(firstModel?.height ?? 0))
- var minSlider = min(videoSize.width, videoSize.height)
- var maxSlider = max(videoSize.width, videoSize.height)
- let ration = 1080 / minSlider
- minSlider = minSlider * ration
- maxSlider = maxSlider * ration
- if videoSize.width > videoSize.height { // 宽屏
- videoSize = CGSize(width: maxSlider, height: minSlider)
- } else {
- videoSize = CGSize(width: minSlider, height: maxSlider)
- }
- let maxValue = max(videoSize.width, videoSize.height )
- if maxValue > 1920 {
- let maxRation = 1920 / maxValue
- videoSize = CGSize(width: videoSize.width * CGFloat(maxRation), height: videoSize.height * CGFloat(maxRation))
- BFLog(message: "最长边已经超过 1920 要等比缩小 缩放后\(videoSize)")
- }
- if (Int(videoSize.width) % 2) != 0 {
- videoSize.width = videoSize.width - 1
- }
- if (Int(videoSize.height) % 2) != 0 {
- videoSize.height = videoSize.height - 1
- }
- projectModel.sData?.videoMetaData?.videoWidth = Int(videoSize.width)
- projectModel.sData?.videoMetaData?.videoHeight = Int(videoSize.height)
- // 2,创建滤镜
- let beginTime: TimeInterval = Date().timeIntervalSince1970
- mStickers = createStickers(sections: projectModel.sData?.sections ?? List(), inputSize: CGSize(width: CGFloat(projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(projectModel.sData?.videoMetaData?.videoHeight ?? 0)), model: currentCreateStickersModel)
- playerView.mStickers = mStickers
- let end: TimeInterval = Date().timeIntervalSince1970
- BFLog(message: "createStickers tiskskskskme \(end - beginTime)")
- // 3,设置音频
- let audioPath = stuckPointMusicData?.localPath ?? ""
-
- BFLog(message: "初始化音频播放器的音频地址为:\(audioPath)")
- playerView.stop()
- // 这里的测试这个音乐播放有问题
- // self.playerView.updateAsset(URL(fileURLWithPath: "63930549652d74e477141e3b79c8d29a9ef8af81625053214516.mp3", relativeTo:Bundle.main.resourceURL!), videoComposition: nil, audioMixModel: nil)
-
- playerView.updateAsset(URL(fileURLWithPath: documensDirectory + audioPath), videoComposition: nil, audioMixModel: nil,originMusicDuration:finallyUserAudioTime,clipAudioRange: getClipAudioRange())
-
- let end2: TimeInterval = Date().timeIntervalSince1970
- BFLog(message: "updateAsset tiskskskskme \(end2 - end)")
- // 4, 设置播放器的输出画布大小
- playerView.movie?.mShowVidoSize = CGSize(width: CGFloat(projectModel.sData?.videoMetaData?.videoWidth ?? 0), height: CGFloat(projectModel.sData?.videoMetaData?.videoHeight ?? 0))
- // 5,开始播放
- playerView.isLoop = false
- playerView.showProgressLab = true
- // 初始化音频的开始和结束时间
- BFLog(message: "播放的器 开始\(String(describing: CMTimeGetSeconds(playeTimeRange.start))) 结束 \(String(describing: CMTimeGetSeconds(playeTimeRange.end)))")
- let end3: TimeInterval = Date().timeIntervalSince1970
- playerView.play(pauseFirstFrame: false, playeTimeRange: CMTimeRange(start: playeTimeRange.start, end: playeTimeRange.end))
- let end4: TimeInterval = Date().timeIntervalSince1970
- BFLog(message: " playerView.play tiskskskskme \(end4 - end3)")
- // 6,进度回调
- playerView.progress = { [weak self] currentTime, tatolTime, _ in
- // 更新进度
- let progress = (currentTime - CMTimeGetSeconds(self?.playeTimeRange.start ?? .zero)) / CMTimeGetSeconds(self?.playeTimeRange.duration ?? .zero)
- BFLog(message: "\(currentTime) \(tatolTime) 显示播放器进度为: \(progress)")
- self?.stuckPointCuttingView.updateProgress(progress: CGFloat(progress))
- if self?.synchroMarskView.superview != nil {
- self?.synchroMarskView.removeMarskView()
- }
- }
- }
- deinit {
- BFLog(message: "卡点视频预览界面销毁")
- musicNameLab.stop()
- playerView.pause()
- // 取消所有的导出
- PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
- exportSession.cancelExport()
- }
- }
- }
- // MARK: - 视频渲染相关逻辑方法
- extension PQStuckPointEditerController {
- /// 分割视频 这里只设置视频类型的 in 和 out 并不设置显示的开始和结束时间 mp4 ,png ,png ,mp4
- /// - Parameter section: 当前段
- /// - Parameter stuckPoints: 用户选择的,或推荐的卡点数
- /// - Returns: 返回分割后的所有 stickers 和卡点数是一致的
- func clipVideoMerage(section: PQEditSectionModel, stuckPoints: [Float]) -> [PQEditVisionTrackMaterialsModel] {
- var stickers: Array = Array<PQEditVisionTrackMaterialsModel>.init()
- // 第二种情况:有视频要进行分割
- /*
- 1, 确定每个视频素材需要切的段数p
- 2, 将所有视频时长相加,得到总视频素材时长L = l1 + l2 + ... + ln
- 3, 视频素材a1需要切分的个数clipNum = max (round (kongduan * a1 / L) , 1)
- */
- // 要补的空位数
- let kongduan: Int = Int(stuckPoints.count) - Int(section.sectionTimeline!.visionTrack?.getEnableVisionTrackMaterials().count ?? 0)
- // 所有视频总时长
- var videoTotalDuration: Float64 = 0.0
- for video in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video") {
- let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + video.locationPath), options: nil)
- videoTotalDuration = videoTotalDuration + Float64(CMTimeGetSeconds(asset.duration))
- }
- if videoTotalDuration == 0 {
- BFLog(message: "视频总时长出现错误!!!!这里应该有视频素材的")
- return stickers
- }
- for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
- if sticker.type == StickerType.VIDEO.rawValue {
- let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
- // 要分割的段落
- let clipNum = Int(max(round(Double(kongduan) * CMTimeGetSeconds(asset.duration) / videoTotalDuration), 1))
- sticker.duration = CMTimeGetSeconds(asset.duration)
- BFLog(message: "单个视频\(sticker.locationPath)时长::\(CMTimeGetSeconds(asset.duration)) ,clipNum is:\(clipNum)")
- for clipindex in 0 ... clipNum - 1 {
- // deep copy sticker model 防止只有一个对象
- let stickerjson = sticker.toJSONString(prettyPrint: false)
- let deepCopySticker = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: stickerjson!)
- // 设置循环模式和适配模式
- deepCopySticker?.generateDefaultValues()
- deepCopySticker?.model_in = clipindex == 0 ? 0 : CMTimeGetSeconds(asset.duration) / Double(clipNum) * Double(clipindex)
- deepCopySticker?.out = (deepCopySticker?.model_in ?? 0) + CMTimeGetSeconds(asset.duration) / Double(clipNum)
- if (deepCopySticker?.model_in ?? 0) >= CMTimeGetSeconds(asset.duration) || (deepCopySticker?.out ?? 0) >= CMTimeGetSeconds(asset.duration) {
- deepCopySticker?.model_in = CMTimeGetSeconds(asset.duration) - CMTimeGetSeconds(asset.duration) / Double(clipNum)
- deepCopySticker?.out = CMTimeGetSeconds(asset.duration)
- }
- BFLog(message: " crilp is in \(deepCopySticker?.model_in ?? 0) out \(deepCopySticker?.out ?? 0) 总时长\(CMTimeGetSeconds(asset.duration))")
- if deepCopySticker != nil {
- stickers.append(deepCopySticker!)
- }
- }
- } else if sticker.type == StickerType.IMAGE.rawValue {
- sticker.generateDefaultValues()
- stickers.append(sticker)
- }
- }
- return stickers
- }
- /// 创建sticker
- /// - Parameters:
- /// - sections: 项目所有段落数据信息
- /// - inputSize: 画布大小
- /// - Returns: filters 数据 播放器可直接使用
- func createStickers(sections: List<PQEditSectionModel>, inputSize _: CGSize = .zero, model: createStickersModel = .createStickersModelPoint) -> [PQEditVisionTrackMaterialsModel] {
-
- // 推荐卡点数
- dealParameter(model: model)
- // 保存滤镜对象数据
- var stickers: Array = Array<PQEditVisionTrackMaterialsModel>.init()
- if model == .createStickersModelPoint { //跳跃卡点
- for section in sections {
- if section.sectionType == "normal" {
-
- // 当用户上传视觉素材个数大于等于音乐选择区域节拍分割个数时,无需进行视频分割,只显示卡点数-1 个素材
- if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count >= stuckPointMusicData!.rhythmSdata[0].pointTimes.count {
- for (index, sticker) in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().enumerated() {
- if index == stuckPointMusicData!.rhythmSdata[0].pointTimes.count {
- BFLog(message: "到达卡点数量")
- break
- }
- BFLog(message: "创建 filter start :\(sticker.timelineIn) end :\(sticker.timelineOut) type is \(sticker.type) \(sticker.locationPath)")
- sticker.timelineIn = Float64("\(finallyStuckPoints[index])") ?? 0.0
- sticker.timelineOut = Float64("\(finallyStuckPoints[index + 1])") ?? 0.0
- BFLog(message: "卡点 间隔 \(sticker.timelineIn - sticker.timelineOut)")
- sticker.generateDefaultValues()
- stickers.append(sticker)
- }
- } else {
- // 卡点数 > 选择素材数
- // 第一种情况:全是图片,图片回环播放
- if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video").count == 0, section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "image").count > 0 {
- for (index, point) in finallyStuckPoints.enumerated() {
- let sticker: PQEditVisionTrackMaterialsModel = section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials()[index % section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count]
- BFLog(message: "stickerlocationPath sticker : \(sticker.locationPath)")
- let stickerjson = sticker.toJSONString(prettyPrint: false)
- let deepCopySticker = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: stickerjson!)
- if deepCopySticker!.type == StickerType.IMAGE.rawValue {
- if index + 1 < finallyStuckPoints.count {
- deepCopySticker!.timelineIn = Float64("\(finallyStuckPoints[index])") ?? 0.0
-
- deepCopySticker!.timelineOut = Float64("\(finallyStuckPoints[index + 1])") ?? 0.0
- if deepCopySticker != nil {
- deepCopySticker?.generateDefaultValues()
- stickers.append(deepCopySticker!)
- }
- }
- }
- }
- } else {
- // 第二种情况:有视频要进行分割
- let clipFilters = clipVideoMerage(section: section, stuckPoints: finallyStuckPoints)
- for (index, point) in finallyStuckPoints.enumerated() {
- BFLog(message: "aaaaaindexindeindexxindexindexindex \(index) \(point)")
- if index + 1 < finallyStuckPoints.count, index < clipFilters.count {
- BFLog(message: "bbbbbindexindeindexxindexindexindex \(index) \(point)")
- let sticker: PQEditVisionTrackMaterialsModel = clipFilters[index]
- sticker.timelineIn = Float64("\(finallyStuckPoints[index])") ?? 0.0
-
- sticker.timelineOut = Float64("\(finallyStuckPoints[index + 1])") ?? 0.0
- // 卡点的时间 > in out 值 这里就会出现鬼畜效果
- let timelineInterval = sticker.timelineOut - sticker.timelineIn
- let inOutInterval = sticker.out - sticker.model_in
- if timelineInterval > inOutInterval {
- BFLog(message: "实际要显示卡点时长\(timelineInterval) 素材裁剪时长:\(inOutInterval)")
- sticker.out = sticker.model_in + timelineInterval
- // 下面只是 LOG 方便查问题
- let stickerInOut = sticker.out - sticker.model_in
- let stickerTimelineInOut = sticker.timelineOut - sticker.timelineIn
- if stickerInOut != stickerTimelineInOut {
- BFLog(message: "sticker.timelineIn \(sticker.timelineIn) stickerTimelineInOut is\(stickerTimelineInOut) stickerInOut is\(stickerInOut) 相差\(stickerTimelineInOut - stickerInOut)")
- }
- }
- // out > 素材的总时长in out 进行前移操作
- let offsetAssetDuration = sticker.out - sticker.duration
- if offsetAssetDuration > 0 {
- sticker.model_in = sticker.model_in - offsetAssetDuration
- sticker.out = sticker.out - offsetAssetDuration
- }
- 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)")
- stickers.append(sticker)
- }
- }
- }
- }
- }
- }
- }else if (model == .createStickersModelOnlyMusic || model == .createStickersModelSpeed){ //仅音乐 和 跳跃卡点
- for section in sections {
- if section.sectionType == "normal" {
- BFLog(message: "stuckPoints count is \(finallyStuckPoints.count)")
-
- // 第一种情况:全是图片,图片回环播放
- if section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "video").count == 0, section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials(type: "image").count > 0 {
- for (index, point) in finallyStuckPoints.enumerated() {
- let sticker: PQEditVisionTrackMaterialsModel = section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials()[index % section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials().count]
- BFLog(message: "stickerlocationPath sticker : \(sticker.locationPath)")
- let stickerjson = sticker.toJSONString(prettyPrint: false)
- let deepCopySticker = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: stickerjson!)
- if deepCopySticker!.type == StickerType.IMAGE.rawValue {
- if index + 1 < finallyStuckPoints.count {
- deepCopySticker!.timelineIn = Float64("\(finallyStuckPoints[index])") ?? 0.0
-
- deepCopySticker!.timelineOut = Float64("\(finallyStuckPoints[index + 1])") ?? 0.0
- if deepCopySticker != nil {
- deepCopySticker?.generateDefaultValues()
- stickers.append(deepCopySticker!)
- }
- }
- }
- }
- }else{
- //一共裁剪的段数
- var totalClipNum:Int = 0
- for sticker in section.sectionTimeline!.visionTrack!.getEnableVisionTrackMaterials() {
- if sticker.type == StickerType.VIDEO.rawValue {
- let asset: AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + sticker.locationPath), options: nil)
- var i:Int = 0
- //一个视频切的的段落
- var oneVideoClipNum:Int = 0
- var realUsedMusicDuration = 0.0
- while realUsedMusicDuration <= CMTimeGetSeconds(asset.duration) {
- //快速段
- let LA = maxSpeed * (finallyStuckPoints[i+1] - finallyStuckPoints[i])
- realUsedMusicDuration = realUsedMusicDuration + Float64(LA)
- if(realUsedMusicDuration > CMTimeGetSeconds(asset.duration)){ break}
- oneVideoClipNum = oneVideoClipNum + 1
- //慢速段
- if(i + 2 >= finallyStuckPoints.count){
- break
- }
- let LB = minSpeed * (finallyStuckPoints[i+2] - finallyStuckPoints[i+1])
- realUsedMusicDuration = realUsedMusicDuration + Float64(LB)
- oneVideoClipNum = oneVideoClipNum + 1
- i = i + 1
- }
- BFLog(message: "单个视频\(sticker.locationPath)时长::\(CMTimeGetSeconds(asset.duration)) ,clipNum is:\(oneVideoClipNum)")
- var lastOutTime:Float64 = 0.0
- for clipindex in 0 ... oneVideoClipNum - 1 {
- // deep copy sticker model 防止只有一个对象
- let stickerjson = sticker.toJSONString(prettyPrint: false)
- let deepCopySticker = Mapper<PQEditVisionTrackMaterialsModel>().map(JSONString: stickerjson!)
- // 设置循环模式和适配模式
- deepCopySticker?.generateDefaultValues()
- //当前分段的速度
- var tempSpeed:Float = 1.0
- if model == .createStickersModelSpeed{
- tempSpeed = (totalClipNum + clipindex) % 2 == 0 ? maxSpeed : minSpeed
- }
-
- if(totalClipNum + 1 + clipindex < finallyStuckPoints.count){
- deepCopySticker?.speedRate = tempSpeed
- //定义临时使用的变量
- let tempModel_In = lastOutTime
- let tempOut = lastOutTime + Float64(tempSpeed) * Float64(finallyStuckPoints[totalClipNum + 1 + clipindex] - finallyStuckPoints[totalClipNum + clipindex])
- let tempTimelineIn = Float64(finallyStuckPoints[totalClipNum + clipindex])
- let timelineOut = Float64(finallyStuckPoints[totalClipNum + 1 + clipindex])
- //处理最后一点视频素材不够卡点时长 e.g. 0.3 卡点时长0.5
- if(tempOut > realUsedMusicDuration){
- BFLog(message: "最后一点视频素材不够卡点时长 ")
- //最后一点素材时长
- let lastAssetDuration = realUsedMusicDuration - tempOut
- let pointDuration = timelineOut - tempTimelineIn
- let needSpeed = lastAssetDuration / pointDuration
- //当前卡点段为快速
- if(tempSpeed >= 1){
- if(needSpeed > 0.4 * Double(tempSpeed)){
- deepCopySticker?.speedRate = tempSpeed
- } else{
- continue
- }
- }else{//当前卡点段为慢速
- if(needSpeed > 0.4 * Double(tempSpeed) && needSpeed > 0.2){
- deepCopySticker?.speedRate = tempSpeed
- } else{
- continue
- }
- }
- }
- deepCopySticker?.model_in = tempModel_In
- deepCopySticker?.out = tempOut
- deepCopySticker?.timelineIn = tempTimelineIn
- deepCopySticker?.timelineOut = timelineOut
- lastOutTime = deepCopySticker?.out ?? 0
- }
- BFLog(message: " 创建 sticker crilp is in \(String(format: "%.6f", deepCopySticker?.model_in ?? 0)) out \(String(format: "%.6f", deepCopySticker?.out ?? 0)) ,分段素材时长:\(String(format: "%.6f", (deepCopySticker?.out ?? 0) - (deepCopySticker?.model_in ?? 0))) ,分段显示时长:\(String(format: "%.6f", (deepCopySticker?.timelineOut ?? 0) - (deepCopySticker?.timelineIn ?? 0))), 总时长\(CMTimeGetSeconds(asset.duration)) timelineIN: \(String(format: "%.6f", deepCopySticker?.timelineIn ?? 0)) timelineOUT:\(String(format: "%.6f", deepCopySticker?.timelineOut ?? 0)) speedRate:\(deepCopySticker?.speedRate ?? 0.0)")
- if deepCopySticker != nil {
- stickers.append(deepCopySticker!)
- }
- }
- totalClipNum = totalClipNum + oneVideoClipNum - 1
- } else if sticker.type == StickerType.IMAGE.rawValue {
- if(totalClipNum + 1 >= finallyStuckPoints.count){
- BFLog(message: "数据出现错误!!!查正")
- break
- }
- sticker.generateDefaultValues()
- sticker.timelineIn = Float64(finallyStuckPoints[totalClipNum])
- sticker.timelineOut = Float64(finallyStuckPoints[totalClipNum + 1])
- totalClipNum = totalClipNum + 1
- stickers.append(sticker)
- }
- }
- }
-
- }
-
-
- }
- }
- return stickers
- }
-
-
- /// 根据档位取最后使用的卡点数据
- /// - Parameter seed: 档位速度
- /// - Returns: 最后使用的卡点
- func getUsedStuckPoint(seed:Int) -> Array<Float> {
- // 推荐卡点数
- var stuckPoints: Array = Array<Float>.init()
- var stuckPointsTemp = Array<Float>.init()
- for (index, dunshu) in stuckPointMusicData!.rhythmSdata[0].pointTimes.enumerated() {
- BFLog(message: "原所有卡点数:\(index) \(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))")
- if Float64(dunshu) / Float64(BASE_FILTER_TIMESCALE) > (stuckPointMusicData?.startTime ?? 0) {
- stuckPointsTemp.append(Float(dunshu) / Float(BASE_FILTER_TIMESCALE))
- }
- }
- /*
- 一,快慢速模式下取卡点 1 3 5
- 快节奏为选中区域的所有点位,即0,1,2,3,4 5 6 7 8 9 10 ……
- 适中为每两个点位取一个,即0,3,6,9 12
- 慢节奏为每三个点位取一个,即0 5 10 15
-
- 二,跳跃卡点模式下根据不同速度 取卡点 1,2,3
- 快节奏为选中区域的所有点位,即0,1,2,3,4 5 6 7 8 9 10 ……
- 适中为每两个点位取一个,即0,2,4,6……
- 慢节奏为每三个点位取一个,即0,3,6,9……
- */
- BFLog(message: "stuckPointMusicData?.speed is \(String(describing: stuckPointMusicData?.speed))")
-
- //不丢
- if(seed == 1){
- stuckPoints = stuckPointsTemp
- }else{
- //根据档位要丢
- for (index, point) in stuckPointsTemp.enumerated() {
- if index % seed == 0 {
- stuckPoints.append(point)
- }
- }
- }
-
- for point in stuckPoints {
- BFLog(message: "没有 start end 计算后的卡点数\(point)")
- }
- //若音乐起点至第一个卡点点位之间时长t0<0.3时,此段时长与下一个点位时长合并,故第一段卡点部分时长为t0+d
- if(Float(stuckPointMusicData?.startTime ?? 0) - (stuckPoints.first ?? 0.0) < 0.3){
- if stuckPoints.first != nil {
- stuckPoints.removeFirst()
- }
- }
- stuckPoints.insert(Float(stuckPointMusicData?.startTime ?? 0), at: 0)
-
- // if stuckPoints.last != nil {
- // stuckPoints.removeLast()
- // }
- // 开始时间是服务器返回, 结果时间根据策略计算的
- // stuckPoints.insert(Float(stuckPointMusicData?.startTime ?? 0), at: 0)
- // stuckPoints.insert(Float(stuckPointMusicData?.endTime ?? 0), at: stuckPoints.count)
- for point in stuckPoints {
- BFLog(message: "有 start end 计算后的卡点数\(point)")
- }
- BFLog(message: "stuckPoints count is \(stuckPoints.count)")
-
- return stuckPoints
-
-
-
- }
-
- /// 根据不同模式model, maxSpeed ,minSpeed, self?.stuckPointMusicData?.speed 档位,生成音乐时长和最终使用的卡点信息
- func dealParameter(model:createStickersModel){
-
- //清空上一次使用的卡点数据
- finallyStuckPoints.removeAll()
- //注意推荐时间位置和后面最近的卡点时间与0.3的关系
- //保存丢卡点处理后的卡点信息推荐开始到最后倒数第二个
- let stuckPointsTemp:Array<Float>
-
-
- //最后真正要使用推荐结束时长
- var realUsedMusicDuration:Float = 0.0
- switch model {
- case .createStickersModelPoint://跳跃卡点
- stuckPointsTemp = getUsedStuckPoint(seed: stuckPointMusicData?.speed ?? 0)
- // L/(n+1) L -原视觉素材总时长 n-抛留倍数
- realUsedMusicDuration = Float(selectedTotalDuration) / (Float(maxSpeed) + 1)
-
- //要拼接的段数
- var clipNum:Int = 0
- //所有段的时长总和
- var tempTime:Float = 0.0
- var i:Int = 0
- while tempTime < Float(selectedTotalDuration) {
-
- //回环从头取
- if(i+2 >= stuckPointsTemp.count){
- i = 0
- }
- //快速段
- let LA = (stuckPointsTemp[i+1] - stuckPointsTemp[i])
- tempTime = tempTime + Float(LA)
- if(tempTime > Float(selectedTotalDuration)){ break}
- //慢速段
- let LB = (stuckPointsTemp[i+2] - stuckPointsTemp[i+1])
- tempTime = tempTime + Float(LB)
- i = i + 1
- clipNum = clipNum + 1
- }
-
- //拼接要使用的卡点信息
- for i in 0...clipNum {
- finallyStuckPoints.append(stuckPointsTemp[i % stuckPointsTemp.count] + Float(i / stuckPointsTemp.count) * (stuckPointsTemp.first ?? 0))
- }
-
- break
- case .createStickersModelSpeed://快慢速
- // 快慢速 (2:快节奏,3:适中,4:慢节奏)
- stuckPointsTemp = getUsedStuckPoint(seed: (stuckPointMusicData?.speed ?? 0) + 1)
-
- /*
- - A-视频中的快速片段
- - B-视频中的慢速片段
- - d-在一档下音乐每个点位时长
- - n-不同音乐档位对应的d倍数,快节奏时,n=1;适中时,n=3;慢节奏时,n=5
- - L-原视觉素材时长
- - x-视频在A片段的播放倍速
- - y-视频在B片段的播放倍速
- */
- //LA=x*n*d,LB=y*n*d (n=1/3/5) 注:视频经过快慢速处理后的总时长约=L*2/(x+y)
- BFLog(message: "Ax快速为:\(maxSpeed) By慢速为:\(minSpeed) 档位 N为:\(stuckPointMusicData?.speed ?? 0) 使用的卡点总数:\(stuckPointsTemp.count)")
-
- //1,计算 AB 段的个数
- var i:Int = 0
- var LACount:Int = 0
- var LBCount:Int = 0
- while realUsedMusicDuration < Float(selectedTotalDuration) && selectedTotalDuration != 0 {
-
- //回环从头取
- if(i+2 > stuckPointsTemp.count){
- i = 0
- }
- //快速段
- let LA = maxSpeed * (stuckPointsTemp[i+1] - stuckPointsTemp[i])
- realUsedMusicDuration = realUsedMusicDuration + Float(LA)
- if(realUsedMusicDuration > Float(selectedTotalDuration)){ break}
- LACount = LACount + 1
- BFLog(message: "快速段段段段段段后LACount:\(LACount) realUsedMusicDuration:\(realUsedMusicDuration)")
- //慢速段
- let LB = minSpeed * (stuckPointsTemp[i+2] - stuckPointsTemp[i+1])
- realUsedMusicDuration = realUsedMusicDuration + Float(LB)
- LBCount = LBCount + 1
- BFLog(message: "慢速段段段段段段后LACount:\(LACount) realUsedMusicDuration:\(realUsedMusicDuration)")
-
- i = i + 1
- }
-
- //2,拼接要使用的卡点信息
- for (index,point) in stuckPointsTemp.enumerated() {
-
- if(index < LACount + LBCount){
- finallyStuckPoints.append(point)
- }
- }
- //2.1不足所有卡点回环拼接
- if( LACount + LBCount > stuckPointsTemp.count ){
-
- for (index,point) in stuckPointsTemp.enumerated() {
- if(index <= LACount + LBCount - stuckPointsTemp.count){
- finallyStuckPoints.append(Float((stuckPointsTemp.last ?? 0) + point))
- }
- }
- }
-
- BFLog(message: "计算后的\(realUsedMusicDuration) LACount:\(LACount) LBCount \(LBCount) 估算时长为:\(selectedTotalDuration * 2 / Float64(maxSpeed + minSpeed))")
-
- break
- case .createStickersModelOnlyMusic://仅音乐
-
- stuckPointsTemp = getUsedStuckPoint(seed: 1)
- //要拼接的段数
- var clipNum:Int = 0
- //所有段的时长总和
- var tempTime:Float = 0.0
- var i:Int = 0
- while tempTime < Float(selectedTotalDuration) {
-
- //回环从头取
- if(i+2 >= stuckPointsTemp.count){
- i = 0
- }
- //快速段
- let LA = (stuckPointsTemp[i+1] - stuckPointsTemp[i])
- tempTime = tempTime + Float(LA)
- clipNum = clipNum + 1
- if(tempTime > Float(selectedTotalDuration)){ break}
- //慢速段
- let LB = (stuckPointsTemp[i+2] - stuckPointsTemp[i+1])
- tempTime = tempTime + Float(LB)
- i = i + 1
- clipNum = clipNum + 1
- }
-
- //拼接要使用的卡点信息
- for i in 0...clipNum {
- let a = stuckPointsTemp[i % stuckPointsTemp.count]
- let b = Float(i / stuckPointsTemp.count) * (stuckPointsTemp.first ?? 0)
- finallyStuckPoints.append(a + b)
- }
-
- break
-
- }
- //拼接图片所使用的时长
- //二,最一个卡点在原数组中的位置,从这个位置开始拼接图片所要使用的卡点
- if(selectedImageDataCount > 0){
- var pointIndex = stuckPointsTemp.firstIndex(where: { (point) -> Bool in
- (point == (finallyStuckPoints.last ?? 0).truncatingRemainder(dividingBy: (stuckPointsTemp.last ?? 0)))
- })
- pointIndex = (pointIndex ?? 0) + 1
-
- for i in 0...selectedImageDataCount {
-
- let a = stuckPointsTemp[i % stuckPointsTemp.count]
- let b = Float(i / stuckPointsTemp.count) * (stuckPointsTemp.first ?? 0)
- finallyStuckPoints.append(a + b)
- }
- selectedTotalDuration = Float64(((finallyStuckPoints.last ?? 0) - (finallyStuckPoints.first ?? 0)))
- }
-
-
- //3)素材全是图片时如果时长 < 10s要做轮播处理
- if(selectedDataCount == selectedImageDataCount && selectedTotalDuration < 10 ){
- //已经设置过循环次数
- if(lastCyclesSelectIndex != 0){
- for i in 0...lastCyclesSelectIndex {
-
- var pointIndex = stuckPointsTemp.firstIndex(where: { (point) -> Bool in
- (point == (finallyStuckPoints.last ?? 0).truncatingRemainder(dividingBy: (stuckPointsTemp.last ?? 0)))
- })
- pointIndex = (pointIndex ?? 0) + 1
- for i in 0...selectedImageDataCount {
-
- let a = stuckPointsTemp[i % stuckPointsTemp.count]
- let b = Float(i / stuckPointsTemp.count) * (stuckPointsTemp.first ?? 0)
- finallyStuckPoints.append(a + b)
- }
- }
- }else{
-
- lastCyclesSelectIndex = 0
- while selectedTotalDuration < 10{
-
- //不够10S 时 一次加图片数量的卡点数
- var pointIndex = stuckPointsTemp.firstIndex(where: { (point) -> Bool in
- (point == (finallyStuckPoints.last ?? 0).truncatingRemainder(dividingBy: (stuckPointsTemp.last ?? 0)))
- })
- pointIndex = (pointIndex ?? 0) + 1
- for i in 0...selectedImageDataCount {
- let a = stuckPointsTemp[(i + Int(pointIndex ?? 0)) % stuckPointsTemp.count]
- let b = Float((i + Int(pointIndex ?? 0)) / stuckPointsTemp.count) * (stuckPointsTemp.first ?? 0)
- finallyStuckPoints.append(a + b)
- }
-
- selectedTotalDuration = Float64((finallyStuckPoints.last ?? 0) - (finallyStuckPoints.first ?? 0))
- lastCyclesSelectIndex = lastCyclesSelectIndex + 1
- }
- speedSettingView.setSelectItem(index: lastCyclesSelectIndex)
-
- }
-
-
- }
-
- //三,计算最后使用的音频时长, 如果不用拼接音频时长度是卡点的倒数第二位时间
- let asset = AVURLAsset(url: URL(fileURLWithPath: documensDirectory + (stuckPointMusicData?.localPath ?? "")), options: nil)
-
- //原推荐卡点的倒数第二位时间
- let lastSecondPoint = Float((stuckPointMusicData!.rhythmSdata[0].pointTimes[stuckPointMusicData!.rhythmSdata[0].pointTimes.count - 2] )) / Float(BASE_FILTER_TIMESCALE)
-
- finallyUserAudioTime = Float(lastSecondPoint)
- if((finallyStuckPoints.last ?? 0) > Float(CMTimeGetSeconds(asset.duration))){
-
- finallyUserAudioTime = Float(finallyStuckPoints.last ?? 0) + (Float(CMTimeGetSeconds(asset.duration)) - Float(lastSecondPoint))
-
- }
-
-
- playeTimeRange = CMTimeRange(start: CMTime(value: CMTimeValue( Float(stuckPointMusicData?.startTime ?? 0) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE), end: CMTime(value: CMTimeValue((Float(finallyStuckPoints.last ?? 0)) * Float(BASE_FILTER_TIMESCALE)), timescale: BASE_FILTER_TIMESCALE))
-
- // 更新裁剪时间条的的ui数据
- stuckPointCuttingView.videoDuration = CGFloat(finallyUserAudioTime)
- stuckPointCuttingView.updateEndTime(startTime:CGFloat(CMTimeGetSeconds( playeTimeRange.start)), endTime: CGFloat( CMTimeGetSeconds( playeTimeRange.end)))
-
-
- }
- }
- // MARK: - 同步/下载素材相关
- /// 同步/下载素材相关
- extension PQStuckPointEditerController {
- /// 同步音乐相关数据
- /// - Returns: <#description#>
- func synchroMusicInfoData() {
- if (stuckPointMusicData?.rhythmSdata.count ?? 0) <= 0 {
- if synchroMarskView.superview == nil {
- UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
- }
- PQStuckPointViewModel.stuckPointMusicDetailData(musicId: stuckPointMusicData?.musicId ?? "", originType: stuckPointMusicData?.originType ?? 1) { [weak self] newMusicData, _ in
- if newMusicData != nil, (newMusicData?.rhythmSdata.count ?? 0) > 0 {
- self?.isStuckPointDataSuccess = true
- self?.stuckPointMusicData?.rhythmSdata = newMusicData?.rhythmSdata ?? []
- self?.stuckPointMusicData?.startTime = newMusicData?.startTime ?? 0
- self?.stuckPointMusicData?.endTime = newMusicData?.endTime ?? 0
- if newMusicData?.speed != nil {
- self?.stuckPointMusicData?.speed = newMusicData?.speed ?? 2
- }
- if (self?.stuckPointMusicData?.rhythmSdata.count ?? 0) > 0 && (((self?.selectedDataCount ?? 0) - (self?.selectedImageDataCount ?? 0)) > 0 || (self?.selectedImageDataCount ?? 0) > 0 || (self?.selectedTotalDuration ?? 0) > 0) {
- // 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)
- }
- self?.stuckPointCuttingView.updateEndTime(startTime: CGFloat(self?.stuckPointMusicData?.startTime ?? 0), endTime: CGFloat(self?.stuckPointMusicData?.endTime ?? 0))
- if self?.stuckPointMusicData?.localPath == nil || (self?.stuckPointMusicData?.localPath?.count ?? 0) > 0 {
- PQDownloadManager.downLoadFile(url: self?.stuckPointMusicData?.musicPath ?? "") { [weak self] filePath, error in
- if error == nil, filePath != nil {
- self?.isSynchroMusicInfoSuccess = true
- self?.stuckPointMusicData?.localPath = filePath?.replacingOccurrences(of: documensDirectory, with: "")
- // 处理所有数据完成
- self?.dealWithDataSuccess()
- }
- if self?.synchroMarskView.superview != nil {
- self?.synchroMarskView.removeMarskView()
- }
- }
- } else {
- self?.isSynchroMusicInfoSuccess = true
- // 处理所有数据完成
- self?.dealWithDataSuccess()
- }
- // 添加子视图
- // if(playerView )
- self?.addSubViews()
- } else {
- if self?.synchroMarskView.superview != nil {
- self?.synchroMarskView.removeMarskView()
- }
-
- }
- }
- } else if stuckPointMusicData?.localPath == nil || (stuckPointMusicData?.localPath?.count ?? 0) > 0 {
- if synchroMarskView.superview == nil {
- UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
- }
- isStuckPointDataSuccess = true
- PQDownloadManager.downLoadFile(url: stuckPointMusicData?.musicPath ?? "") { [weak self] filePath, error in
- if error == nil, filePath != nil {
- self?.isSynchroMusicInfoSuccess = true
- self?.stuckPointMusicData?.localPath = filePath?.replacingOccurrences(of: documensDirectory, with: "")
- // 处理所有数据完成
- self?.dealWithDataSuccess()
- } else {
- if self?.synchroMarskView.superview != nil {
- self?.synchroMarskView.removeMarskView()
- }
- // PQUploadRemindView.showUploadRemindView(title: nil, attributedTitle: NSAttributedString(string: "加载音乐失败,请重新选择音乐"), summary: "", confirmTitle: nil) { [weak self] _, _ in
- // self?.navigationController?.popViewController(animated: true)
- // }
- }
- }
- } else {
- isStuckPointDataSuccess = true
- // 处理所有数据完成
- dealWithDataSuccess()
- }
- }
- /// 导出相册数据
- /// - Returns: <#description#>
- func exportPhotoData() {
- // 取消所有的导出
- PQSingletoMemoryUtil.shared.allExportSession.forEach { _, exportSession in
- exportSession.cancelExport()
- }
- var isHaveVideo: Bool = false
- if selectedPhotoData != nil, (selectedPhotoData?.count ?? 0) > 0 {
- if synchroMarskView.superview == nil {
- UIApplication.shared.keyWindow?.addSubview(synchroMarskView)
- }
- let dispatchGroup = DispatchGroup()
- for photo in selectedPhotoData! {
- if photo.asset != nil, photo.asset?.mediaType == .video {
- if !isHaveVideo {
- isHaveVideo = true
- }
- dispatchGroup.enter()
- PQPHAssetVideoParaseUtil.parasToAVAsset(phAsset: photo.asset!) { avAsset, _, _, _ in
- if avAsset is AVURLAsset {
- // 创建目录
- let fileName = (avAsset as! AVURLAsset).url.absoluteString
- BFLog(message: "video fileName is\(fileName)")
- let tempPhoto = self.selectedPhotoData?.first(where: { material in
- material.asset == photo.asset
- })
- if (fileName.count) > 0 {
- createDirectory(path: photoLibraryDirectory)
- let outFilePath = photoLibraryDirectory + fileName.md5 + ".mp4"
- // 文件存在先删除老文件
- if FileManager.default.fileExists(atPath: outFilePath) {
- do {
- try FileManager.default.removeItem(at: NSURL.fileURL(withPath: outFilePath))
- } catch {
- BFLog(message: "导出相册视频-error == \(error)")
- }
- }
- do {
- try FileManager.default.copyItem(atPath: fileName.replacingOccurrences(of: "file:///", with: ""), toPath: outFilePath)
- print("Success to copy file.")
- } catch {
- print("Failed to copy file.")
- }
- tempPhoto?.locationPath = outFilePath.replacingOccurrences(of: documensDirectory, with: "")
- BFLog(message: "导出视频相册地址为 \(String(describing: tempPhoto?.locationPath))")
- dispatchGroup.leave()
- }
- }
- }
- }
- }
- dispatchGroup.notify(queue: DispatchQueue.main) { [weak self] in
- self?.isExportVideosSuccess = true
- BFLog(message: "所有相册视频导出成功")
- // 处理所有数据完成
- if isHaveVideo {
- self?.dealWithDataSuccess()
- }
- }
- // 只有图片
- if !isHaveVideo {
- isExportVideosSuccess = true
- BFLog(message: "所有相册视频导出成功")
- dealWithDataSuccess()
- }
- }
- }
- /// 处理所有数据完成
- /// - Returns: <#description#>
- func dealWithDataSuccess() {
- BFLog(message: "三组参数:\(isSynchroMusicInfoSuccess) \(isStuckPointDataSuccess) \(isExportVideosSuccess)")
- if !isSynchroMusicInfoSuccess || !isStuckPointDataSuccess || !isExportVideosSuccess {
- return
- }
- createPorjectData()
- BFLog(message: "界面编辑界面时参数 选择素材时长:\(selectedTotalDuration) 选择素材总数:\(selectedDataCount) 选择图片总数\(selectedImageDataCount)")
-
-
- //1 生成默认参数值
- /*
- - 当素材总时长∈[0-6)s 时,提示推荐仅配乐模式 or 快慢速模式
- - 当素材总时长∈[6-80)s 时,卡点抛留倍数为1x,即留1抛1
- - 当素材总时长∈[80,120)s 时,卡点抛留倍数为2x,即留1抛2
- - 当素材总时长∈[120,160)s 时,卡点抛留倍数为3x,即留1抛3
- - 当素材总时长∈[160,200)s 时,卡点抛留倍数为4x,即留1抛4
- - 当素材总时长∈[200,240)s 时,卡点抛留倍数为5x,即留1抛5
- - 当素材总时长∈[240,∞)s 时,卡点抛留倍数为5x,即留1抛5
- */
- //1.1) 生成跳跃卡点的默认值
- if(selectedTotalDuration >= 6 && selectedTotalDuration < 80){
- lastJumpSpeedSelectIndex = 0
- }else if(selectedTotalDuration >= 80 && selectedTotalDuration < 120){
- lastJumpSpeedSelectIndex = 1
- }else if(selectedTotalDuration >= 120 && selectedTotalDuration < 160){
- lastJumpSpeedSelectIndex = 2
- }else if(selectedTotalDuration >= 160 && selectedTotalDuration < 200){
- lastJumpSpeedSelectIndex = 3
- }else if(selectedTotalDuration >= 200 && selectedTotalDuration < 240){
- lastJumpSpeedSelectIndex = 4
- }else if(selectedTotalDuration >= 240){
- lastJumpSpeedSelectIndex = 5
- }
-
- /*默认进入快慢速模式
- - 当素材总时长∈[120,144]s 时,快慢速处理方式:快速为 6x,慢速为 1.2x,效果是快&快
- - 当素材总时长∈[70,120)s 时,快慢速处理方式:快速为5x,慢速为 1x,效果是快&正常
- - 当素材总时长∈[56,70)s 时,快慢速处理方式:快速为3x,慢速为 0.5x,效果是快&慢
- - 当素材总时长∈[17.5,56)s 时,快慢速处理方式:快速为 2.4x,慢速为 0.4x,效果是快&慢
- - 当素材总时长∈[10.5,17.5)s 时,快慢速处理方式:快速为 1.8x,慢速为 0.3x,效果是快&慢
- - 当素材总时长∈(0,10.5)s 时,快慢速处理方式:快速为 1x,慢速为 0.2x,效果是正常&慢
-
- */
- //1.2)生成快慢速的默认值
- if(selectedTotalDuration >= 120 && selectedTotalDuration <= 144){
- lastSpeedSelectIndex = 0
- }else if(selectedTotalDuration >= 70 && selectedTotalDuration < 120){
- lastSpeedSelectIndex = 1
- }else if(selectedTotalDuration >= 56 && selectedTotalDuration < 70){
- lastSpeedSelectIndex = 2
- }else if(selectedTotalDuration >= 17.5 && selectedTotalDuration < 56){
- lastSpeedSelectIndex = 3
- }else if(selectedTotalDuration >= 10.5 && selectedTotalDuration < 17.5){
- lastSpeedSelectIndex = 4
- }else if(selectedTotalDuration > 0 && selectedTotalDuration < 10.5){
- lastSpeedSelectIndex = 5
- }
-
-
- /*
- 文档规则 https://w42nne6hzg.feishu.cn/docs/doccnQZm1uCfkU4QtJb5fLxYk4d#
- */
- //2,根据所选择所有素材时长进入默认模式
- //全是图片
- if(selectedDataCount == selectedImageDataCount){
- BFLog(message: "全是图片 \(selectedDataCount) \(selectedImageDataCount)")
-
- //默认进入跳跃卡点模式
- editModelClick(sender: jumpPointBtn)
- // speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex)
-
- }else{
-
- //默认进入快慢速模式
- if(selectedTotalDuration > 0 && selectedTotalDuration <= 144){
-
- editModelClick(sender: speedStuckBtn)
- // speedSettingView.setSelectItem(index: lastSpeedSelectIndex)
-
- }else{
- //默认进入卡点模式
- editModelClick(sender: jumpPointBtn)
- // speedSettingView.setSelectItem(index: lastJumpSpeedSelectIndex)
-
- }
- }
-
- }
- }
|